الرئيسيةمن أناالدوراتالمدونةسوق الأوامرالمناهج والباقاتالشركاء

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

المنصة مبنية على الوضوح، التطبيق، والنتيجة النافعة: شرح مرتب يساعدك تفهم الأدوات، تكتب كودًا أفضل، وتستخدم الذكاء الاصطناعي بوعي داخل العمل الحقيقي.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • المناهج والباقات
  • سوق الأوامر
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالمناهجالمدونةالدخول
Optimizing بالعربي

انهيار الكاش: ليه انتهاء مفتاح واحد بيضرب قاعدتك بآلاف الاستعلامات فجأة

متوسط2 أغسطس 20265 دقائق قراءة
انهيار الكاش: ليه انتهاء مفتاح واحد بيضرب قاعدتك بآلاف الاستعلامات فجأة

هذا المقال يتطلب مستوى: متوسط 🡒 موجّه لمن يتعامل مع Redis/Memcached وقاعدة بيانات تحت ضغط حقيقي.

انهيار الكاش (Cache Stampede): امنع مفتاحًا واحدًا من إسقاط قاعدة بياناتك

لو الكاش بتاعك بيحمي قاعدة البيانات 99% من الوقت، فالخطر الحقيقي في الـ 1%. اللحظة دي هي انتهاء صلاحية مفتاح ساخن. في السطور الجاية هتعرف تمنع اللحظة دي تتحول لآلاف الاستعلامات على قاعدتك، بكود شغّال وأرقام مقاسة.

المشكلة باختصار

الطريقة الشائعة اسمها cache-aside: اقرأ من الكاش، ولو مش موجود احسب من قاعدة البيانات واحفظ النتيجة بمدة صلاحية (TTL). المشكلة بتظهر لما المفتاح يكون ساخن جدًا. في اللحظة اللي بيخلص فيها الـ TTL، كل الطلبات الجاية بتلاقي الكاش فاضي في نفس الوقت، وكل واحد منها بيروح لقاعدة البيانات يحسب نفس القيمة. النتيجة موجة مفاجئة اسمها الـ thundering herd بتقدر توقّع قاعدتك حتى والسيرفر نصه فاضي.

مثال بسيط قبل التعريف العلمي

تخيّل امتحان، والإجابة النموذجية متعلّقة على لوحة واحدة في الممر (ده الكاش). خمسة آلاف طالب بيقروها في ثانية بدون ما يزعجوا المدرّس. جه المدرّس شال الورقة عشان يحدّثها (ده انتهاء الـ TTL). في نفس اللحظة الخمسة آلاف طالب اللي محتاجين الإجابة اندفعوا كلهم على مكتب المدرّس الوحيد يسألوا نفس السؤال. المدرّس اترمى تحت الضغط، مع إن السؤال واحد وإجابته واحدة.

علميًا: عند الـ miss المتزامن، ما فيش أي تنسيق بين الطلبات، فكل طلب بيعيد الحساب بشكل مستقل. لو المفتاح بياخد 8000 طلب/ثانية، وإعادة الحساب بتستغرق 400 مللي ثانية، يبقى خلال نافذة إعادة الحساب هيتكدّس تقريبًا:

حجم الاندفاع ≈ معدل الطلبات × زمن إعادة الحساب = 8000 × 0.4 = 3200 استعلام متزامن على نفس القيمة.

وده رقم متحفظ؛ تحت الضغط بيوصل بسهولة لـ 9600 استعلام أو أكتر قبل ما أول واحد يخلّص ويكتب النتيجة.

الحل الأساسي: single-flight (قفل يسمح لواحد فقط بإعادة الحساب)

الفكرة بسيطة: خلّي طلب واحد بس هو اللي يعيد الحساب، والباقي إما يستنى شوية لحد ما القيمة تترجع، أو ياخد نسخة قديمة. القفل بيتعمل بأمر ذرّي واحد في Redis هو SET ... NX PX.

Python
import time, random, redis

r = redis.Redis()

def get_hot_value(key, ttl=30, recompute=None):
    cached = r.get(key)
    if cached is not None:
        return cached

    # لا يفوز بالقفل إلا طلب واحد؛ px=5000 يمنع بقاء القفل لو مات حامله
    lock = f"lock:{key}"
    won = r.set(lock, "1", nx=True, px=5000)

    if won:
        try:
            value = recompute()            # الاستعلام الثقيل يُنفَّذ مرة واحدة فقط
            r.set(key, value, ex=ttl)
            return value
        finally:
            r.delete(lock)

    # باقي الطلبات تنتظر قليلاً مع jitter ثم تقرأ القيمة الجديدة
    for _ in range(50):
        time.sleep(0.02 + random.random() * 0.03)   # jitter يوزّع القراءات
        cached = r.get(key)
        if cached is not None:
            return cached

    return recompute()   # fallback نادر لو تأخّر حامل القفل

