التخزين المؤقت في JavaScript (Node.js): Redis وغيرها
التخزين المؤقت هو أحد الأدوات الرئيسية لتحسين أداء تطبيقات الويب على Node.js. فهو يسمح بتقليل وقت استجابة الخادم، وتخفيف الحمل على قواعد البيانات وواجهات API الخارجية، بالإضافة إلى تحسين تجربة المستخدم. في هذه المقالة، سنستعرض المكتبات والأساليب الشائعة للتخزين المؤقت: من الحلول المدمجة إلى Redis القوي.
لماذا نحتاج إلى التخزين المؤقت في Node.js؟
بدون التخزين المؤقت، قد يؤدي كل طلب إلى الخادم إلى عمليات حسابية متكررة، أو استعلامات لقاعدة البيانات، أو خدمات خارجية. هذا يزيد من وقت الاستجابة واستهلاك الموارد. يخزن الكاش البيانات المطلوبة بشكل متكرر في مخزن سريع (ذاكرة الوصول العشوائي أو قاعدة بيانات in-memory)، مما يسمح بـ:
- تسريع الاستجابات — القراءة من الذاكرة أسرع بمقدار 100-1000 مرة من القراءة من القرص.
- تقليل الحمل — تعالج قاعدة البيانات عددًا أقل من الاستعلامات.
- توفير الميزانية — موارد أقل للخدمات السحابية.
1. Redis — المعيار الصناعي
Redis هو مخزن بيانات عالي الأداء في الذاكرة (in-memory)، يدعم هياكل البيانات (سلاسل، تجزئات، قوائم، مجموعات). بالنسبة لـ Node.js، يتم استخدام الحزمة redis أو ioredis.
التثبيت والتوصيل
npm install redisconst redis = require('redis');const client = redis.createClient({ url: 'redis://localhost:6379'});
client.on('error', (err) => console.error('Redis Client Error', err));client.connect();العمليات الأساسية
// كتابة مع TTL (مدة الصلاحية)await client.setEx('user:123', 3600, JSON.stringify({name: 'Alice', role: 'admin'}));
// قراءةconst cached = await client.get('user:123');if (cached) { console.log('من الكاش:', JSON.parse(cached));} else { // استعلام لقاعدة البيانات}الإمكانيات المتقدمة
- الإبطال وفقًا لنمط — حذف جميع المفاتيح ذات البادئة
user:*. - Pub/Sub — إشعارات بتغييرات الكاش بين النسخ.
- Redis Cluster — التوسع الأفقي.
2. node-cache — كاش في الذاكرة بدون تبعيات خارجية
للمشاريع الصغيرة أو النماذج الأولية، تناسب المكتبة node-cache. تعمل ضمن عملية Node.js واحدة ولا تتطلب تثبيت Redis.
التثبيت
npm install node-cacheمثال على الاستخدام
const NodeCache = require('node-cache');const myCache = new NodeCache({ stdTTL: 600, checkperiod: 120 });
// حفظ البياناتmyCache.set('posts:recent', postsData, 3600);
// قراءةconst cached = myCache.get('posts:recent');if (cached) { return cached;} else { const fresh = await fetchPostsFromDB(); myCache.set('posts:recent', fresh); return fresh;}الميزات
- إعداد بسيط لـ TTL (time-to-live).
- تنظيف تلقائي للسجلات القديمة.
- غير مناسب للأنظمة الموزعة (البيانات تعيش فقط في عملية واحدة).
3. memory-cache — بديل خفيف
memory-cache هو كاش بسيط آخر في الذاكرة لـ Node.js. يتميز بواجهة برمجة تطبيقات بسيطة.
const cache = require('memory-cache');
// كتابةcache.put('api:weather', weatherData, 300000); // 5 دقائق
// قراءةconst data = cache.get('api:weather');if (!data) { // إعادة الطلب}4. استراتيجيات التخزين المؤقت
يعتمد اختيار الاستراتيجية على نوع البيانات ومتطلبات التحديث:
- Cache-Aside (Lazy Loading) — يتحقق التطبيق أولاً من الكاش، وعند عدم وجود البيانات، يقوم بتحميلها وحفظها في الكاش. هذا هو النهج الأكثر شيوعًا.
- Write-Through — يتم كتابة البيانات