الكاش مع ElastiCache
ليش القراءة من قاعدة البيانات في كل طلب بطيئة ومكلفة، وكيف يحلها الكاش في الذاكرة، ومحركات Valkey وRedis وMemcached، وطرق إبقاء البيانات محدّثة، مع تجربة عملية تبني فيها كاش ولوحة صدارة.
- الفكرة الأساسية
- تعريف
- متى يُستخدم
- تنبيه
في هذا المقال
في الجزء الثامن بنينا قاعدة بيانات قوية. بس تخيل الصفحة الرئيسية في متجرك تعرض نفس 20 منتج لكل زائر. آلاف الزوار يعني قاعدة البيانات تنفذ نفس الاستعلام آلاف المرات، وترجع نفس الجواب.
الحل بسيط: احفظ الجواب في مكان سريع، وارجع له بدل ما تسأل قاعدة البيانات كل مرة. هذا الكاش.
وش هو الكاش؟
الكاش نسخة مؤقتة من بيانات تكلّفك وقت أو جهد عشان تجيبها. والذاكرة أسرع بكثير من القرص، فالجواب يوصل عادةً في أقل من ملي ثانية. وهذا يعطيك:
- سرعة أعلى للمستخدم.
- ضغط أقل على قاعدة البيانات، فتقدر تخدم زوار أكثر بقاعدة أصغر.
والقاعدة الأهم: الكاش مو مصدر الحقيقة. لو ضاع كله، يصير تطبيقك أبطأ شوي، بس ما تضيع أي بيانات مهمة.
ElastiCache خدمة كاش مُدارة: AWS تتكفل بالسيرفرات والتحديثات والتحويل عند الأعطال. بس بخلاف RDS، لازم تعدّل كود تطبيقك، لأن التطبيق هو اللي يقرر وش يحفظ ومتى يقرأ.
اختيار المحرك
| Valkey وRedis OSS | Memcached | |
|---|---|---|
| أنواع البيانات | نصوص وقوائم ومجموعات ومجموعات مرتبة وغيرها | قيم بسيطة |
| النسخ والتحويل | نسخ قراءة وMulti-AZ وتحويل تلقائي | ما فيه نسخ في الكلستر اللي تصممه بنفسك |
| الحفظ | لقطات تسترجع منها | ما فيه، البيانات في الذاكرة فقط |
| التوسّع | تقسيم البيانات مع نسخ | تقسيم على عدة عقد، ومعالجة متعددة الخيوط |
| يناسب | أغلب الحالات: كاش وجلسات ولوحات صدارة | كاش بسيط ما يضر لو ضاع |
وعندك طريقتين للتشغيل:
| Serverless | كلستر تصممه بنفسك | |
|---|---|---|
| الإعداد | اسم وخلاص تقريباً | تختار نوع العقد وعدد التقسيمات والنسخ |
| التوسّع | تلقائي | تخطط له بنفسك |
| الحساب | حجم البيانات والعمليات | بالساعة لكل عقدة |
| يناسب | الضغط المتقلب، وهو أسهل بداية | الضغط الثابت والتحكم الدقيق في التكلفة |
كيف يستخدمه تطبيقك؟
القراءة عند الطلب (Lazy Loading)
هذي أشهر طريقة، وتسمى كمان Cache-Aside: تطبيقك يسأل الكاش أول، ولو ما لقى الجواب يروح لقاعدة البيانات، ثم يحفظ الجواب للمرة الجاية.
وبالكود تقريباً كذا:
def get_product(product_id):
key = f"product:{product_id}"
cached = cache.get(key)
if cached:
return json.loads(cached)
product = db.fetch_product(product_id)
cache.set(key, json.dumps(product), ex=300) # صالح 5 دقايق
return product
- المزايا: ما يخزّن إلا اللي ينطلب فعلاً، ولو تعطّل الكاش يكمل التطبيق من قاعدة البيانات.
- العيوب: أول طلب بطيء، والبيانات ممكن تكون قديمة لين تنتهي صلاحيتها.
الكتابة المباشرة (Write-Through)
كل ما كتبت في قاعدة البيانات، تكتب في الكاش معها.
- المزايا: الكاش محدّث دايماً.
- العيوب: كتابة زيادة في كل مرة، وممكن يمتلي الكاش ببيانات ما أحد يقرأها.
وكثير يجمعون الطريقتين: قراءة عند الطلب مع تحديث الكاش عند الكتابة في البيانات المهمة.
مدة الصلاحية والإخراج
لكل مفتاح مدة صلاحية (TTL)، وبعدها يختفي، فما تبقى البيانات القديمة للأبد. واختيارها يعتمد على البيانات:
| البيانات | مدة مناسبة | السبب |
|---|---|---|
| الصفحة الرئيسية والقوائم | 30 إلى 60 ثانية | حتى مدة قصيرة تشيل أغلب الضغط |
| تفاصيل منتج | دقايق | ما يضر لو تأخر شوي |
| جلسة مستخدم | طول الجلسة، مثل 30 دقيقة | تتجدد مع النشاط |
| رصيد أو مخزون نهائي | لا تخزنه | لازم يكون دقيق |
ولما تمتلي الذاكرة، يخرج الكاش بعض المفاتيح (Eviction)، عادةً الأقل استخداماً. ولو هذا يتكرر كثير، فالكاش صغير عليك.
أكثر من مجرد كاش
الجلسات: تذكر جدول الجزء السابع؟ لما تحفظ الجلسة في ElastiCache، أي سيرفر يقدر يقراها، فالمستخدم ما يخرج لو اختفى سيرفره.
لوحات الصدارة: المجموعة المرتبة (Sorted Set) تبقي العناصر مرتبة حسب النقاط، وتعطيك الأوائل فوراً، بدل ما ترتب ملايين الصفوف في قاعدة البيانات.
تحديد المعدل: عدّاد INCR مع مدة صلاحية يحسب طلبات كل مستخدم في الدقيقة، ويوقف اللي يتجاوز الحد.
الأمان والمراقبة
- الشبكة: الكاش افتراضياً ما يتوصل له إلا من داخل شبكتك، فخل مجموعة الأمان تقبل من مجموعة أمان السيرفرات فقط.
- التشفير: فعّله أثناء النقل وفي التخزين، وهو مفعّل دايماً في Serverless.
- الدخول: في Valkey وRedis استخدم مستخدمين وصلاحيات أو مصادقة IAM، وفي Memcached استخدم SASL.
ومن CloudWatch راقب هذي المقاييس:
| المقياس | يقول لك |
|---|---|
CacheHitRate |
كم طلب لقى جوابه في الكاش. لو منخفض، راجع وش تخزن ومدة الصلاحية |
Evictions |
كم مفتاح انطرد لضيق الذاكرة |
| استهلاك الذاكرة | هل تحتاج كاش أكبر |
عملي: كاش ولوحة صدارة
بنستخدم سيرفرك من الجزء الثالث. من CloudShell، سوّ مجموعة أمان تقبل من مجموعة أمان السيرفر. وخدمة Serverless تستخدم المنفذين 6379 و6380:
aws ec2 create-security-group --group-name cache-sg \
--description "Cache access" --query GroupId --output text
aws ec2 authorize-security-group-ingress --group-id sg-CACHE-GROUP \
--protocol tcp --port 6379-6380 --source-group sg-SERVER-GROUP
ثم أنشئ الكاش بالمعرّف اللي طلع لك:
aws elasticache create-serverless-cache \
--serverless-cache-name series-cache \
--engine valkey \
--security-group-ids sg-CACHE-GROUP
aws elasticache describe-serverless-caches \
--serverless-cache-name series-cache \
--query "ServerlessCaches[0].[Status,Endpoint.Address]" --output table
لما تصير الحالة available، ادخل على السيرفر من EC2 Instance Connect واتصل:
sudo dnf install -y valkey
valkey-cli -h YOUR-CACHE-ENDPOINT --tls -c -p 6379
جرّب مدة الصلاحية:
SET product:1 "Keyboard"
GET product:1
SET offer:today "20% off" EX 30
TTL offer:today
أعد TTL كم مرة وشوف العد التنازلي. وبعد 30 ثانية، GET offer:today يرجع (nil).
الحين لوحة صدارة:
ZADD leaderboard 120 sara 300 omar 90 lina
ZINCRBY leaderboard 250 lina
ZREVRANGE leaderboard 0 2 WITHSCORES
بتشوف lina صارت الأولى بـ 340 نقطة، بدون أي ترتيب من جهتك. اخرج بـ exit.
aws elasticache delete-serverless-cache --serverless-cache-name series-cache
aws ec2 delete-security-group --group-id sg-CACHE-GROUP
الخلاصة
- الكاش نسخة مؤقتة وسريعة، ومو مصدر الحقيقة.
- ابدأ بـ Valkey، وبـ Serverless لو ضغطك متقلب أو تبي بداية سهلة.
- القراءة عند الطلب أشهر طريقة، والكتابة المباشرة تبقيه محدّث.
- مدة الصلاحية تحدد كم تتحمل بيانات قديمة، وبعض البيانات ما ينفع تخزنها.
- ينفع كمان للجلسات ولوحات الصدارة وتحديد المعدل.
اختبر نفسك
قبل ما تكمل، جرّب هذي الأسئلة عشان تتأكد إنك فهمت الجزء. كل سؤال موقف عملي، وبعد ما تجاوب يطلع لك الشرح.
السؤال 1 من 5
الصفحة الرئيسية لمتجر تشغّل نفس الاستعلام على RDS آلاف المرات في الدقيقة، ومعالج القاعدة مرتفع. والبيانات تتغير كم مرة في الساعة فقط. وش يعطي أسرع رد ويريح القاعدة أكثر؟
اعرض الجواب
الجواب الصحيح: C
الجواب نادراً ما يتغير، فما تحتاج القاعدة تحسبه كل مرة. والذاكرة ترد في أقل من ملي ثانية، وحتى TTL القصير يشيل أغلب الحمل.
- A: النسخة الاحتياطية ما تخدم القراءة، فالحمل يبقى نفسه.
- B: هذا يزيد السعة، بس نفس الاستعلام لسا يشتغل كل مرة.
- D: Redshift للتقارير الكبيرة، مو لرد سريع على صفحة.
للمراجعة: وش هو الكاش؟ في هذا الجزء، وCaching strategies في وثائق AWS.
السؤال 2 من 5
لعبة جوال تعرض أفضل 100 لاعب مباشرة. ولوحة الصدارة لازم تستمر لو تعطلت عقدة كاش، مع تحويل تلقائي. أي خيار يناسب؟
اعرض الجواب
الجواب الصحيح: A
Sorted Set يبقي اللاعبين مرتبين بالنقاط ويرجع الأوائل فوراً، وValkey يدعم النسخ مع التحويل التلقائي.
- B: Memcached يخزن قيم بسيطة، وما فيه نسخ في المجموعة اللي تصممها.
- C: ترتيب ملايين الصفوف مع كل طلب يضغط القاعدة كثير.
- D: S3 مو مبني لملف يتغير باستمرار ولازم ينقرأ مباشرة.
للمراجعة: اختيار المحرك في هذا الجزء، وComparing engines في وثائق AWS.
السؤال 3 من 5
متجر يستخدم Lazy Loading مع TTL ساعة، فممكن يشوف العميل سعر قديم حتى ساعة بعد تغييره. والأسعار لازم الحين تتحدث خلال دقيقة، مع الإبقاء على فايدة الكاش. وش يسوي الفريق؟
اعرض الجواب
الجواب الصحيح: D
Lazy Loading لحاله يخلي البيانات قديمة لين تنتهي. والتعامل مع الكاش عند كل كتابة يبقيه محدث، والـ TTL الأقصر يحد الضرر لو فات تحديث.
- A: هذا يحل القِدم، بس يرجع كل الحمل على القاعدة.
- B: هذا يزيد المشكلة.
- C: القيمة القديمة جاية من الكاش، مو من القاعدة.
للمراجعة: الكتابة المباشرة في هذا الجزء، وCaching strategies في وثائق AWS.
السؤال 4 من 5
تطبيق يعبي الكاش عند التشغيل، ويعطي كل المفاتيح نفس الـ TTL. وفي نهاية هذي المدة، يقفز الحمل على القاعدة فجأة، ويتكرر هذا. وش أبسط حل؟
اعرض الجواب
الجواب الصحيح: B
لما تنتهي المفاتيح مع بعض، الطلبات عليها كلها تروح للقاعدة بنفس اللحظة. والقيمة العشوائية توزع هذا الانتهاء على وقت أطول.
- A: المشكلة في القاعدة وقت الانتهاء، مو في حجم الكاش.
- C: المفاتيح ما تتجدد أبداً، فتبقى البيانات قديمة.
- D: هذا يحمي الكاش من الأعطال، والمفاتيح لسا تنتهي مع بعض.
للمراجعة: مدة الصلاحية والإخراج في هذا الجزء، وBest practices for TTLs and jitter في وثائق AWS.
السؤال 5 من 5
فريق يشوف مقياس Evictions يرتفع وCacheHitRate ينزل في الكاش حقه. أي إجراءين يعالجون هذا؟ (اختر اثنين.)
اعرض الجواب
الجواب الصحيح: A + C
الـ Evictions تعني الكاش ممتلئ ويطرد مفاتيح، وهذا يتحول لطلبات ما تلقى جوابها. فإما توسع له بذاكرة أكثر، أو توقف تعبئته باللي ما أحد يقرأه.
- B: هذا يساعد القاعدة، بس ما يغير ذاكرة الكاش.
- D: التشفير ما له علاقة بالطرد، وبتخسر حمايته.
- E: S3 أبطأ بكثير، وهذا يلغي فكرة الكاش.
للمراجعة: الأمان والمراقبة في هذا الجزء، وElastiCache for e-commerce في وثائق AWS.
عرفت الحين RDS وAurora وElastiCache، وAWS عندها خدمات أكثر بكثير. في الجزء العاشر نجاوب على السؤال اللي يحيّر الكل: أي قاعدة بيانات تختار؟