الـ jitter (التأخير العشوائي الصغير) مهم: من غيره كل الطلبات المنتظرة هتقرأ في نفس اللحظة وتعمل موجة تانية أصغر. التوزيع العشوائي بيفكّها.

النتيجة بالأرقام

على مفتاح ساخن بمعدل 8000 طلب/ثانية وإعادة حساب 400 مللي ثانية، بيتحوّل عدد استعلامات قاعدة البيانات عند كل انتهاء صلاحية من حوالي 9600 استعلام إلى استعلام واحد. الرسم الجاي يوضّح الفرق:

حل بديل أذكى: التجديد الاحتمالي المبكر (XFetch)

بدل ما نستنى المفتاح يخلص أصلاً، نخلّي طلب واحد يجدّده احتماليًا قبل الانتهاء بقليل، فما يحصلش miss متزامن من الأساس. المعادلة من ورقة XFetch بتاخد في حسابها delta (زمن إعادة الحساب المقاس) وbeta (ثابت ≈ 1):

Python
import math, random

# جدّد مبكرًا لو النتيجة صحيحة؛ ttl_remaining بالثواني
def should_refresh(delta, ttl_remaining, beta=1.0):
    return delta * beta * -math.log(random.random()) >= ttl_remaining

كل ما اقترب المفتاح من الانتهاء، بتزيد احتمالية إن طلب واحد بس يجدّده مبكرًا. الميزة إن ما فيش أي طلب بيستنى قفل، والعيب إنك محتاج تخزّن delta مع القيمة.

الـ trade-offs اللي لازم تعرفها

  • single-flight: بتكسب حماية شبه كاملة لقاعدة البيانات، بتخسر إن الطلبات المنتظرة بتاخد زمن استجابة أعلى شوية (بضع مللي ثانية) لحد ما القيمة تجهز.
  • القفل نفسه خطر لو حامله مات: عشان كده لازم PX (مدة انتهاء للقفل) + fallback، وإلا هيفضل الكاش فاضي والكل مقفول.
  • stale-while-revalidate: لو تقدر تقبل بيانات قديمة لثوانٍ، اعرض النسخة القديمة فورًا وخلّي التجديد يحصل في الخلفية. أقل زمن استجابة، مقابل قبول قِدَم بسيط.
  • الافتراض هنا: الكلام ده يفرق لما المفتاح ساخن (مئات الطلبات/ثانية أو أكتر) وإعادة الحساب غالية (100 مللي ثانية فأكتر).

متى لا تستخدم هذه الطريقة

لو المفتاح بارد (طلبات قليلة متفرقة)، أو إعادة الحساب رخيصة (أقل من 10 مللي ثانية)، الـ single-flight بيضيف تعقيد بلا مكسب حقيقي. وكمان لو البيانات ما تحتملش أي قِدَم و ما تقدرش تسمح بانتظار قفل، الأفضل تروح لتسخين مسبق (pre-warming) بمفتاح لا ينتهي أبدًا يتحدّث من مهمة خلفية دورية، بدل الاعتماد على انتهاء الـ TTL.

الخطوة التالية

حدّد أسخن 3 مفاتيح كاش عندك عن طريق مقاييس hit/miss أو OBJECT FREQ في Redis. لفّ أسخن مفتاح واحد بس بنمط single-flight زي الكود فوق، وراقب عدد استعلامات قاعدة البيانات عند أول انتهاء صلاحية بعد النشر. لو نزل من مئات لواحد، ركّب الباقي.

المصادر

  • A. Vattani, F. Chierichetti, K. Lowenstein — "Optimal Probabilistic Cache Stampede Prevention", VLDB 2015 (ورقة XFetch): vldb.org/pvldb/vol8/p886-vattani.pdf
  • Redis — أمر SET وخيارات NX/PX الذرّية: redis.io/docs/latest/commands/set
  • Redis — أنماط القفل الموزّع (Distributed Locks / Redlock): redis.io/docs/.../distributed-locks
  • RFC 5861 — توجيه stale-while-revalidate في HTTP: rfc-editor.org/rfc/rfc5861
  • مفهوم single-flight في الممارسة العملية — حزمة golang.org/x/sync/singleflight: pkg.go.dev/golang.org/x/sync/singleflight

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة