Back home

معدل ضربات ذاكرة التخزين المؤقت وجودة سياسة ذاكرة التخزين المؤقت

معدل الضرب هو مجرد مؤشر النتيجة. إن ما يحدد تكلفة النظام حقًا هو وضع الفشل وضغط العودة ونصف قطر انتشار الخطأ.

عندما تتحدث العديد من الفرق عن التخزين المؤقت، فإن أول ما يقولونه هو معدل الضرب.

معدل الإصابة 95% يبدو جيدًا؛ يبدو أن 98% قد تم تحسينه؛ حتى أن 99% تعطي الناس الوهم بأن النظام قد تم “ترويضه”.

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

حكمي بسيط: **الهدف من استراتيجية ذاكرة التخزين المؤقت هو التحكم في تكلفة “الإخفاقات”. ** معدل الدخول المرتفع يعني فقط أنه تمت قراءة العديد من الطلبات في ذاكرة التخزين المؤقت؛ وهذا لا يعني أن تصميم الفشل صحيح، ولا يعني أن تكلفة العودة إلى المصدر يمكن التحكم فيها، ولا يعني أن الأخطاء ستقتصر على نطاق صغير.

إذا زادت ذاكرة التخزين المؤقت من معدل الإصابة من 92% إلى 99%، ولكن على حساب زيادة صعوبة إبطالها، وصعوبة تحديد موقع البيانات القذرة، ومن المرجح أن تشكل عاصفة العودة إلى الأصل أثناء الارتعاش، فمن المرجح ألا يكون ذلك بمثابة تحسين، ولكنه ينقل التعقيد من قاعدة البيانات إلى زاوية أخرى.

السؤال ليس “كم عدد الضربات”، بل “أين يجب أن تضرب عندما تخطئ”

يحتوي مؤشر معدل الإصابة على عيب طبيعي: فهو يعمل على تسوية التوزيع الساخن والبارد.

تتلقى واجهة واحدة مليون طلب يوميًا، 990.000 منهم يقرأون تفاصيل المنتجات الشائعة، والـ 10.000 المتبقية يقرأون المنتجات الطويلة. ذاكرة التخزين المؤقت للبيانات الشائعة وسيكون معدل الإصابة جيدًا جدًا على الفور. ولكن إذا وقعت هذه الأخطاء الطويلة البالغ عددها 10000 في الاستعلام الأبطأ والأغلى والذي يتضمن أيضًا ربط الجداول، فقد لا يكون ضغط قاعدة البيانات أقل من المتوقع.

بمعنى آخر، يتم حساب معدل الدخول على أنه “عدد المرات”، ولكن غالبًا ما تتم تسوية تكلفة النظام على أنها “السعر”. **

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

عند الحكم على استراتيجية التخزين المؤقت، أفضل النظر إلى ثلاثة أشياء أولاً:

  1. تفويت الجزء الأغلى من الطلب؛
  2. ما إذا كانت الأخطاء ستحدث بشكل مركز وليس بالتساوي؛
  3. هل سينتقل الضغط إلى المصب بعد الخطأ؟

هذه الأسئلة الثلاثة أقرب إلى التكلفة الحقيقية من “هل ارتفع معدل النجاح الإجمالي نقطتين إضافيتين؟”

ما يحدد جودة ذاكرة التخزين المؤقت هو عادة طريقة الفشل.

العديد من حوادث ذاكرة التخزين المؤقت هي في الأساس “تصميم فاشل قاسٍ للغاية”.

الطريقة الأكثر شيوعًا هي تعيين TTL فقط. التنفيذ بسيط والمؤشرات سهلة التحسين، لأنه طالما أن مدة البقاء طويلة بما فيه الكفاية، فإن معدل الضربة عادة لا يكون سيئًا للغاية. ولكن هناك مشكلتان نموذجيتان في مخططات TTL.

أولاً، يترك وقتًا لتحديد متى ستنتهي صلاحية البيانات، بدلاً من تغيير الأعمال. **

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

ثانيًا، من السهل إنشاء حالات فشل المزامنة. **

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

لذلك، فإن أهم شيء في تصميم ذاكرة التخزين المؤقت هو غالبًا ما إذا كانت آلية الفشل تتماشى مع تغييرات العمل.

