ريديس هو مخزن بيانات في الذاكرة يعمل على تسريع التطبيقات بشكل كبير عن طريق التخزين المؤقت لاستعلامات قاعدة البيانات باهظة الثمن واستجابات واجهة برمجة التطبيقات (API). يمكن لذاكرة التخزين المؤقت الموضوعة بشكل جيد تحويل استعلام قاعدة بيانات تبلغ 200 مللي ثانية إلى نتيجة ذاكرة تخزين مؤقت تبلغ 2 مللي ثانية. يضيف هذا الدليل التخزين المؤقت لـ Redis إلى تطبيق Node.js بالطريقة الصحيحة.
📋 Table of Contents
لماذا ذاكرة التخزين المؤقت مع Redis؟
- السرعة: تكون القراءات في الذاكرة أسرع بمعدل 10 إلى 100 مرة من استعلامات قاعدة البيانات
- تقليل تحميل قاعدة البيانات: خدمة الطلبات المتكررة من ذاكرة التخزين المؤقت، مع الحفاظ على قاعدة البيانات الخاصة بك
- المقياس: تعامل مع ارتفاع حركة المرور دون إرباك مخزن البيانات الأساسي لديك
- متعددة الاستخدامات: يتعامل أيضًا مع الجلسات وتحديد المعدل وقوائم الانتظار والنشر/الفرعي
الإعداد
npm install ioredis
# Run Redis locally with Docker
docker run -d --name redis -p 6379:6379 redis:7-alpine
// redis.js — connection singleton
import Redis from 'ioredis';
export const redis = new Redis({
host: process.env.REDIS_HOST || 'localhost',
port: parseInt(process.env.REDIS_PORT || '6379'),
maxRetriesPerRequest: 3,
retryStrategy: (times) => Math.min(times * 50, 2000),
});
redis.on('error', (err) => console.error('Redis error:', err));
redis.on('connect', () => console.log('Redis connected'));
نمط ذاكرة التخزين المؤقت (الأكثر شيوعًا)
تحقق من ذاكرة التخزين المؤقت أولا؛ في حالة حدوث خطأ، قم بالجلب من قاعدة البيانات وملء ذاكرة التخزين المؤقت:
import { redis } from './redis';
async function getUser(userId) {
const cacheKey = `user:${userId}`;
// 1. Try cache first
const cached = await redis.get(cacheKey);
if (cached) {
return JSON.parse(cached); // cache hit — fast path
}
// 2. Cache miss — fetch from database
const user = await db.users.findById(userId);
if (!user) return null;
// 3. Populate cache with a TTL (expire after 1 hour)
await redis.set(cacheKey, JSON.stringify(user), 'EX', 3600);
return user;
}
غلاف ذاكرة التخزين المؤقت القابل لإعادة الاستخدام
async function cached(key, ttlSeconds, fetchFn) {
const hit = await redis.get(key);
if (hit) return JSON.parse(hit);
const data = await fetchFn();
if (data !== null && data !== undefined) {
await redis.set(key, JSON.stringify(data), 'EX', ttlSeconds);
}
return data;
}
// Usage — clean and reusable
const products = await cached('products:featured', 600, () =>
db.products.findFeatured()
);
const user = await cached(`user:${id}`, 3600, () =>
db.users.findById(id)
);
إبطال ذاكرة التخزين المؤقت (الجزء الصعب)
// When data changes, invalidate the cache to avoid stale reads
async function updateUser(userId, updates) {
const user = await db.users.update(userId, updates);
// Invalidate the cached version
await redis.del(`user:${userId}`);
// Invalidate related caches too
await redis.del('users:list');
return user;
}
// Invalidate by pattern (use SCAN, not KEYS, in production)
async function invalidatePattern(pattern) {
const stream = redis.scanStream({ match: pattern, count: 100 });
stream.on('data', (keys) => {
if (keys.length) redis.del(...keys);
});
}
// invalidatePattern('user:*'); // clear all user caches
برنامج وسيط سريع للتخزين المؤقت للطريق
function cacheRoute(ttlSeconds) {
return async (req, res, next) => {
const key = `route:${req.originalUrl}`;
const cached = await redis.get(key);
if (cached) {
res.setHeader('X-Cache', 'HIT');
return res.json(JSON.parse(cached));
}
// Override res.json to cache the response
const originalJson = res.json.bind(res);
res.json = (body) => {
redis.set(key, JSON.stringify(body), 'EX', ttlSeconds);
res.setHeader('X-Cache', 'MISS');
return originalJson(body);
};
next();
};
}
// Cache this route for 5 minutes
app.get('/api/products', cacheRoute(300), getProducts);
تحديد المعدل باستخدام Redis
async function rateLimit(req, res, next) {
const key = `ratelimit:${req.ip}`;
const limit = 100; // requests
const window = 60; // seconds
const current = await redis.incr(key);
if (current === 1) {
await redis.expire(key, window); // set TTL on first request
}
if (current > limit) {
return res.status(429).json({ error: 'Too many requests' });
}
res.setHeader('X-RateLimit-Remaining', Math.max(0, limit - current));
next();
}
أفضل ممارسات التخزين المؤقت
- قم دائمًا بتعيين TTL: يمنع البيانات القديمة من العيش إلى الأبد ويتحكم في الذاكرة
- قم بتخزين ما هو باهظ الثمن وقراءته بشكل متكرر: لا تقم بتخزين كل شيء مؤقتًا – قم بتخزين المسارات الساخنة
- التعامل مع حالات فشل ذاكرة التخزين المؤقت بأمان: إذا كان Redis معطلاً، فانتقل إلى قاعدة البيانات، ولا تتعطل
- استخدم تسمية مفاتيح متسقة:
entity:id:fieldالاصطلاحات تجعل الإبطال قابلاً للتنبؤ به - تجنب أمر KEYS في الإنتاج: إنه يحظر Redis — استخدم SCAN بدلاً من ذلك
- انتبه للتدافع في ذاكرة التخزين المؤقت: عندما تنتهي صلاحية مفتاح شائع، تصل العديد من الطلبات إلى قاعدة البيانات مرة واحدة – استخدم الأقفال أو TTLs المتداخلة
الأسئلة المتداولة
س: ما الذي لا يجب علي تخزينه مؤقتًا؟
ج: البيانات شديدة التقلب التي تغير كل طلب، والبيانات الحساسة التي لا تتمتع بأمان مناسب، والبيانات الرخيصة في الحوسبة. قم بتخزين البيانات باهظة الثمن والمقروءة بشكل متكرر والمستقرة نسبيًا.
س: كيف أختار TTL؟
ج: توازن بين النضارة والأداء. ملفات تعريف المستخدمين: 1 ساعة. قوائم المنتجات: 5-10 دقائق. البيانات في الوقت الفعلي: ثوانٍ أو لا تقم بتخزينها مؤقتًا. قم بمطابقة TTL مع عدد المرات التي تتغير فيها البيانات فعليًا.
س: ماذا يحدث إذا تعطل Redis؟
ج: يجب أن يتدهور تطبيقك بشكل جيد — اكتشف أخطاء Redis وانتقل إلى قاعدة البيانات. لا تسمح أبدًا لفشل ذاكرة التخزين المؤقت بإسقاط تطبيقك. لف مكالمات ذاكرة التخزين المؤقت في محاولة/التقاط.
س: Redis مقابل التخزين المؤقت في الذاكرة (مثل خريطة JS)؟
ج: تكون ذاكرة التخزين المؤقت للخريطة في الذاكرة لكل عملية (تُفقد عند إعادة التشغيل، ولا تتم مشاركتها عبر المثيلات). تتم مشاركة Redis عبر جميع مثيلات تطبيقك ويستمر – وهو ضروري للتطبيقات ذات الحجم الأفقي.
س: كيف يمكنني منع التدافع في ذاكرة التخزين المؤقت؟
ج: استخدم قفلًا (SET NX) بحيث يقوم طلب واحد فقط بتحديث ذاكرة التخزين المؤقت بينما ينتظر الآخرون أو يقدمون بيانات قديمة، أو أضف ارتعاشًا عشوائيًا إلى TTLs حتى لا تنتهي صلاحية جميع المفاتيح الشائعة في وقت واحد.
الخلاصة
يمكن أن يؤدي التخزين المؤقت لـ Redis إلى تحويل أداء تطبيق Node.js الخاص بك – وتحويل استعلامات قاعدة البيانات البطيئة إلى نتائج فورية لذاكرة التخزين المؤقت. يغطي نمط ذاكرة التخزين المؤقت مع غلاف قابل لإعادة الاستخدام معظم الاحتياجات، وتقوم البرامج الوسيطة على مستوى المسار بتخزين الاستجابات بالكامل مؤقتًا، ويتعامل نفس مثيل Redis مع تحديد المعدل والجلسات. تذكر الجزأين الصعبين: قم دائمًا بتعيين TTL، وإبطال ذاكرة التخزين المؤقت عندما تتغير البيانات. تعامل مع حالات فشل Redis بأمان، وسيصبح تطبيقك أسرع وأكثر مرونة في نفس الوقت.
🔗 Share this article
✍️ Leave a Comment