التخزين المؤقت في Rust: Redis وMemcached والحلول المدمجة

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة: لماذا التخزين المؤقت في Rust؟

\n

السرعة هي الميزة الرئيسية لـ Rust. ولكن حتى أسرع كود يمكن أن يصطدم ببطء الإدخال/الإخراج (I/O): استعلامات قاعدة البيانات، واجهات برمجة التطبيقات الخارجية، أو حسابات البيانات المعقدة. يحل التخزين المؤقت هذه المشكلة عن طريق حفظ نتائج العمليات المكلفة في مخزن سريع. تقدم Rust أدوات مرنة للتخزين المؤقت: من هياكل البيانات المدمجة البسيطة إلى الأنظمة الموزعة القوية مثل Redis وMemcached.

\n

في هذه المقالة، سنقوم بتحليل جميع مستويات التخزين المؤقت في Rust: من HashMap البسيط إلى الحلول الاحترافية مع TTL (وقت الحياة) والمزامنة. ستتعلم كيفية اختيار الأداة المناسبة لمشروعك وتجنب الأخطاء الشائعة.

\n\n

1. التخزين المؤقت المدمج: HashMap ومتغيراته

\n

أبسط طريقة للتخزين المؤقت في Rust هي استخدام HashMap من المكتبة القياسية. ومع ذلك، في البيئة متعددة الخيوط، هناك حاجة إلى آليات مزامنة إضافية.

\n\n

1.1. HashMap بسيط (أحادي الخيط)

\n

للتطبيقات أحادية الخيط، فإن HashMap العادي مناسب. مثال:

\n
use std::collections::HashMap;\n\nfn main() {\n    let mut cache: HashMap<String, String> = HashMap::new();\n    cache.insert("user:1".to_string(), "Alice".to_string());\n    \n    if let Some(name) = cache.get("user:1") {\n        println!("القيمة المخزنة مؤقتاً: {}", name);\n    }\n}
\n

الإيجابيات: كود أدنى، لا تبعيات.
\nالسلبيات: لا يوجد TTL، غير آمن للخيوط، الذاكرة غير محدودة.

\n\n

1.2. ذاكرة تخزين مؤقت آمنة للخيوط مع Mutex وArc

\n

للتطبيقات متعددة الخيوط، استخدم Arc<Mutex<HashMap>>:

\n
use std::collections::HashMap;\nuse std::sync::{Arc, Mutex};\nuse std::thread;\n\nstruct Cache {\n    store: Arc<Mutex<HashMap<String, String>>>,\n}\n\nimpl Cache {\n    fn new() -> Self {\n        Cache { store: Arc::new(Mutex::new(HashMap::new())) }\n    }\n    \n    fn get(&self, key: &str) -> Option<String> {\n        self.store.lock().ok()?.get(key).cloned()\n    }\n    \n    fn set(&self, key: String, value: String) {\n        self.store.lock().unwrap().insert(key, value);\n    }\n}\n\nfn main() {\n    let cache = Cache::new();\n    let c1 = cache.clone();\n    thread::spawn(move || {\n        c1.set("key".to_string(), "value".to_string());\n    });\n    println!("{:?}", cache.get("key"));\n}
\n

العيب: قفل Mutex يمكن أن يقلل الأداء عند التنافس العالي.

\n\n

1.3. مكتبات متقدمة: lru و cached

\n

لكود الإنتاج، من الأفضل استخدام الكريتات الجاهزة:

\n
    \n
  • lru — تطبق خوارزمية الإزاحة LRU (الأقل استخداماً مؤخراً). مناسبة للذاكرة التخزينية المحدودة.
  • \n
  • cached — توفر ماكرو للتخزين المؤقت التلقائي للدوال مع دعم TTL وLRU.
  • \n
\n

مثال مع cached:

\n
use cached::proc_macro::cached;\n\n#[cached(size = 100, time = 300)]\nfn expensive_computation(x: u32) -> u32 {\n    std::thread::sleep(std::time::Duration::from_secs(2));\n    x * x\n}\n\nfn main() {\n    let result = expensive_computation(5); // يستغرق 2 ثانية\n    let cached_result = expensive_computation(5); // فوري\n    println!("{}", cached_result);\n}
\n\n

2. Redis: ذاكرة تخزين مؤقت موزعة للأنظمة القابلة للتوسع

\n

Redis هو مخزن بيانات في الذاكرة، مثالي للتخزين المؤقت الموزع. في Rust، يتم استخدام الكريت redis للعمل مع Redis.

\n\n

2.1. الاتصال والعمليات الأساسية

\n
use redis::Commands;\n\nfn main() -> redis::RedisResult<()> {\n    let client = redis::Client::open("redis://127.0.0.1:6379/")?;\n    let mut con = client.get_connection()?;\n    \n    // تعيين قيمة مع TTL (وقت الحياة)\n    con.set_ex("user:1", "Alice", 3600)?; //                

المدونات

توصيات الكتب