MAISON CODE .
/ Tech · Backend · Redis · Caching · Performance · Scale

التخزين المؤقت لـ Redis: بنية المللي ثانية

لماذا قاعدة البيانات الخاصة بك هي عنق الزجاجة دليل تقني عميق لأنماط Redis (التخزين المؤقت، والكتابة)، وإبطال العلامات، وHyperLogLog.

AB
Alex B.
التخزين المؤقت لـ Redis: بنية المللي ثانية

في التسلسل الهرمي لزمن الوصول، تكون الشبكة بطيئة، ويكون القرص بطيئًا، لكن ذاكرة الوصول العشوائي (RAM) فورية.

  • استعلام PostgreSQL (SSD): 10 مللي ثانية - 100 مللي ثانية.
  • استعلام Redis (RAM): 0.2 مللي ثانية.

يعد هذا الفارق في السرعة بمقدار 500 مرة هو السبب الوحيد الذي يجعل تطبيقات الويب واسعة النطاق تظل متصلة بالإنترنت. إذا ضربت أمازون قاعدة بيانات SQL الخاصة بها لكل عرض منتج في برايم داي، فسوف يتعطل الإنترنت بالكامل.

في Maison Code Paris، نتعامل مع Redis ليس فقط باعتباره “ذاكرة تخزين مؤقت”، ولكن كخادم بنية بيانات أساسي. وهي الطبقة التكتيكية التي تحمي الطبقة الاستراتيجية (قاعدة البيانات).

لماذا تتحدث Maison Code عن هذا

في Maison Code Paris، نعمل كضمير معمari لعملائنا. غالبًا ما نرث حزمًا “حديثة” تم بناؤها دون فهم أساسي للحجم.

نناقش هذا الموضوع لأنه يمثل نقطة تحول حاسمة في النضج الهندسي. التنفيذ الصحيح يميز MVP الهش عن منصة مؤسسية مرنة يمكنها التعامل مع حركة مرور الجمعة السوداء.

الأنماط: كيفية التخزين المؤقت بشكل صحيح

التخزين المؤقت سهل. إبطال ذاكرة التخزين المؤقت أمر مستحيل. هناك نوعان من أنماط التنفيذ الرئيسية التي نستخدمها.

1. وضع ذاكرة التخزين المؤقت جانبًا (التحميل البطيء)

هذا هو الافتراضي. التطبيق هو المسؤول عن القراءة / الكتابة.

// ديسيبل/repo.ts
استيراد {redis} من './redis'؛

دالة غير متزامنة getProduct(id: string) {
  مفتاح const = `المنتج:${id}`;
  
  // 1. تحقق من ذاكرة التخزين المؤقت
  const مخبأ = انتظار redis.get(key);
  إذا تم إرجاع (نسخة مخبأة) JSON.parse(cached);
  
  // 2. ذاكرة التخزين المؤقت مفقودة -> التحقق من قاعدة البيانات
  بيانات ثابتة = انتظار db.product.findUnique({ id });
  
  // 3. ملء ذاكرة التخزين المؤقت
  إذا (البيانات) {
     // ذاكرة التخزين المؤقت لمدة 60 ثانية (TTL)
     في انتظار redis.set(key, JSON.stringify(data), 'EX', 60);
  }
  
  إرجاع البيانات؛
}
  • الإيجابيات: مرن. إذا مات Redis، يعمل التطبيق (ببطء).
  • السلبيات: نافذة البيانات القديمة. قد يرى المستخدم السعر القديم لمدة 60 ثانية.

2. الكتابة (الإبطال المستند إلى الحدث)

نحن نفضل ذلك للبيانات عالية الاتساق (مثل المخزون). نستمع إلى تغييرات قاعدة البيانات (CDC أو أحداث التطبيق) ونقوم بقصف ذاكرة التخزين المؤقت.

// الخدمات/المنتج.ts
تحديث وظيفة غير متزامنة (المعرف: سلسلة، السعر: رقم) {
  // 1. تحديث قاعدة البيانات (مصدر الحقيقة)
  انتظر db.product.update({ حيث: { المعرف }، البيانات: { السعر } });
  
  // 2. قم بإبطال ذاكرة التخزين المؤقت على الفور
  في انتظار redis.del(`product:${id}`);
}

المشكلة الأصعب: الإبطال بالعلاقة (الوسوم)

تخيل أنك مخبأ:

  1. المنتج: 123
  2. الفئة:أحذية (يحتوي على المنتج 123)

