الفكرة في سطر
الكاش مخزن سريع وقريب بتحط فيه نسخة من حاجة غالية تجيبها، عشان المرة الجاية تاخدها من غير ما تدفع التمن تاني.
- الحاجة الغالية: query تقيلة على الداتابيز، أو حساب، أو call لـ API تاني.
- المخزن السريع: غالبًا Redis أو Memcached (في الـ memory، على الشبكة)، أو memory الـ process نفسه.
- النسخة دي مؤقتة: ليها عمر (TTL)، وممكن تبقى قديمة عن الداتابيز شوية.
بيكسّبك إيه؟
سرعة (latency)
قراءة من Redis في نفس الـ data center بتاخد أقل من ملّي ثانية تقريبًا. query عليها joins وaggregations ممكن تاخد عشرات أو مئات الملّي ثانية.
حمل أقل على الداتابيز
كل request بيتخدم من الكاش هو request مراحش للداتابيز. فالداتابيز بتفضل فاضية للـ writes والـ queries اللي ملهاش كاش.
فلوس أقل
تكبير الداتابيز (CPU أكتر، replicas) غالي وصعب. شوية Redis قدامها غالبًا أرخص بكتير من إنك تكبّرها.
امتصاص الزحمة
لما الـ traffic يعلى فجأة على نفس البيانات، الكاش بيشيل الضربة بدل الداتابيز. بس لو اتعمل صح، وده موضوع المذكرة كلها.
إمتى الكاش يفيد؟
- القراءة أكتر بكتير من الكتابة: صفحة منتج بتتقري ألف مرة وبتتعدل مرة في اليوم.
- الـ query غالية: joins كتير، أو
COUNT/SUMعلى جداول كبيرة، أو ترتيب وفلترة. - نفس البيانات بتتطلب كتير: الـ homepage، الـ categories، الإعدادات، المنتجات المشهورة.
- البيانات تستحمل تبقى قديمة شوية: عدد المشاهدات لو اتأخر ٣٠ ثانية مفيش مشكلة.
إمتى الكاش يضر أو ملوش لازمة؟
- الكتابة كتير: لو القيمة بتتغير كل ثانية، هتقضي وقتك بتمسح الكاش وتملاه، والـ hit rate هيبقى واطي.
- محتاج consistency قوية: رصيد الحساب البنكي، المخزون وقت الدفع، الصلاحيات بعد ما اتسحبت. هنا القيمة القديمة غلطة مش تأخير.
- الـ hit rate واطي: لو كل request بيطلب key مختلف، الكاش مجرد خطوة زيادة قبل الداتابيز.
- بيانات خاصة بكل يوزر ومش بتتكرر: نتيجة بحث فريدة ليوزر واحد مش هتتطلب تاني غالبًا.
- الداتابيز أصلًا سريعة: query على primary key بـ index بتاخد ملّي ثانية. اتأكد إن فيه مشكلة قبل ما تزود طبقة.
نكاش إيه؟
| النوع | مثال | ملاحظة |
|---|---|---|
| نتيجة query | SELECT * FROM products WHERE id = $1 | الأشهر، والـ key فيه الـ id |
| حساب مجمّع | عدد الطلبات النهارده، الـ top 10 | غالي جدًا يتحسب كل مرة |
| object جاهز | المنتج بالـ reviews والصور مع بعض | بتجمّع كذا query في قيمة واحدة |
| session | بيانات اليوزر اللي عامل login | غالبًا Redis هو المكان الأساسي ليها مش كاش |
| response كامل | JSON بتاع endpoint عام | لو نفس الـ response لكل الناس |
حسبة الـ hit rate
الـ hit rate هو نسبة الـ requests اللي لقت القيمة في الكاش.
الحسبة
hit rate = hits / (hits + misses)متوسط الوقت = (hit rate × وقت الكاش) + (miss rate × (وقت الكاش + وقت الداتابيز))حمل الداتابيز = عدد الـ requests في الثانية × miss rateمثال: 10,000 request في الثانية، والكاش بياخد 1ms، والداتابيز 20ms.
| الـ hit rate | متوسط الوقت | الـ requests اللي بتوصل الداتابيز |
|---|---|---|
| 0% (من غير كاش) | 20ms | 10,000 / ثانية |
| 90% | 1 + 0.10 × 20 = 3ms | 1,000 / ثانية |
| 95% | 1 + 0.05 × 20 = 2ms | 500 / ثانية |
| 99% | 1 + 0.01 × 20 = 1.2ms | 100 / ثانية |
- لاحظ القفزة: من 95% لـ 99% شكلها ٤% بس، بس حمل الداتابيز قلّ ٥ مرات.
- يعني إيه؟ الداتابيز متصممة على إن الـ miss rate صغير. لو الكاش فجأة بطّل يشتغل (أو key سخن خلص)، الحمل بيرجع ١٠,٠٠٠ مرة واحدة. ودي بالظبط بداية المشكلة اللي في المذكرة.
خلّصت الفصل لما
🧠 اختبر نفسك
١) رصيد المحفظة وقت الدفع، تكاشه؟
لأ. محتاج consistency قوية، والقيمة القديمة هنا ممكن تخلي اليوزر يدفع بفلوس مش معاه.
٢) 20,000 request في الثانية والـ hit rate بتاعك 98%. كام request بيوصل الداتابيز؟
20,000 × 0.02 = 400 request في الثانية.
٣) كل يوزر بيعمل بحث بكلمات مختلفة تمامًا. الكاش هيفيد؟
غالبًا لأ، لأن نفس الـ key مش هيتطلب تاني، فالـ hit rate هيبقى واطي والكاش خطوة زيادة.