مقدمة: لماذا التخزين المؤقت في Rust؟
Rust هي لغة برمجة أنظمة تُقدّر للأداء وأمان الذاكرة. ومع ذلك، حتى أسرع البرامج يمكن أن تبطئ بسبب عمليات الإدخال/الإخراج المتكررة، أو الحسابات المعقدة، أو الاستعلامات المتكررة لقواعد البيانات. يسمح التخزين المؤقت بحفظ نتائج العمليات المكلفة وإعادتها فورًا، وهو أمر بالغ الأهمية لخدمات الويب عالية التحميل، ومحركات الألعاب، وأنظمة الوقت الفعلي.
في هذه المقالة، سنستعرض ثلاثة أساليب للتخزين المؤقت في Rust: الأنظمة الخارجية (Redis, Memcached) والحلول المدمجة (lru-rs, moka). ستتعلم كيفية دمجها في الكود، وما هي المهام التي يحلها كل أداة، وكيفية اختيار الخيار الأمثل لمشروعك.
1. التخزين المؤقت الخارجي: Redis و Memcached
تعمل أنظمة التخزين المؤقت الخارجية كخدمات منفصلة يتصل بها التطبيق عبر الشبكة. وهي مثالية للأنظمة الموزعة حيث تحتاج عدة عمليات أو خوادم إلى مشاركة ذاكرة تخزين مؤقت مشتركة.
1.1. Redis عبر crate redis
Redis هو مخزن in-memory الأكثر شهرة، ويدعم هياكل بيانات معقدة (قوائم، مجموعات، تجزئات). للاتصال بـ Redis في Rust، يُستخدم crate redis.
التثبيت: أضف إلى Cargo.toml:
[dependencies]redis = { version = "0.25", features = ["tokio-comp"] }tokio = { version = "1", features = ["full"] }مثال للتخزين المؤقت الأساسي لسلسلة نصية:
use redis::AsyncCommands;
#[tokio::main]async fn main() -> redis::RedisResult<()> { let client = redis::Client::open("redis://127.0.0.1:6379/")?; let mut con = client.get_multiplexed_async_connection().await?;
// كتابة في ذاكرة التخزين المؤقت لمدة 60 ثانية let _: () = con.set_ex("user:42", "Alice", 60).await?; // قراءة let val: Option = con.get("user:42").await?; println!("القيمة المخزنة مؤقتًا: {:?}", val); Ok(())} Redis مناسب تمامًا لتخزين الجلسات، واستجابات API، وقوائم المهام. عيبه هو الحاجة إلى تثبيت وصيانة خادم منفصل.
1.2. Memcached عبر crate memcache
Memcached هو بديل أخف لـ Redis، موجه لتخزين key-value البسيط. يوفر crate memcache واجهة برمجة تطبيقات متزامنة وغير متزامنة.
مثال باستخدام crate memcache:
use memcache::{Client, MemcacheError};
fn main() -> Result<(), MemcacheError> { let client = Client::connect("memcache://127.0.0.1:11211")?; client.set("counter", 100, 30)?; // TTL = 30 ثانية let val: Option = client.get("counter")?; println!("القيمة: {:?}", val); Ok(())} Memcached أسرع من Redis في عمليات get/set البسيطة، لكنه لا يدعم هياكل البيانات المعقدة. اختره إذا كنت تحتاج فقط إلى "key-value" بأقل تكلفة إضافية.
2. مكتبات التخزين المؤقت المدمجة
للتطبيقات أحادية العملية (أدوات CLI، برامج سطح المكتب، خوادم الألعاب) يكون التخزين المؤقت الخارجي مبالغًا فيه. الحلول المدمجة تعمل في ذاكرة العملية، ولا تحتاج إلى شبكة، وهي الأسرع على الإطلاق.
2.1. lru-rs — ذاكرة تخزين مؤقت LRU بسيطة
crate lru ينفذ ذاكرة تخزين مؤقت مع إزالة الأقل استخدامًا مؤخرًا (Least Recently Used). مثالي لتخزين نتائج التحليل، أو التكوينات، أو الكائنات الصغيرة.
use lru::LruCache;use std::num::NonZeroUsize;
fn main() { let mut cache: LruCache = LruCache::new(NonZeroUsize::new(3).unwrap()); cache.put("a", 1); cache.put("b", 2); cache.put("c", 3); cache.put("d", 4); // "a" ستتم إزالتها لأن الذاكرة ممتلئة
println!("{:?}", cache.get(&"a")); // None println!("{:?}", cache.get(&"d")); // Some(4)} المزايا: بسيط، وقت تشغيل قابل للتنبؤ، آمن للخيوط (thread-safe) عند الاستخدام