استراتيجيات التخزين المؤقت: التحميل الكسول والكتابة الفورية
طبّق التحميل الكسول لملء ذاكرة التخزين المؤقت عند حدوث حالات عدم العثور، والكتابة الفورية للحفاظ على اتساق ذاكرة التخزين المؤقت مع كل عملية كتابة في قاعدة البيانات
استراتيجيات التخزين المؤقت: التحميل الكسول والكتابة الفورية درس مجاني في AWS Solutions Architect على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في AWS Solutions Architect، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة AWS Solutions Architect 4 دروس في المجموع.
أهمية استراتيجيات التخزين المؤقت
يتطلب وضع ذاكرة مؤقتة بين التطبيق وقاعدة البيانات استراتيجية للتخزين المؤقت، أي مجموعة من القواعد التي تحدد متى تُضاف البيانات إلى الذاكرة المؤقتة، ومتى تُقرأ منها، ومتى تُزال. يؤدي اختيار الاستراتيجية الخاطئة إلى بيانات قديمة (تُرجع الذاكرة المؤقتة قيمًا غير محدثة)، أو إخفاقات ذاكرة مؤقتة باردة (تكون الذاكرة المؤقتة فارغة، فتصل كل الطلبات إلى قاعدة البيانات)، أو تدافع الذاكرة المؤقتة (تصل طلبات متزامنة كثيرة للمفتاح المفقود نفسه إلى قاعدة البيانات في الوقت نفسه). الاستراتيجيتان الأساسيتان هما التحميل الكسول والكتابة المتزامنة مع الذاكرة المؤقتة.
التحميل الكسول (Cache-Aside): آلية العمل
يُعد التحميل الكسول (ويُسمى أيضًا cache-aside) نمط التخزين المؤقت الأكثر شيوعًا. يتحقق التطبيق من الذاكرة المؤقتة أولًا. عند حدوث إصابة في الذاكرة المؤقتة، تُعاد البيانات مباشرةً من الذاكرة المؤقتة، وهو المسار الأسرع. وعند حدوث إخفاق في الذاكرة المؤقتة، يجلب التطبيق البيانات من قاعدة البيانات، ويكتب النتيجة في الذاكرة المؤقتة مع تحديد TTL، ثم يعيدها إلى المستدعي. ولا تُملأ الذاكرة المؤقتة إلا بالبيانات المطلوبة فعليًا، ومن هنا جاءت تسمية «كسول». وسيجد الطلب التالي للمفتاح نفسه البيانات مخزنة في الذاكرة المؤقتة.
# Lazy loading pattern (Python with redis-py)
def get_user(user_id, redis_client, db):
cache_key = f'user:{user_id}'
# 1. Check cache
cached = redis_client.get(cache_key)
if cached:
return json.loads(cached) # Cache HIT
# 2. Cache MISS: fetch from DB
user = db.query('SELECT * FROM users WHERE id = %s', user_id)
# 3. Populate cache with TTL of 300 seconds
redis_client.setex(cache_key, 300, json.dumps(user))
return userالتحميل الكسول: المزايا
يتمتع التحميل الكسول بثلاث مزايا رئيسية: تُخزَّن البيانات المطلوبة فقط، فلا تُملأ الذاكرة المؤقتة ببيانات لا يقرأها أحد، ما يضمن استخدام الذاكرة بكفاءة. لا يؤدي فراغ الذاكرة المؤقتة إلى تعطل التطبيق، فعند إخفاقات الذاكرة المؤقتة يعود التطبيق إلى قاعدة البيانات، ولذلك يستمر في العمل حتى إذا أُعيد تشغيل ElastiCache أو تعطلت عقدة، مع ارتفاع زمن الاستجابة. تظل الذاكرة المؤقتة متسقة في النهاية مع قاعدة البيانات، لأن البيانات القديمة تنتهي صلاحيتها عبر TTL حتى إذا لم تُلتقط بعض التحديثات.
التحميل الكسول: العيوب
للتحميل الكسول ثلاثة عيوب رئيسية: إخفاقات الذاكرة المؤقتة مكلفة، إذ تتطلب ثلاث عمليات (التحقق من الذاكرة المؤقتة، والقراءة من قاعدة البيانات، والكتابة في الذاكرة المؤقتة) مقابل عملية واحدة عند حدوث إصابة، ما يسبب زمن استجابة أعلى للطلبات الباردة. البيانات القديمة، إذ تواصل الذاكرة المؤقتة تقديم القيمة القديمة بعد تحديث قاعدة البيانات إلى أن تنتهي صلاحية TTL أو يُبطل المفتاح صراحةً، ما ينشئ نافذة لعدم الاتساق. تدافع الذاكرة المؤقتة، إذ تؤدي انتهاء صلاحية مفتاح شائع إلى تعرض طلبات متزامنة كثيرة لإخفاق في الذاكرة المؤقتة ووصولها جميعًا إلى قاعدة البيانات في الوقت نفسه، ما قد يحمّلها فوق طاقتها.
# Mitigating cache stampede with a probabilistic early expiration
# (Refresh the key before it expires to avoid simultaneous misses)
def get_with_stampede_protection(key, redis_client, db_fetch_fn, ttl=300):
value = redis_client.get(key)
ttl_remaining = redis_client.ttl(key)
# Probabilistically refresh before expiry
if value is None or (ttl_remaining < 30 and random.random() < 0.1):
value = db_fetch_fn()
redis_client.setex(key, ttl, json.dumps(value))
return json.loads(value)الكتابة المتزامنة مع الذاكرة المؤقتة: آلية العمل
في استراتيجية الكتابة المتزامنة مع الذاكرة المؤقتة، تُكتب كل عملية كتابة في قاعدة البيانات أيضًا في الذاكرة المؤقتة في الوقت نفسه. يكتب التطبيق في الذاكرة المؤقتة وقاعدة البيانات ضمن العملية نفسها، أو تؤدي قاعدة البيانات إلى تحديث الذاكرة المؤقتة. لذلك تظل الذاكرة المؤقتة متزامنة دائمًا مع قاعدة البيانات، ولا توجد نافذة لعرض بيانات قديمة. وتضمن هذه الاستراتيجية أن تكون البيانات الموجودة في الذاكرة المؤقتة محدثة دائمًا، ما يجعل القراءات اللاحقة للبيانات المكتوبة حديثًا إصابات في الذاكرة المؤقتة.
# Write-through pattern (Python)
def update_user(user_id, user_data, redis_client, db):
# 1. Write to database FIRST
db.execute('UPDATE users SET name=%s WHERE id=%s',
(user_data['name'], user_id))
# 2. Update cache immediately (write-through)
cache_key = f'user:{user_id}'
redis_client.setex(cache_key, 3600, json.dumps(user_data))
return user_data
# Every read is now a cache hit for recently updated dataالكتابة المتزامنة مع الذاكرة المؤقتة: المزايا
من مزايا الكتابة المتزامنة مع الذاكرة المؤقتة: تظل بيانات الذاكرة المؤقتة حديثة دائمًا، فلا توجد بيانات قديمة لأن الذاكرة المؤقتة تُحدّث مع كل عملية كتابة. تكون القراءات سريعة دائمًا، إذ تبقى البيانات الشائعة التي تُحدّث باستمرار في الذاكرة المؤقتة. لا يحدث تدافع للذاكرة المؤقتة أثناء القراءة، لأن البيانات تُملأ مسبقًا قبل قراءتها، فلا توجد إخفاقات باردة للبيانات المكتوبة حديثًا. وتناسب هذه الاستراتيجية أحمال العمل كثيفة القراءة وكثيرة التحديثات التي تكون حداثة البيانات فيها مهمة، مثل كتالوجات المنتجات وأنظمة التسعير وذاكرات ملفات تعريف المستخدمين.
الكتابة المتزامنة مع الذاكرة المؤقتة: العيوب
من عيوب الكتابة المتزامنة مع الذاكرة المؤقتة: كلفة الكتابة، إذ تتطلب كل عملية كتابة عمليتين (قاعدة البيانات والذاكرة المؤقتة)، ما يضيف زمن استجابة إلى عمليات الكتابة. تلويث الذاكرة المؤقتة، إذ تُخزَّن البيانات حتى إذا لم تُقرأ مجددًا، مثل البيانات التي تُكتب مرة واحدة ولا تُطلب بعد ذلك، ما يهدر مساحة الذاكرة المؤقتة. إعادة تشغيل الذاكرة المؤقتة تعني برودة الذاكرة، فإذا أُعيد تشغيل عنقود الذاكرة المؤقتة، فستُفقد جميع البيانات المكتوبة استباقيًا، ويجب ملء الذاكرة مجددًا من خلال عمليات الكتابة أو عملية تهيئة للذاكرة المؤقتة. ادمج هذه الاستراتيجية مع TTL لمنع النمو غير المحدود للبيانات المخزنة التي نادرًا ما تُقرأ.
دمج التحميل الكسول والكتابة المتزامنة مع الذاكرة المؤقتة
تجمع كثير من أنظمة الإنتاج عمليًا بين الاستراتيجيتين: استخدم الكتابة المتزامنة مع الذاكرة المؤقتة للبيانات كثيرة التحديث وكثيرة القراءة، مثل جلسات المستخدمين أو الأسعار الحالية، والتحميل الكسول للبيانات قليلة التحديث وكثيرة القراءة، مثل أوصاف المنتجات أو محتوى المقالات. حدّد قيم TTL مناسبة لكليهما: تحصل مفاتيح الكتابة المتزامنة على TTL طويل لأن البيانات تكون حديثة دائمًا، بينما تحصل المفاتيح المحمّلة كسولًا على TTL أقصر للحد من نافذة البيانات القديمة. يزيد هذا النهج الهجين معدل إصابات الذاكرة المؤقتة إلى أقصى حد، مع تقليل قدم البيانات.
# Hybrid: write-through for sessions, lazy loading for product data
# Write-through for session data (always fresh, critical)
def save_session(session_id, data, redis_client, db):
db.upsert('sessions', session_id, data)
redis_client.setex(f'session:{session_id}', 3600, json.dumps(data))
# Lazy loading for product catalog (infrequent updates OK)
def get_product(product_id, redis_client, db):
cached = redis_client.get(f'product:{product_id}')
if cached:
return json.loads(cached)
product = db.query('SELECT * FROM products WHERE id=%s', product_id)
redis_client.setex(f'product:{product_id}', 86400, json.dumps(product))
return productمبادئ تصميم TTL
يتحكم Time-To-Live (TTL) للبيانات المخزنة مؤقتًا في الحد الأقصى لنافذة البيانات القديمة وفي حجم الذاكرة الذي تشغله الذاكرة المؤقتة. صمّم قيم TTL استنادًا إلى: تكرار تحديث البيانات (تتغير بيانات الجلسات كثيرًا ← TTL قصير؛ ويتغير المحتوى الثابت نادرًا ← TTL طويل)، ومدى تحمل قدم البيانات (أسعار المعاملات المالية ← مدة قصيرة جدًا؛ ونصوص منشورات المدونات ← ساعات أو أيام)، وسعة ذاكرة الذاكرة المؤقتة (سعة منخفضة ← TTL أقصر لإزالة البيانات القديمة بسرعة أكبر). حدّد TTL دائمًا، ولا تخزّن البيانات مؤقتًا من دون انتهاء صلاحية، وإلا فستمتلئ الذاكرة المؤقتة في النهاية بالبيانات القديمة.
# TTL examples by data type
# API rate limit counter: 60 seconds
redis_client.setex(f'ratelimit:{ip}', 60, count)
# User session: 30 minutes
redis_client.setex(f'session:{id}', 1800, json.dumps(session))
# Product catalog: 24 hours
redis_client.setex(f'product:{id}', 86400, json.dumps(product))
# Stock price: 10 seconds
redis_client.setex(f'price:{symbol}', 10, price)
# Static site content: 7 days
redis_client.setex(f'page:{slug}', 604800, html_content)إبطال الذاكرة المؤقتة عند التحديث
بدلًا من الاعتماد على انتهاء TTL وحده، يمكنك إبطال (حذف) مفاتيح الذاكرة المؤقتة صراحةً عند تغير البيانات الأساسية. يؤدي ذلك إلى إزالة نافذة البيانات القديمة بالكامل. ومن الأنماط الشائعة: الحذف عند الكتابة (حذف مفتاح الذاكرة المؤقتة بعد كل تحديث لقاعدة البيانات، ثم يعيد الطلب التالي ملأه عبر التحميل الكسول)، والإبطال المستند إلى الأحداث (تؤدي DynamoDB Streams أو آلية التقاط التغييرات في RDS إلى تشغيل Lambda تحذف المفاتيح المتأثرة). يُعد إبطال الذاكرة المؤقتة من أصعب المشكلات في الأنظمة الموزعة؛ وكلما كان منطق الإبطال أبسط، كانت الذاكرة المؤقتة أكثر موثوقية.
# Delete-on-write invalidation pattern
def update_product(product_id, new_data, redis_client, db):
# Update the database
db.execute('UPDATE products SET ... WHERE id=%s', (product_id,))
# Invalidate the cache key
redis_client.delete(f'product:{product_id}')
# Also invalidate any list/search caches that may include this product
redis_client.delete('products:list:page:1')
redis_client.delete(f'products:category:{new_data["category_id"]}')
# Next read will trigger lazy loading with fresh dataالتخزين المؤقت بالكتابة اللاحقة (Write-Back)
يُعد التخزين المؤقت بالكتابة اللاحقة (write-back) نمطًا أقل شيوعًا لكنه قوي؛ إذ يكتب التطبيق في الذاكرة المؤقتة فقط، ثم تفرغ الذاكرة المؤقتة البيانات بشكل غير متزامن إلى قاعدة البيانات في الخلفية. يوفر ذلك عمليات كتابة سريعة للغاية (لأنها تتم في الذاكرة فقط)، على حساب احتمال فقدان البيانات إذا تعطلت الذاكرة المؤقتة قبل تفريغها. يناسب هذا النمط عمليات الكتابة عالية التكرار والحجم، حيث يمكن إعادة إنشاء البيانات أو تكون الخسائر الصغيرة مقبولة، مثل عدادات الزيارات وأحداث التحليلات وتحديثات نتائج الألعاب. لا يدعم ElastiCache الكتابة اللاحقة بشكل أصلي؛ بل يجب تنفيذها على مستوى التطبيق.
# Write-behind pattern: write to cache, flush to DB asynchronously
# Application writes:
# redis_client.incr('post:42:views') # fast, in-memory only
# Background job (runs every 60 seconds):
def flush_view_counts(redis_client, db):
for key in redis_client.scan_iter('post:*:views'):
count = redis_client.getdel(key) # atomic get-and-delete
post_id = key.split(':')[1]
db.execute('UPDATE posts SET views = views + %s WHERE id = %s',
(int(count), post_id))تحقق سريع
اختبر مدى فهمك لمفاهيم AWS Solutions Architect (SAA-C03) الواردة في هذا الدرس.
مراجعة الدرس
تعلمت في هذا الدرس أن التحميل الكسول يملأ الذاكرة المؤقتة عند إخفاقات القراءة ويستخدم الذاكرة بكفاءة، لكنه قد يقدم بيانات قديمة حتى انتهاء TTL؛ وأن الكتابة المتزامنة مع الذاكرة المؤقتة تحدّث الذاكرة المؤقتة مع كل عملية كتابة، فلا تترك بيانات قديمة، لكنها تهدر الذاكرة عند تخزين بيانات لا تُقرأ؛ وأن الإبطال الصريح يحذف مفاتيح الذاكرة المؤقتة عند تحديث قاعدة البيانات لإزالة نوافذ البيانات القديمة. سنتناول بعد ذلك أنماط تخزين الجلسات ولوحات الصدارة باستخدام ElastiCache.
الأسئلة الشائعة
هل درس «استراتيجيات التخزين المؤقت: التحميل الكسول والكتابة الفورية» مجاني؟
نعم — نص درس «استراتيجيات التخزين المؤقت: التحميل الكسول والكتابة الفورية» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة AWS Solutions Architect، انتقل إلى CoddyKit PRO. تتضمن دورة AWS Solutions Architect 4 دروس في المجموع.
ماذا ستتعلم في «استراتيجيات التخزين المؤقت: التحميل الكسول والكتابة الفورية»؟
طبّق التحميل الكسول لملء ذاكرة التخزين المؤقت عند حدوث حالات عدم العثور، والكتابة الفورية للحفاظ على اتساق ذاكرة التخزين المؤقت مع كل عملية كتابة في قاعدة البيانات تتمرن على AWS Solutions Architect مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ AWS Solutions Architect؟
لا تُشترط خبرة سابقة. AWS Solutions Architect على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.
كم من الوقت يستغرق درس «استراتيجيات التخزين المؤقت: التحميل الكسول والكتابة الفورية»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس AWS Solutions Architect هذا؟
نعم. كل درس في AWS Solutions Architect يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- Redis في مقابل Memcached: اختيار المحرك المناسب
- مجموعات النسخ المتماثل في ElastiCache Redis ووضع العنقود
- استراتيجيات التخزين المؤقت: التحميل الكسول والكتابة الفورية
- تخزين الجلسات وأنماط لوحات الصدارة