تشمل التعديلات الشائعة ما يلي:

  • استخدم حالات الفشل المستندة إلى الأحداث بدلاً من الاعتماد فقط على TTL؛
  • إضافة ارتعاش عشوائي إلى TTL لمنع انتهاء صلاحية مفاتيح نقطة الاتصال في نفس الوقت؛
  • ضع رقم الإصدار أو الطابع الزمني في المفتاح لتبديل عملية إنشاء البيانات بشكل صريح؛
  • السماح بإرجاع القيم القديمة لفترة قصيرة من الوقت، ولكن يتم تحديثها بشكل غير متزامن في الخلفية بدلاً من إرجاع جميع الطلبات إلى الأصل معًا.

الجوهر هنا هو أنك تحتاج إلى تحديد متى تقبل البيانات القديمة، ومتى يجب أن تكون متسقة على الفور، ومتى تفضل إرجاع نتيجة الرجوع إلى إصدار أقدم بدلاً من تفجير قاعدة البيانات الرئيسية. ** هذه هي استراتيجية التخزين المؤقت، ومعدل الإصابة هو مجرد ظل لها.

غالبًا ما يؤدي تمديد TTL إلى إرجاع المشكلة مرة أخرى.

لقد رأيت الكثير من “تحسين ذاكرة التخزين المؤقت” يتم إجراؤه على هذا النحو: قاعدة البيانات عبر الإنترنت تحت الضغط، لذلك تم تغيير TTL من 5 دقائق إلى 30 دقيقة، ثم إلى ساعة واحدة. ونتيجة لذلك، ارتفع معدل الدخول وأصبح منحنى قاعدة البيانات مسطحًا بعض الشيء، وأعلن الفريق أن عملية التحسين قد اكتملت.

وأخطر ما في هذا النهج هو أنه من السهل للغاية أن يبدو فعّالاً على المدى القصير.

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

خاصة بالنسبة لأنواع البيانات التالية، غالبًا ما يكون تمديد TTL تقريبًا فكرة سيئة:

  • البيانات التي ستؤثر على تسوية المبلغ؛
  • البيانات التي تؤثر على الأذونات والرؤية؛
  • البيانات التي لا يتم تحديثها بشكل متكرر، ولكن يجب تجميعها في أسرع وقت ممكن بمجرد تحديثها.

بالنسبة لهذا النوع من البيانات، إذا تم الحصول على أعلى معدل إصابة عن طريق “التعرف المتأخر على التغييرات”، فهذا يعني تجاوزًا للدقة. **

أصعب جزء في التخزين المؤقت هو الحد من الأخطاء

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

يعد التخزين المؤقت في الأنظمة الحقيقية بمثابة سطح ضغط يؤدي إلى تضخيم عيوب التصميم. إذا تم تصميم المفتاح بشكل سميك جدًا، فسيكون نطاق الفشل كبيرًا جدًا؛ إذا تم تصميم المفتاح بشكل رقيق جدًا، فسيزداد تعقيد استخدام الذاكرة وصيانتها؛ يتم وضع تجميع البيانات في طبقة ذاكرة التخزين المؤقت، ومن السهل تكرار الحسابات عند العودة إلى المصدر؛ يتم تجميع ذاكرة التخزين المؤقت المحلية وذاكرة التخزين المؤقت الموزعة معًا، وستكون هناك طبقة إضافية من مشكلات الاتساق.

لذلك سأقسم حل التخزين المؤقت إلى سؤالين للمراجعة:

  • هل أنت سعيد عادة أم لا؟
  • عندما يحدث خطأ ما، هل سيؤدي ذلك إلى سحب أجزاء أخرى من النظام إلى الأسفل؟

السؤال الأخير عادة ما يكون أكثر أهمية.

غالبًا ما يتعامل تنفيذ ذاكرة التخزين المؤقت الأكثر موثوقية مع الأمور التالية بشكل صريح:

v, ok := cache.Get(key)
if ok && !v.SoftExpired() {
  return v.Data
}

return singleflight.Do(key, func() any {
  fresh := db.Load(id)
  cache.Set(key, fresh, ttlWithJitter())
  return fresh
})

ما هو ذو قيمة حقًا هنا هو وجود قيدين:

  • عند إرجاع نفس المفتاح إلى الأصل، يُسمح لطلب واحد فقط بالوصول إلى قاعدة البيانات فعليًا؛
  • انتهاء الصلاحية يمنح النظام نافذة تحديث يمكن التحكم فيها.

