🌐 Detecting your location…
📢 Advertisement — Configure AdSense in Appearance → Customize → AdSense Settings

كيفية استخدام Redis للتخزين المؤقت في تطبيق Node.js: دليل 2026 الكامل

⏱️3 min read  ·  450 words

ريديس هو مخزن بيانات في الذاكرة يعمل على تسريع التطبيقات بشكل كبير عن طريق التخزين المؤقت لاستعلامات قاعدة البيانات باهظة الثمن واستجابات واجهة برمجة التطبيقات (API). يمكن لذاكرة التخزين المؤقت الموضوعة بشكل جيد تحويل استعلام قاعدة بيانات تبلغ 200 مللي ثانية إلى نتيجة ذاكرة تخزين مؤقت تبلغ 2 مللي ثانية. يضيف هذا الدليل التخزين المؤقت لـ Redis إلى تطبيق Node.js بالطريقة الصحيحة.

لماذا ذاكرة التخزين المؤقت مع 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 بأمان، وسيصبح تطبيقك أسرع وأكثر مرونة في نفس الوقت.

✍️ Leave a Comment

Your email address will not be published. Required fields are marked *

🌐 Read in:🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা