0Pricing
AWS Solutions Architect · درس

تخزين الجلسات وأنماط لوحات الصدارة

استخدم ElastiCache لتخفيف حالة جلسات HTTP عن خوادم تطبيقك، وأنشئ لوحات صدارة فورية باستخدام المجموعات المرتبة في Redis

تخزين الجلسات وأنماط لوحات الصدارة درس مجاني في AWS Solutions Architect على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في AWS Solutions Architect، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة AWS Solutions Architect 4 دروس في المجموع.

مشكلة الجلسات على جانب الخادم

تخزّن تطبيقات الويب التقليدية بيانات الجلسات في ذاكرة الخادم. ينجح ذلك مع خادم واحد، لكنه يفشل عند التوسعة أفقيًا؛ فإذا وُجّه طلب المستخدم التالي إلى مثيل EC2 مختلف، فلن يملك ذلك المثيل أي معرفة بجلسة المستخدم، وسيُسجّل خروج المستخدم. تحل الجلسات الثابتة (ارتباط الجلسة بموازن التحميل) المشكلة جزئيًا، لكنها تقلل فعالية موازنة التحميل. أما الحل القابل للتوسعة فهو نقل حالة الجلسة إلى مخزن مشترك منخفض زمن الاستجابة يمكن لجميع المثيلات الوصول إليه، وهو ما يوفره ElastiCache Redis تحديدًا.

استخدام Redis لتخزين الجلسات

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

# Session storage with Redis (Python Flask example)
import redis, json, uuid
from datetime import timedelta

redis_client = redis.Redis(host='prod-redis-primary', port=6379)
SESSION_TTL = int(timedelta(hours=8).total_seconds())

def create_session(user_id):
    session_id = str(uuid.uuid4())
    session_data = {'user_id': user_id, 'logged_in': True}
    redis_client.setex(f'session:{session_id}', SESSION_TTL, json.dumps(session_data))
    return session_id

def get_session(session_id):
    data = redis_client.get(f'session:{session_id}')
    return json.loads(data) if data else None

TTL الجلسة وانتهاء الصلاحية المنزلق

يعني TTL الثابت انتهاء صلاحية الجلسة بعد N ثانية من إنشائها، بغض النظر عن النشاط. أما TTL المنزلق (تمديد انتهاء الصلاحية عند كل وصول) فهو أكثر ملاءمة للمستخدم؛ إذ تنتهي صلاحية الجلسة بعد N ثانية من آخر وصول. في Redis، نفّذ TTL منزلقًا باستدعاء EXPIRE (أو EXPIREAT) على مفتاح الجلسة عند كل قراءة ناجحة للجلسة لإعادة ضبط عدّاد انتهاء الصلاحية. ويضمن ذلك عدم تسجيل خروج المستخدمين النشطين بشكل مفاجئ، مع انتهاء صلاحية الجلسات غير النشطة تلقائيًا وتحرير الذاكرة.

# Sliding TTL session implementation
def get_session_with_sliding_ttl(session_id, redis_client, ttl_seconds=1800):
    session_key = f'session:{session_id}'

    # Pipeline: GET + EXPIRE in one round trip
    pipe = redis_client.pipeline()
    pipe.get(session_key)
    pipe.expire(session_key, ttl_seconds)  # Reset TTL on access
    results = pipe.execute()

    data = results[0]
    if data:
        return json.loads(data)
    return None   # Session expired or not found

عربة التسوق في Redis

تُعد عربة التسوق في التجارة الإلكترونية مناسبةً طبيعيًا لـRedis. تُخزَّن كل عربة على شكل Redis Hash، بحيث يكون الحقل هو SKU المنتج والقيمة هي الكمية. وتتيح عمليات Hash، مثل HINCRBY وHDEL، إجراء تحديثات ذرية من دون استرداد العربة بأكملها وإعادة كتابتها. وبدمجه مع TTL (لانتهاء صلاحية العربات المتروكة بعد 24 ساعة)، يوفر Redis مخزنًا سريعًا ومستمرًا لعربات التسوق، من دون أعباء قاعدة البيانات العلائقية لكل حدث إضافة إلى العربة.

# Shopping cart operations using Redis Hash
cart_key = f'cart:{user_id}'

# Add item (or increase quantity)
# HINCRBY cart:user42 SKU-001 2
redis_client.hincrby(cart_key, 'SKU-001', 2)

# Remove item
# HDEL cart:user42 SKU-001
redis_client.hdel(cart_key, 'SKU-001')

# Get all items in cart
# HGETALL cart:user42
cart = redis_client.hgetall(cart_key)  # {b'SKU-001': b'2', b'SKU-002': b'1'}

# Set TTL for cart abandonment (24 hours)
redis_client.expire(cart_key, 86400)

بنية لوحة المتصدرين

تُعد لوحة المتصدرين في الوقت الفعلي من حالات الاستخدام الكلاسيكية لـ Redis، وتدعمها المجموعات المرتبة (ZSETs). يحتوي إدخال كل لاعب على نتيجة، وتحافظ المجموعة المرتبة على الأعضاء بترتيب تصاعدي للنتيجة في جميع الأوقات. وتبلغ تكلفة استعلامات لوحة المتصدرين (أفضل N من اللاعبين، وترتيب اللاعب، واللاعبين ضمن نطاق من النتائج) ‎O(log n)‎ أو ‎O(log n + m)‎، ما يجعلها سريعة للغاية حتى مع ملايين اللاعبين. وتشكل المجموعات المرتبة في Redis الأساس للعديد من ميزات الترتيب في الألعاب واللياقة البدنية والتطبيقات الاجتماعية، من دون الحاجة إلى استعلام قاعدة بيانات معقد أو إعادة حساب الترتيب عند كل عرض للصفحة.

# Real-time leaderboard with Redis Sorted Set

# Add or update a player's score
# ZADD game:weekly:leaderboard 15750 'player:alice'
redis_client.zadd('game:weekly:leaderboard', {'player:alice': 15750})

# Increment score (atomic)
# ZINCRBY game:weekly:leaderboard 500 'player:alice'
redis_client.zincrby('game:weekly:leaderboard', 500, 'player:alice')

# Get top 10 players (highest scores first)
# ZREVRANGE game:weekly:leaderboard 0 9 WITHSCORES
top_10 = redis_client.zrevrange('game:weekly:leaderboard', 0, 9, withscores=True)

ترتيب اللاعب واللاعبون القريبون

هناك ميزتان شائعتان في لوحات المتصدرين تتجاوزان «عرض أفضل 10 لاعبين»: عرض ترتيب لاعب وعرض اللاعبين القريبين من لاعب محدد. وكلتاهما بسيطتان للغاية باستخدام المجموعات المرتبة في Redis. يعيد ZREVRANK ترتيب اللاعب المفهرس بدءًا من الصفر، وفق ترتيب تنازلي للنتيجة. ولعرض 5 لاعبين أعلى من لاعب و5 لاعبين أدنى منه، احصل على ترتيبه، ثم استخدم ZREVRANGE من الرتبة 5- إلى الرتبة 5+. يوفر ذلك عرضًا مخصصًا للوحة المتصدرين باستخدام أمرين فقط من Redis، من دون الحاجة إلى دوال نافذة SQL معقدة.

# Get Alice's rank (0-indexed, so add 1 for display)
# ZREVRANK game:weekly:leaderboard 'player:alice'
rank = redis_client.zrevrank('game:weekly:leaderboard', 'player:alice')
print(f'Alice is rank #{rank + 1}')

# Get 5 players above and below Alice
start = max(0, rank - 5)
end = rank + 5
nearby = redis_client.zrevrange(
    'game:weekly:leaderboard', start, end, withscores=True
)
print('Players near Alice:', nearby)

تحديد معدل الطلبات باستخدام Redis

يُعد تحديد معدل الطلبات (تقييد عدد الطلبات التي يمكن للعميل إجراؤها ضمن نافذة زمنية) حالة استخدام أخرى عالية القيمة لـ Redis. تستخدم خوارزمية النافذة المنزلقة مجموعة مرتبة يكون كل عضو فيها طابعًا زمنيًا لطلب. عند كل طلب: أزل الأعضاء الأقدم من النافذة، ثم احسب عدد الأعضاء المتبقين، وارفض الطلب إذا تجاوز العدد الحد، وأضف الطابع الزمني الجديد. يطبق ذلك تحديدًا دقيقًا لمعدل الطلبات باستخدام نافذة منزلقة بدقة ميلي ثانية، وهو أدق بكثير من عدادات النوافذ الثابتة ومن دون الحمل الإضافي لقاعدة البيانات.

# Sliding window rate limiter (100 requests per 60 seconds)
import time

def is_rate_limited(user_id, redis_client, limit=100, window_seconds=60):
    key = f'ratelimit:{user_id}'
    now = time.time()
    window_start = now - window_seconds

    pipe = redis_client.pipeline()
    pipe.zremrangebyscore(key, '-inf', window_start)  # Remove old
    pipe.zcard(key)                                    # Count current
    pipe.zadd(key, {str(now): now})                   # Add this request
    pipe.expire(key, window_seconds)
    results = pipe.execute()

    request_count = results[1]
    return request_count >= limit  # True = rate limited

القفل الموزع باستخدام Redis

تنسق الأقفال الموزعة الوصول الحصري إلى مورد مشترك عبر عدة خوادم للتطبيق. يوفر الأمر SET key value NX EX ttl في Redis اكتسابًا ذريًا للقفل؛ إذ يعيّن المفتاح فقط إذا لم يكن موجودًا (NX = Not eXists)، كما يعيّن مدة بقاء (TTL) لمنع حالات الجمود إذا تعطل مالك القفل. عند اكتمال العملية، يحذف المالك المفتاح. توفر خوارزمية Redlock (باستخدام عدة عقد Redis للوصول إلى النصاب) قفلًا موزعًا أكثر متانة، لكنها تزيد التعقيد. وبالنسبة إلى معظم حالات الاستخدام، يكفي قفل على عقدة Redis واحدة.

# Distributed lock with Redis SET NX EX
import uuid

def acquire_lock(redis_client, resource, ttl_seconds=30):
    lock_id = str(uuid.uuid4())  # Unique ID to identify this lock holder
    key = f'lock:{resource}'
    acquired = redis_client.set(key, lock_id, nx=True, ex=ttl_seconds)
    return lock_id if acquired else None

def release_lock(redis_client, resource, lock_id):
    key = f'lock:{resource}'
    # Only delete if we still own the lock (Lua script for atomicity)
    lua = 'if redis.call("get",KEYS[1])==ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end'
    redis_client.eval(lua, 1, key, lock_id)

تخزين الجلسات: ElastiCache مقابل DynamoDB

يمكن لكل من ElastiCache Redis وDynamoDB تخزين بيانات الجلسات، لكن لكل منهما مفاضلات مختلفة. ElastiCache Redis: زمن استجابة بالميكروثانية، وتخزين في الذاكرة (متطاير ما لم يتم تفعيل الاستمرارية)، ونموذج بيانات أبسط، ويتطلب VPC. DynamoDB: زمن استجابة بالميلي ثانية أحادية الرقم (ويمكن لـ DAX أن يضاهي Redis)، ومدار بالكامل من دون عنقود يتعين صيانته، ومستمر افتراضيًا، ويمكن الوصول إليه عالميًا باستخدام Global Tables، وعديم الخوادم مع السعة عند الطلب. في اختبار SAA-C03: إذا ركز السؤال على زمن الاستجابة بالميكروثانية أو العمليات المعقدة داخل الذاكرة، فاختر Redis. وإذا ركز على الاستمرارية أو غياب الخوادم أو النطاق العالمي، ففكر في DynamoDB.

الفهرسة الجغرافية المكانية باستخدام Redis

يحتوي Redis على نوع بيانات جغرافي مكاني مضمّن (أوامر GEO) يخزن إحداثيات خطوط العرض والطول ويمكّن من تنفيذ استعلامات القرب. باستخدام GEOADD وGEODIST وGEORADIUS (وأصبح الآن GEOSEARCH في Redis 6.2)، يمكنكم العثور على جميع المواقع الواقعة ضمن نصف قطر محدد من نقطة ما في زمن ‎O(n + log n)‎. ومن حالات الاستخدام: العثور على السائقين القريبين (خدمات مشاركة الرحلات)، والعثور على المطاعم ضمن مسافة 5 كم، وترتيب نتائج البحث حسب المسافة. يؤدي ذلك إلى الاستغناء عن قاعدة بيانات جغرافية مكانية منفصلة، مع إبقاء استعلامات المواقع بسرعات الذاكرة.

# Store driver locations
# GEOADD drivers 13.361389 38.115556 'driver:001'
# GEOADD drivers 15.087269 37.502669 'driver:002'

# Find all drivers within 10 km of a point
# GEOSEARCH drivers FROMLONLAT 13.5 38.1 BYRADIUS 10 km ASC COUNT 5 WITHCOORD

# Result: sorted list of driver IDs within 10 km with coordinates

HyperLogLog لحساب الزوار الفريدين

يُعد HyperLogLog بنية بيانات احتمالية تقدّر عدد العناصر الفريدة في مجموعة باستخدام مقدار ثابت من الذاكرة (12 كيلوبايت في Redis)، بغض النظر عن عدد العناصر الفريدة المضافة. ويوفر خطأً معياريًا يبلغ نحو 0.81%. استخدموا PFADD لإضافة العناصر وPFCOUNT للحصول على التقدير. وهذا مناسب تمامًا لحساب المستخدمين النشطين الفريدين يوميًا، أو مشاهدات الصفحات الفريدة، أو عناوين IP الفريدة عندما لا تكون الأعداد الدقيقة مطلوبة وتكون كفاءة الذاكرة مهمة. فتخزين ملايين معرّفات المستخدمين الفريدة في Redis Set قد يستهلك غيغابايتات، بينما يستخدم HyperLogLog مقدار 12 كيلوبايت فقط.

# Count unique daily visitors using HyperLogLog
date = '2024-01-15'
hll_key = f'unique_visitors:{date}'

# Track a visitor (PFADD is idempotent for the same user)
# PFADD unique_visitors:2024-01-15 'user:12345'
redis_client.pfadd(hll_key, 'user:12345')
redis_client.pfadd(hll_key, 'user:67890')
redis_client.pfadd(hll_key, 'user:12345')  # Duplicate — not counted again

# Get estimated unique visitor count
# PFCOUNT unique_visitors:2024-01-15
count = redis_client.pfcount(hll_key)
print(f'Unique visitors today (estimate): {count}')

اختبار سريع

اختبروا فهمكم لمفاهيم AWS Solutions Architect (SAA-C03) التي تناولها هذا الدرس.

مراجعة الدرس

تعلمتم في هذا الدرس أن تخزين الجلسات في Redis يتيح التوسع الأفقي عديم الحالة عبر منح جميع المثيلات إمكانية الوصول إلى حالة جلسة مشتركة بزمن استجابة أقل من ميلي ثانية، وأن المجموعات المرتبة في Redis تدعم لوحات المتصدرين في الوقت الفعلي باستعلامات ترتيب بتكلفة ‎O(log n)‎، وأن أنواع بيانات Redis المتخصصة (HyperLogLog لحساب العناصر الفريدة، وGEO لاستعلامات القرب، والأقفال الموزعة) تحل مشكلات معمارية شائعة بكفاءة. بهذا يكتمل مقرر التخزين المؤقت باستخدام ElastiCache، وسنستكشف بعد ذلك البنى عالية التوافر والمتسامحة مع الأعطال.

الأسئلة الشائعة

هل درس «تخزين الجلسات وأنماط لوحات الصدارة» مجاني؟

نعم — نص درس «تخزين الجلسات وأنماط لوحات الصدارة» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة AWS Solutions Architect، انتقل إلى CoddyKit PRO. تتضمن دورة AWS Solutions Architect 4 دروس في المجموع.

ماذا ستتعلم في «تخزين الجلسات وأنماط لوحات الصدارة»؟

استخدم ElastiCache لتخفيف حالة جلسات HTTP عن خوادم تطبيقك، وأنشئ لوحات صدارة فورية باستخدام المجموعات المرتبة في Redis تتمرن على AWS Solutions Architect مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ AWS Solutions Architect؟

لا تُشترط خبرة سابقة. AWS Solutions Architect على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.

كم من الوقت يستغرق درس «تخزين الجلسات وأنماط لوحات الصدارة»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس AWS Solutions Architect هذا؟

نعم. كل درس في AWS Solutions Architect يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. Redis في مقابل Memcached: اختيار المحرك المناسب
  2. مجموعات النسخ المتماثل في ElastiCache Redis ووضع العنقود
  3. استراتيجيات التخزين المؤقت: التحميل الكسول والكتابة الفورية
  4. تخزين الجلسات وأنماط لوحات الصدارة
← العودة إلى AWS Solutions Architect