استخدامات Redis: التخزين المؤقت والطوابير وتحديد المعدل
لقد قمت ببناء تطبيق ويب يعمل بشكل جيد في بيئة التطوير، ولكن مع نمو حركة المرور، تلاحظ بطء في أوقات الاستجابة وحملاً زائداً على قاعدة البيانات. السبب غالباً هو العمليات المتكررة المكلفة: جلب نفس البيانات من قاعدة البيانات، أو معالجة المهام بشكل متزامن، أو السماح بمعدلات طلبات مسيئة. يقدم Redis، وهو مخزن بيانات في الذاكرة، حلولاً أنيقة لهذه المشكلات. في هذه المقالة، سنستكشف ثلاثة استخدامات عملية لـ Redis: التخزين المؤقت، والطوابير، وتحديد المعدل. ستتعلم كيفية تنفيذ كل نمط مع أمثلة برمجية وفهم متى تطبقها.
لماذا Redis؟
Redis هو مخزن مفتوح المصدر للقيم والمفاتيح في الذاكرة، معروف بسرعته وتعدد استخداماته. يدعم هياكل بيانات مثل السلاسل النصية، والقوائم، والمجموعات، والتجزئات (hashes)، مما يجعله مناسباً لمهام متنوعة تتجاوز التخزين المؤقت البسيط. ولأنه يحتفظ بالبيانات في الذاكرة، يوفر Redis زمن استجابة أقل من ميلي ثانية، وهو مثالي لسيناريوهات الأداء العالي. كما يوفر خيارات استمرارية، حتى لا تفقد البيانات عند إعادة التشغيل. دعنا نتعمق في حالات الاستخدام الثلاث.
1. التخزين المؤقت باستخدام Redis
التخزين المؤقت هو حالة الاستخدام الأكثر شيوعاً لـ Redis. الفكرة بسيطة: خزّن نتائج العمليات المكلفة (مثل استعلامات قاعدة البيانات أو استدعاءات API) في Redis، وقدمها مباشرة للطلبات اللاحقة. هذا يقلل الحمل على قاعدة البيانات الأساسية ويسرع أوقات الاستجابة.
كيفية تنفيذ التخزين المؤقت
- تحقق من ذاكرة التخزين المؤقت: قبل الوصول إلى قاعدة البيانات، استعلم Redis عن البيانات باستخدام مفتاح فريد (مثل
user:123). - إذا وُجد (إصابة ذاكرة التخزين المؤقت): أعد البيانات المخزنة مؤقتاً فوراً.
- إذا لم يوجد (فشل ذاكرة التخزين المؤقت): اجلب البيانات من قاعدة البيانات، وخزّنها في Redis مع وقت انتهاء صلاحية (TTL)، وأعدها.
إليك مثالاً بلغة Python باستخدام redis-py:
import redis
import json
r = redis.Redis(host='localhost', port=6379, db=0)
def get_user(user_id):
cache_key = f'user:{user_id}'
cached = r.get(cache_key)
if cached:
return json.loads(cached)
# Simulate database fetch
user = fetch_user_from_db(user_id)
r.setex(cache_key, 3600, json.dumps(user)) # Cache for 1 hour
return user
أفضل الممارسات: اختر مفاتيح ذات معنى، واضبط أوقات TTL مناسبة لتجنب البيانات القديمة، وفكر في استراتيجيات إبطال ذاكرة التخزين المؤقت. بالنسبة لأحمال العمل كثيفة القراءة، يمكن للتخزين المؤقت أن يقلل الحمل على قاعدة البيانات بدرجات كبيرة.
2. الطوابير باستخدام Redis
تفصل الطوابير المهام المستهلكة للوقت عن دورة الطلب والاستجابة. على سبيل المثال، إرسال رسائل البريد الإلكتروني، أو معالجة الصور، أو إنشاء التقارير يمكن القيام بها بشكل غير متزامن. قوائم Redis أو التدفقات (streams) مثالية لتنفيذ طوابير بسيطة.
تنفيذ طابور أساسي
استخدم LPUSH لإضافة المهام إلى يسار القائمة و BRPOP لحظر وإخراج من اليمين. هذا ينشئ طابور FIFO.
# Producer: add job
r.lpush('email_queue', json.dumps({'to': 'user@example.com', 'subject': 'Welcome'}))
# Consumer: process jobs
while True:
_, job = r.brpop('email_queue', timeout=30)
if job:
process_email(json.loads(job))
للاحتياجات الأكثر تقدماً، توفر Redis Streams مجموعات المستهلكين، والإقرارات، وإمكانية إعادة التشغيل، مما يجعلها مناسبة لمعالجة الرسائل القوية.
3. تحديد المعدل باستخدام Redis
يحمي تحديد المعدل واجهة برمجة التطبيقات الخاصة بك من الإساءة ويضمن الاستخدام العادل. العمليات الذرية في Redis تجعل من السهل تنفيذ عدادات تنتهي صلاحيتها تلقائياً.
تحديد المعدل بنافذة ثابتة
في نهج النافذة الثابتة، تسمح بـ N طلب لكل نافذة زمنية (مثلاً، 100 طلب في الساعة لكل عنوان IP). استخدم INCR و EXPIRE:
def is_allowed(ip, limit=100, window=3600):
key = f'rate:{ip}'
current = r.incr(key)
if current == 1:
r.expire(key, window)
return current