قد لا يحقق هذا النوع من التصميم بالضرورة أفضل معدل إصابة، ولكنه يمكن أن يقلل بشكل كبير من العواصف المرتدة والتضخيم الفوري. بدون الارتعاش، يكون النظام أشبه بـ “التباطؤ” وليس “الانهيار”.

سوء فهم شائع: التعامل مع معدل ضربات ذاكرة التخزين المؤقت كمؤشر أداء رئيسي للفريق

بمجرد أن تصبح نسبة النجاح مؤشر أداء رئيسي، يصبح من السهل على الفرق العمل في الاتجاه الخاطئ.

لأن أسهل طريقة لتحسين معدل الإصابة غالبًا ما تكون تحسين النتائج الإحصائية:

  • تمتد TTL.
  • وضع المزيد من البيانات التي لا ينبغي تخزينها مؤقتا؛
  • استخدم المفاتيح ذات الحبيبات الخشنة لمزج المشاهد المختلفة معًا؛
  • يؤدي تقسيم الواجهة لجعلها صديقة لذاكرة التخزين المؤقت إلى جعل دلالات الأعمال أسوأ.

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

يتمثل النهج الأكثر صحة في النظر إلى التخزين المؤقت جنبًا إلى جنب مع المقاييس التالية:

  • تستغرق Miss P95/P99 وقتًا للعودة إلى المصدر؛
  • عدد مفاتيح نقطة الاتصال المتزامنة للعودة إلى المصدر؛
  • معدل الخطأ وذروة قاعدة البيانات بعد إبطال ذاكرة التخزين المؤقت؛
  • الوقت الذي تستغرقه البيانات لتتقارب مع القيم الجديدة بعد التحديث؛
  • كم عدد مسارات التعليمات البرمجية الإضافية والإجراءات التشغيلية التي تم تقديمها للحفاظ على ذاكرة التخزين المؤقت.

فقط عندما يتم تجميع هذه التكاليف معًا، يكون لمعدل النجاح قوة تفسيرية. بالنظر إلى الأمر بمفردك، من السهل جدًا أن يتم خداعك.

مثال مضاد: في بعض السيناريوهات، يجب أن يكون معدل الإصابة مرتفعًا قدر الإمكان

لا يمكنك أن تقول ذلك حتى الموت.

إذا كانت البيانات مستقرة بشكل طبيعي، أو مجرد مورد غير قابل للتغيير، مثل الملفات الثابتة، والتكوينات ذات الإصدارات، ومحتوى توزيع CDN، وجداول القاموس التي نادرًا ما تتغير، فإن معدل الدخول يعد بالفعل هدفًا مهمًا للغاية. لأنه في هذا النوع من السيناريوهات، لا يمثل “الأقدم” مشكلة خطيرة، ويكون الفشل بسيطًا نسبيًا، وعادة ما يكون مسار العودة إلى المصدر واضحًا ويمكن التحكم فيه.

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

لذا فإن هذه المقالة ضد إخراج معدل الإصابة من السياق واستخدامه بمفرده كدليل على نجاح ذاكرة التخزين المؤقت.

ملخص

للحكم على ما إذا كان حل التخزين المؤقت موثوقًا به، أود أن أسأل أولاً: **ماذا سيحدث عند حدوث أغلى خطأ في هذا النظام؟ **

إذا كانت الإجابة “أبطأ قليلاً، لا يزال النظام مستقرًا”، فمن المرجح أن تكون ذاكرة التخزين المؤقت مصممة.

إذا كانت الإجابة “عادةً ما يكون معدل الإصابة مرتفعًا جدًا، ولكن بمجرد فشله، فسوف يخترق قاعدة البيانات الرئيسية ويخفض الدقة”، فهذا مجرد خطر خفي يبدو أنه يعمل بجد في الأوقات العادية.

لا يقتصر التخزين المؤقت أبدًا على “تقريب البيانات”. ما يختبره حقًا هو كيفية التعامل مع التغييرات والإخفاقات والتكاليف. معدل الضرب المرتفع يعني أنك قد أتممت النصف الأول من الجملة بشكل جيد على الأكثر؛ فالنصف الثاني من الجملة هو الذي يحدد ما إذا كانت الإستراتيجية تستحق العناء أم لا.

FAQ

What to read next

Related

Continue reading