إذا قمت بتحديث المنتج 123، فإنك تقوم بحذف “المنتج:123”. لكن الفئة:أحذية لا تزال تحتوي على الإصدار القديم من المنتج! يجب أن تعرف أيضًا أي الفئات التي تحتوي على المفتاح 123.

نقوم بتنفيذ الإبطال المستند إلى العلامات باستخدام مجموعات Redis.

  1. عند إنشاء “الفئة:أحذية”، نقوم بتتبع التبعيات: علامات SADD:المنتج:123 الفئة:أحذية
  2. عند تحديث المنتج 123:
    • ابحث عن جميع المفاتيح المعتمدة عليه: علامات SMEMBERS:المنتج:123 -> الإرجاعات ['الفئة:أحذية'].
    • احذفها: فئة DEL: أحذية.
    • تنظيف مجموعة العلامات.

هذا المنطق معقد ولكنه يحل مشكلة “لماذا لا تزال الصفحة الرئيسية تعرض الصورة القديمة؟” علة إلى الأبد.

التسلسل: تكلفة وحدة المعالجة المركزية المخفية

يعد تخزين سلاسل JSON (JSON.stringify) أمرًا قياسيًا ولكنه غير فعال.

  1. التخزين: JSON مطول. نشط: true` يستغرق 15 بايت.
  2. وحدة المعالجة المركزية: يقوم JSON.parse بحظر حلقة الحدث. بالنسبة لحمولة تبلغ 1 ميجابايت (صفحة رئيسية كبيرة)، يستغرق التحليل 20 مللي ثانية من وقت وحدة المعالجة المركزية.

الحل: MessagePack (msgpack). إنه تنسيق تسلسل ثنائي. إنه أسرع وأصغر.

استيراد {حزمة، فك} من 'msgpackr'؛
استيراد Redis من 'ioredis'؛

const redis = new Redis({
  // ioredis يدعم المخازن المؤقتة الثنائية
  بادئة المفتاح: 'v1:'،
});

في انتظار redis.set('key', pack(largeObject)); // مخازن المخزن المؤقت
const buffer = انتظار redis.getBuffer('key');
const obj = unpack(buffer);

تظهر المعايير أن msgpack أسرع بثلاث مرات في التشفير/فك التشفير من JSON الأصلي للهياكل الكبيرة.

الهياكل المتقدمة: ليس فقط المفاتيح والقيم

Redis هو “خادم بنية البيانات”. توقف عن استخدامه مثل Map<String, String>.

1. HyperLogLog (العد التقريبي)

المشكلة: “احصاء الزوار الفريدين اليوم.” ساذج: قم بتخزين كل IP في مجموعة. ساد {ip}. بالنسبة لـ 100 مليون مستخدم، يتطلب ذلك 1.5 جيجابايت من ذاكرة الوصول العشوائي. حل Redis: زوار PFADD {ip}. يستخدم HyperLogLog الرياضيات الاحتمالية (التجزئة). يضم 100 مليون عنصر فريد مع 12 كيلو بايت من ذاكرة الوصول العشوائي. الموثوقية 99.19%. هل تحتاج إلى أعداد دقيقة للزوار؟ لا، أنت بحاجة إلى معرفة “هل هو 10 ألف أم 100 ألف؟”. HLL مثالي.

2. الفهارس الجغرافية المكانية

المشكلة: “البحث عن المتاجر القريبة مني.” ساذج: صيغة SQL Haversine (بطيئة). حل Redis: مخازن GEOADD 2.3522 48.8566 "باريس". GEORADIUS يخزن 2.35 48.85 10 كيلومتر يُرجع النتائج بالميكروثانية باستخدام فرز Geohash.

3. تحديد السعر (دلو الرمز المميز)

نحن نحمي واجهات برمجة التطبيقات الخاصة بنا من DDoS باستخدام عدادات Redis. نحن نستخدم برنامج Lua النصي (لضمان الذرية) الذي ينفذ خوارزمية “النافذة المنزلقة”. “مفتاح INCR”. “مفتاح انتهاء الصلاحية 60”. إذا كان > 100، قم بالرفض.

مشكلة القطيع الهادر

إذا كانت وظيفة ذاكرة التخزين المؤقت حيوية، فإن فشلها يكون كارثيًا. السيناريو:

  1. تنتهي صلاحية مفتاح الصفحة الرئيسية في الساعة 12:00:00.
  2. في الساعة 12:00:01، قام 5000 مستخدم بطلب الصفحة الرئيسية.
  3. 5000 طلب يحصل على “ذاكرة التخزين المؤقت المفقودة”.
  4. وصل 5000 طلب إلى قاعدة بيانات Postgres في وقت واحد.
  5. يمتلئ المخزن المؤقت لإخراج قاعدة البيانات. تعطل قاعدة البيانات.
  6. تعطل موقع الويب.

الحل: قفل ذاكرة التخزين المؤقت (The Mutex). عند حدوث “خطأ”، يحاول الرمز الحصول على قفل (قفل SETNX:home_page).

  • الفائز: يُنشئ الصفحة. يكتب إلى ذاكرة التخزين المؤقت. قفل الإصدارات.
  • الخاسرون: انتظر 100 مللي ثانية. تحقق من ذاكرة التخزين المؤقت مرة أخرى. النتيجة: استعلام 1 فقط يصل إلى قاعدة البيانات.

10. مجموعة Redis مقابل Sentinel (التوفر العالي)

العقدة الواحدة Redis هي نقطة فشل واحدة. الحارس: الشاشات الرئيسية. فشل في النسخ المتماثلة. الكتلة: بيانات الأجزاء عبر عقد متعددة. بالنسبة للتجارة الإلكترونية، نادرًا ما نحتاج إلى مجموعة (التقسيم معقد). يمكن لمثيل Redis واحد التعامل مع 100 ألف عملية في الثانية. نحن نفضل الحارس. لكن احذر: يعد تكوين العميل لـ Sentinel أمرًا صعبًا. redis = new Redis({ الحراس: [{ المضيف: 'sentinel-1'، المنفذ: 26379 }]، الاسم: 'mymaster' }). إذا قمت بترميز عنوان IP الرئيسي، فلن يعمل تجاوز الفشل.

11. برمجة Lua: الذرية هي الملك

تريد “التحقق من الرصيد، خصم الأموال”. يعد القيام بذلك في Node.js (Get -> Logic -> Set) بمثابة حالة سباق. نكتب Lua Scripts التي تعمل داخل Redis. مخطوطات Lua ذرية. لا يتم تشغيل أي أمر آخر أثناء تنفيذ البرنامج النصي. وهذا يضمن أن يكون “خصم المخزون” متسقًا تمامًا، حتى مع 500 متسوق متزامن.

13. استراتيجيات تسخين ذاكرة التخزين المؤقت الذكية

يعد انتظار وصول المستخدم الأول إلى “ملكة جمال” ودفع غرامة زمن الوصول أمرًا سيئًا لتجربة المستخدم. نقوم بتنفيذ تدفئة ذاكرة التخزين المؤقت النشطة.

  1. عند النشر: يقوم بتشغيل البرنامج النصي “warm-cache.ts”.
  2. المنطق: جلب أفضل 500 عنوان URL من Google Analytics (عبر واجهة برمجة التطبيقات).
  3. الإجراء: يصل إلى واجهة برمجة التطبيقات الداخلية localhost:3000/api/render?url={top_url}. يؤدي هذا إلى إجبار الخادم على إنشاء الصفحة وملء Redis قبل وصول حركة المرور. والنتيجة هي زمن استجابة قدره 0 مللي ثانية لـ 80% من المستخدمين مباشرة بعد النشر.

14. ما وراء القيمة الرئيسية: Redis Stack (Search & JSON)

يتيح لك Redis Stack الجديد تشغيل استعلامات تشبه SQL على مستندات JSON. FT.SEARCH ProductIdx "@title:Shoes @price:[0 100]" هذا يعمل في الذاكرة. إنه أسرع بـ 50 مرة من Elasticsearch لمجموعات البيانات الصغيرة (أقل من مليون عنصر). نحن نستخدم هذا لميزات “البحث الفوري” حيث تكون Algolia باهظة الثمن أو بطيئة جدًا.

15. الاستنتاج

Redis هي أقوى أداة في حزام المرافق لمهندس الواجهة الخلفية. إنه يسد الفجوة بين العالم الجامد والبطيء لقواعد بيانات ACID وحجم الطلب الفوضوي السريع للويب الحديث.

لكنه يتطلب الانضباط. إن ذاكرة التخزين المؤقت الجامحة (بدون TTLs، ولا سياسة الإخلاء، ومفاتيح ضخمة) عبارة عن تسرب للذاكرة في انتظار تعطل الخادم الخاص بك.


ريديس أو يموت؟

هل وحدة المعالجة المركزية PostgreSQL لديك تبلغ 90٪؟

[تنفيذ التخزين المؤقت لـ Redis](/جهة اتصال). قم بتوظيف مهندسينا.