परिचय: Rust में कैशिंग क्यों एक चुनौती और लाभ है
\nRust पर उच्च-लोड सिस्टम में, कैशिंग केवल एक लाइब्रेरी जोड़ने से कहीं अधिक हो जाती है। सख्त स्वामित्व प्रणाली, गार्बेज कलेक्टर की अनुपस्थिति और थ्रेड-सुरक्षा आवश्यकताओं के कारण, पारंपरिक दृष्टिकोणों पर पुनर्विचार करने की आवश्यकता है। हालाँकि, Rust अद्वितीय क्षमताएँ प्रदान करता है: पूर्वानुमानित प्रतिक्रिया समय, GC पर शून्य ओवरहेड और मेमोरी पर पूर्ण नियंत्रण। इस लेख में, हम कैशिंग की उन्नत तकनीकों पर चर्चा करेंगे: वितरित समाधानों (Redis, Memcached) से लेकर अंतर्निहित कैश (moka, lru) और उनके संयोजन तक।
\n\n1. deadpool-redis के माध्यम से Redis के साथ अतुल्यकालिक कार्य
\n1.1. सीधे redis-rs क्यों नहीं?
\nक्लाइंट redis-rs सिंक्रोनस है। tokio पर अतुल्यकालिक अनुप्रयोगों के लिए कनेक्शन पूल की आवश्यकता होती है। deadpool-redis एक अतुल्यकालिक पूल प्रदान करता है जो कनेक्शन का प्रबंधन करता है और विफलताओं पर स्वचालित रूप से पुनः कनेक्ट होता है।
1.2. उदाहरण: TTL और सीरियलाइज़ेशन के साथ कैशिंग
\nuse deadpool_redis::{Config, Runtime, Pool};\nuse serde::{Serialize, Deserialize};\nuse tokio::sync::OnceCell;\n\n#[derive(Serialize, Deserialize, Clone)]\nstruct User {\n id: u64,\n name: String,\n email: String,\n}\n\nasync fn get_pool() -> Pool {\n static POOL: OnceCell<Pool> = OnceCell::const_new();\n POOL.get_or_init(|| async {\n let cfg = Config::from_url("redis://127.0.0.1/");\n cfg.create_pool(Some(Runtime::Tokio1)).unwrap()\n }).await.clone()\n}\n\nasync fn get_user_cached(user_id: u64) -> Option<User> {\n let pool = get_pool().await;\n let mut conn = pool.get().await.unwrap();\n let key = format!("user:{}", user_id);\n\n // कैश से पढ़ने का प्रयास\n if let Ok(Some(data)) = redis::cmd("GET").arg(&key).query_async::<Option<Vec<u8>>>(&mut conn).await {\n if let Ok(user) = bincode::deserialize::<User>(&data) {\n return Some(user);\n }\n }\n None\n}\n\nasync fn set_user_cache(user: &User) {\n let pool = get_pool().await;\n let mut conn = pool.get().await.unwrap();\n let key = format!("user:{}", user.id);\n let data = bincode::serialize(user).unwrap();\n\n // TTL 300 सेकंड\n let _: () = redis::cmd("SETEX")\n .arg(&key)\n .arg(300)\n .arg(&data)\n .query_async(&mut conn)\n .await\n .unwrap();\n}\n\n1.3. उन्नत रणनीति: लॉक के साथ Cache-Aside
\ncache stampede (जब कई अनुरोध एक साथ कैश को फिर से भरने का प्रयास करते हैं) के मामले में, एप्लिकेशन स्तर पर म्यूटेक्स का उपयोग करें:
\nuse tokio::sync::Mutex;\nuse std::collections::HashMap;\n\nstruct CacheLock {\n locks: Mutex<HashMap<String, tokio::sync::Semaphore>>,\n}\n\nimpl CacheLock {\n async fn get_or_compute<F, Fut, T>(&self, key: &str, f: F) -> T\n where\n F: FnOnce() -> Fut,\n Fut: Future<Output = T>,\n {\n let sem = {\n let mut map = self.locks.lock().await;\n map.entry(key.to_string())\n .or_insert_with(|| tokio::sync::Semaphore::new(1))\n .clone()\n };\n let _permit = sem.acquire().await.unwrap();\n f().await\n }\n}\n\n2. Memcached: memcache-async के साथ हल्कापन और गति
\n2.1. Rust के लिए Memcached की विशेषताएँ
\nMemcached Redis से सरल है: इसमें कोई स्थायित्व या जटिल डेटा संरचनाएँ नहीं हैं, लेकिन यह सरल key-value संचालन के लिए तेज़ है। क्रेट memcache-async tokio पर एक अतुल्यकालिक क्लाइंट प्रदान करता है।
2.2. उदाहरण: बैच लोडिंग और पाइपलाइनिंग
\nuse memcache_async::{Client, Protocol, Value};\nuse bytes::Bytes;\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n let client = Client::connect_async("memcache://127.0.0.1:11211").await?;\n\n // प