حول المحتوى:
شرح استخدام Redis لتخزين النتائج، صفحات الكاش، إدارة الجلسات، والكيوز، مع أمثلة مباشرة ومقارنة مع الذاكرة الداخلية.
استخدام Redis كمخزن مؤقت (Cache) أصبح من أهم الأدوات لتحسين أداء تطبيقات الويب الحديثة، خصوصًا مع أطر مثل Django وFastAPI. في هذا المقال سنشرح كيف تستخدم Redis Django FastAPI Cache لتقليل الحمل على قاعدة البيانات، تسريع الاستجابة، وإدارة الجلسات والكيوز بشكل فعّال، مع مقارنات مباشرة مع التخزين في الذاكرة فقط.
إذا كنت مهتمًا بالأداء والمهام الخلفية، قد يفيدك أيضًا مقالنا عن التعامل مع Background Tasks في Django وFastAPI، وكذلك مقال البرمجة غير المتزامنة في بايثون: تحسين الأداء باستخدام async و await.
Redis هو مخزن بيانات في الذاكرة (In-memory Data Store) يدعم هياكل بيانات مختلفة مثل: Strings, Lists, Hashes, Sets. يمكن استخدامه كـ:
بما أنه يعمل في الذاكرة، فهو أسرع بكثير من قواعد البيانات التقليدية مثل PostgreSQL أو MySQL، لكن يمكن ضبطه ليكتب على القرص لضمان عدم ضياع البيانات حسب الحاجة.
أطر مثل Django و FastAPI تستطيع العمل بدون Redis، لكنها ستعتمد على:
هذا يسبب عدة مشاكل:
باستخدام Redis Django FastAPI Cache يمكنك تخزين الكاش في مكان مركزي، سريع، مشترك بين كل عمليات التطبيق، ويمكن التحكم في انتهاء صلاحيته (TTL).
لربط Django مع Redis كـ Cache Backend:
pip install redis django-redis
في ملف settings.py:
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
هنا نستخدم قاعدة بيانات Redis رقم 1 (/1) للكاش. يمكنك استخدام قواعد مختلفة لأغراض مختلفة (مثل الجلسات أو الكيوز).
افترض أن لدينا موديل Article ونريد تخزين قائمة المقالات الأكثر قراءة:
from django.core.cache import cache
from django.shortcuts import render
from .models import Article
def popular_articles(request):
cache_key = "popular_articles"
articles = cache.get(cache_key)
if articles is None:
# استعلام ثقيل أو متكرر
articles = Article.objects.order_by("-views")[:10]
# تخزين النتيجة في Redis لمدة 5 دقائق (300 ثانية)
cache.set(cache_key, articles, timeout=300)
return render(request, "articles/popular.html", {"articles": articles})
بهذه الطريقة:
يمكنك استخدام Decorator cache_page مع Redis Backend:
from django.views.decorators.cache import cache_page
from django.urls import path
from .views import popular_articles
urlpatterns = [
path("popular/", cache_page(60 * 5)(popular_articles)), # كاش لمدة 5 دقائق
]
الآن نتيجة الـ View كاملة يتم تخزينها في Redis كـ صفحة HTML جاهزة، وDjango لن ينفذ الكود الداخلي إلا بعد انتهاء مدة الكاش.
بدل تخزين الجلسات في قاعدة البيانات، يمكنك استخدام Redis لتسريعها:
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default"
بهذا الشكل، جلسات المستخدمين تخزن في Redis بدلًا من جدول في قاعدة البيانات، مما يقلل القراءة والكتابة من وإلى الـ DB.
pip install redis fastapi uvicorn
في المشاريع غير المتزامنة (async)، يفضّل استخدام عميل Redis غير متزامن مثل redis>=4 مع Async API أو مكتبات مثل aioredis (إذا كنت تستخدم إصدارات أقدم).
from fastapi import FastAPI, Depends
import redis.asyncio as redis
app = FastAPI()
async def get_redis():
r = redis.Redis(host="localhost", port=6379, db=1)
try:
yield r
finally:
await r.close()
@app.on_event("startup")
async def startup_event():
# يمكن إضافة أي تهيئة هنا لو احتجت
pass
افترض أن لديك Endpoint يعيد بيانات إحصائية تعتمد على قاعدة البيانات أو خدمة خارجية:
from fastapi import HTTPException
from datetime import timedelta
import json
CACHE_EXPIRE_SECONDS = 300 # خمس دقائق
@app.get("/stats")
async def get_stats(r: redis.Redis = Depends(get_redis)):
cache_key = "stats:v1"
cached = await r.get(cache_key)
if cached:
# البيانات مخزنة كسلسلة JSON
return json.loads(cached)
# هنا مكان استعلام ثقيل أو اتصال بخدمة خارجية
# مثال مبسط:
stats_data = {
"users": 1200,
"active": 200,
"orders": 50,
}
await r.setex(cache_key, CACHE_EXPIRE_SECONDS, json.dumps(stats_data))
return stats_data
النتيجة: أول طلب يحسب الإحصائيات، والطلبات التالية خلال 5 دقائق تقرأ مباشرة من Redis، مما يقلل الحمل على قاعدة البيانات أو الخدمات الخارجية.
من الاستخدامات الشائعة لـ Redis مع FastAPI: بناء Queue بسيطة للمهام الخلفية.
@app.post("/send-email")
async def send_email_task(email: str, r: redis.Redis = Depends(get_redis)):
task = {"type": "send_email", "email": email}
await r.lpush("tasks_queue", json.dumps(task))
return {"detail": "task_queued"}
ثم Worker منفصل (اسكربت بايثون) يقوم بسحب المهام من الكيو:
import asyncio
import redis.asyncio as redis
import json
async def worker():
r = redis.Redis(host="localhost", port=6379, db=2)
while True:
task_data = await r.brpop("tasks_queue", timeout=0)
_, task_json = task_data
task = json.loads(task_json)
if task["type"] == "send_email":
print(f"Sending email to {task['email']}")
# تنفيذ الارسال الفعلي...
await asyncio.sleep(0)
if __name__ == "__main__":
asyncio.run(worker())
بهذا الأسلوب، يمكن لتطبيق FastAPI معالجة الطلبات بسرعة (فقط يدفع المهمة للكيو)، بينما يقوم الـ Worker بتنفيذ المهام الثقيلة في الخلفية.
بعض المطورين يستخدمون متغيرات Global أو Dict داخل الكود لتخزين الكاش:
cache_local = {}
def get_data():
if "key" in cache_local:
return cache_local["key"]
data = heavy_db_query()
cache_local["key"] = data
return data
هذه الطريقة لها عيوب واضحة:
تجنب تخزين بيانات تتغير باستمرار أو ذات طبيعة حساسة جدًا تحتاج إلى تحديث لحظي إلا إذا كان الكاش قصير المدى جدًا.
user:<id>:profile، article:list:popular.home_page:lang:ar.INFO وMONITOR أو أدوات الـ Dashboard.سيناريو شائع:
في هذه الحالة:
هذا التصميم يقلل بشكل كبير من الحمل على قاعدة البيانات الرئيسية، ويجعل التطبيق قابلاً للتوسع (Scale Out) عن طريق إضافة مزيد من الـ Workers والسيرفرات بدون تعقيدات كبيرة.
استخدام Redis Django FastAPI Cache خطوة أساسية لأي تطبيق يسعى إلى أداء عالي وقابلية للتوسع. Redis ليس فقط مخزن كاش، بل منصة متكاملة:
إذا كنت تعمل على API كبير باستخدام Django REST، يمكنك الاستفادة من الكاش كما شرحنا هنا بالتوازي مع ما تعلمته في دليل شامل لإطار Django REST مع أمثلة، لتصل لأقصى أداء ممكن مع تقليل الحمل على قاعدة البيانات.
ابدأ بخطوات بسيطة: كاش لاستعلامات ثقيلة أو صفحات عامة، ثم تدريجيًا وسّع استخدام Redis للجلسات والكيوز. مع الوقت، ستلاحظ فرقًا واضحًا في استجابة التطبيق واستهلاك موارد السيرفر.
شرح استخدام Redis لتخزين النتائج، صفحات الكاش، إدارة الجلسات، والكيوز، مع أمثلة مباشرة ومقارنة مع الذاكرة الداخلية.
مساحة اعلانية