بنية التخزين المؤقت والمراسلة

Redis في بيئة الإنتاج

18 دقيقة الدرس 3 من 30

Redis في بيئة الإنتاج

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

ضبط سياسة الإخلاء

عندما يقترب Redis من حد maxmemory، يجب أن يقرر ماذا يفعل. تتحكم في هذا الإعداد maxmemory-policy، واختيار السياسة الخاطئة لحمل عملك هو أحد أشيع أسباب الأعطال الخفية في الإنتاج.

تنقسم السياسات الثماني عبر محورين: ما الذي يُخلى (LRU — الأقل استخداماً حديثاً، LFU — الأقل استخداماً بالتكرار، TTL — الأقرب للانتهاء، أو عشوائي) وأي فضاء مفاتيح يُستعمَل للعيّنة (جميع المفاتيح، أو فقط المفاتيح التي لها TTL محدد). المصفوفة العملية:

  • noeviction — رفض الكتابات حين تمتلئ الذاكرة. استخدمه للبيانات الأساسية التي لا يمكن خسارتها (مثل قائمة انتظار مدعومة بقوائم Redis). سيحصل تطبيقك على أخطاء OOM command not allowed ويجب معالجتها بلطف.
  • allkeys-lru — إخلاء المفتاح الأقل استخداماً حديثاً عبر جميع المفاتيح. الافتراضي الصحيح للذاكرة المؤقتة حيث جميع المفاتيح مرشحة للإخلاء بالتساوي. تستخدمه Netflix في طبقة EVCache.
  • volatile-lru — إخلاء المفاتيح ذات TTL فقط. آمن للأحمال المختلطة حيث بعض المفاتيح دائمة (العدادات، محددات المعدل) وبعضها مؤقت (رموز الجلسة). تُخلى المفاتيح المؤقتة فقط.
  • allkeys-lfu — إخلاء المفاتيح الأقل تكراراً في الاستخدام. أفضل من LRU للأحمال ذات نمط المسح-مرة-واحدة (مجموعات بيانات كبيرة يتم التكرار عبرها دورياً بدلاً من الوصول الساخن). Redis 4.0+.
  • volatile-ttl — إخلاء المفتاح الأقرب إلى انتهاء TTL. مفيد حين يُشفّر TTL أولوية العمل — المفاتيح المنتهية قريباً هي الأقل قيمة.
مخاطرة: noeviction مع النمو غير المحدود. أحياناً يُعيّن المشغّلون noeviction ظناً منهم أنه يعني "لا تحذف البيانات أبداً". ما يعنيه فعلاً هو "أوقف قبول الكتابات عند الامتلاء". إذا لم يلتقط تطبيقك خطأ OOM ويُعيد المحاولة بتأخير تراجعي، فسيُسقط الأحداث بصمت أو يُفشل الطلبات أو يتعطل. إذا كنت تحتاج حقاً إلى الاستدامة، استخدم المثابرة (RDB + AOF) وحجّم نسختك بحيث لا تصل أبداً إلى maxmemory، أو استخدم بنية مدعومة بقائمة انتظار.

تعتمد سياسات إخلاء LFU على ذراعيْن تحكّم يُتحكم بهما في مدى سرعة تحلّل عداد التكرار وكمية العشوائية الاحتمالية في الإدراج:

# /etc/redis/redis.conf — ضبط الإخلاء maxmemory 12gb maxmemory-policy allkeys-lfu # LFU decay: تنصيف عداد التكرار كل 10 دقائق # قيمة أقل = نسيان أسرع = LFU يتصرف أشبه بـ LRU # النطاق: 1 (تحلل سريع) إلى 255 (بطيء جداً) lfu-decay-time 10 # LFU log factor: مدى الزيادة العدوانية للعداد لكل وصول # أعلى = أصعب الوصول للحد الأقصى (255) = مزيد من التمييز بين المفاتيح # الافتراضي 10 مناسب لمعظم الأحمال lfu-log-factor 10 # عدد المفاتيح التي يأخذ منها Redis عينة عند البحث عن مرشحي الإخلاء # أعلى = تقريب أفضل لـ LRU/LFU الحقيقي، المزيد من وحدة المعالجة المركزية لكل إخلاء # 10 توازن جيد؛ 5 هو الافتراضي maxmemory-samples 10
تحديد حجم maxmemory: اضبطه على 75–80% من RAM الفعلية للنسخة. اترك هامشاً لبيانات التعريف الداخلية لـ Redis (كل مفتاح له تكلفة ~50–90 بايت بغض النظر عن حجم القيمة)، ومخازن النسخ المتماثلة، وذاكرة التخزين المؤقت لصفحات نظام التشغيل. على نسخة 16 جيجابايت، اضبط maxmemory 12gb. راقب used_memory_rss في منظومة مقاييسك — إذا تجاوز RSS باستمرار الذاكرة الفعلية، فأنت تستخدم الإبدال (Swap) مما يُدمّر الكمون.

المفاتيح الساخنة

المفتاح الساخن (Hot Key) هو مفتاح واحد يستقبل حصة غير متناسبة من حركة المرور — غالباً آلاف المرات من الطلبات في الثانية مقارنة بالمفتاح العادي. نظراً لأن Redis ذو خيط معالجة واحد (للمعالجة الأمرية)، يمكن لمفتاح ساخن أن يُشبع وحدة المعالجة المركزية على شريحة واحدة، مما يُنشئ طابور انتظار من الأوامر المعلّقة ويُسبب ارتفاعات الكمون عبر النسخة بأكملها حتى للمفاتيح غير الساخنة.

سيناريوهات المفاتيح الساخنة الكلاسيكية: عداد الإعجابات بتغريدة رائجة، عداد مخزون منتج في تخفيض، علامة ميزة يتحقق منها كل طلب API، أو مفتاح جلسة لحساب خدمة مشترك.

# تحديد المفاتيح الساخنة باستخدام ماسح LFU المدمج (يتطلب سياسة LFU نشطة) redis-cli --hotkeys -h 10.0.1.50 -p 6379 -a "$REDIS_PASSWORD" # على Redis القديم أو للدقة العالية: أخذ عينة من تدفق الأوامر # --bigkeys و--hotkeys يستخدمان SCAN داخلياً وآمنان للإنتاج redis-cli --bigkeys -h 10.0.1.50 -p 6379 # للكشف الفوري عن المفاتيح الساخنة بدون LFU: استخدم أمر MONITOR # تحذير: MONITOR يُخفض الإنتاجية ~50% — شغّله على نسخة متماثلة، ليس الأساسية # استخدمه لفترة وجيزة (ثوان)، لا تتركه يعمل redis-cli --no-auth-warning -h 10.0.1.50 -p 6379 MONITOR | \ head -n 50000 | \ awk '{print $4}' | \ sort | uniq -c | sort -rn | head -20 # فحص عداد الوصول لكل مفتاح مباشرة (LFU يجب أن يكون نشطاً) redis-cli OBJECT FREQ session:user:12345 # يُعيد: (integer) 148 — عداد التكرار اللوغاريتمي

التخفيف من المفاتيح الساخنة على نطاق الإنتاج يتطلب استراتيجية، ليس مجرد كشف:

  • التخزين المؤقت على جانب العميل (Redis 6+): استخدم بروتوكول تتبع العميل بحيث تحتفظ خوادم التطبيق بنسخة محلية في الذاكرة. يُرسل خادم Redis رسائل إبطال عندما يتغير المفتاح. يُلغي هذا رحلة الشبكة ذهاباً وإياباً للمفاتيح الساخنة الغالبة على القراءة.
  • تجزئة المفاتيح (Key Sharding): لمفتاح ساخن مثل العداد، قسّمه إلى N أجزاء (counter:foo:0 حتى counter:foo:N-1)، زد جزءاً عشوائياً عند الكتابة، واجمع جميع الأجزاء عند القراءة. تستخدم Facebook هذا بمضاعفة المفاتيح 10x+ لعداداتها الأكثر تنافساً.
  • نسخ القراءة المتماثلة مع التوجيه على جانب العميل: وجّه قراءات المفاتيح الساخنة إلى النسخ المتماثلة. لا يفعل Redis Cluster هذا تلقائياً — يجب تطبيقه على طبقة العميل أو الوكيل.
  • ذاكرة التخزين المؤقت المحلية في العملية (L1): خزّن المفتاح الساخن في ذاكرة التطبيق بـ TTL قصير (1–5 ثوان). التقادم لثانية مقبول لمعظم المفاتيح الساخنة غالبة القراءة ويُلغي حركة مرور Redis بالكامل.

المفاتيح الكبيرة

المفتاح الكبير (Big Key) هو مفتاح قيمته كبيرة بما يكفي لإحداث مشاكل تشغيلية: أوامر بطيئة تُعطّل حلقة الأحداث، ارتفاعات حمولة النسخ المتماثلة الكبيرة، وإبطاء تسلسل RDB. يعني نموذج Redis أحادي الخيط أن حذف أو قراءة قيمة 10 ميجابايت يُعطّل كل عميل آخر لعشرات المللي ثانية.

الحدود المهمة على نطاق الإنتاج: السلاسل > 1 ميجابايت، القوائم/المجموعات/قوائم Hash/المجموعات المرتبة > 5,000 عنصر (أو > 1 ميجابايت مُسلسَل). أي شيء يتجاوز هذه الحدود هو مرشح لإعادة التصميم.

# فحص المفاتيح الكبيرة بدون إغلاق (يستخدم SCAN المؤشري، آمن في الإنتاج) redis-cli --bigkeys -h 10.0.1.50 -p 6379 # الحصول على الحجم المُسلسَل لمفتاح محدد بالبايت redis-cli MEMORY USAGE mykey:12345 # يُعيد: (integer) 2097152 (2 ميجابايت — هذا كبير جداً) # فحص عدد العناصر لأنواع المجموعات redis-cli LLEN mylist redis-cli HLEN myhash redis-cli SCARD myset redis-cli ZCARD myzset # الحذف الآمن لمفتاح كبير بدون إغلاق: استخدم UNLINK (غير متزامن، O(1)) # UNLINK يُحرر الذاكرة في خيط خلفية؛ DEL يُعطّل redis-cli UNLINK bigkey:session:all_events # للمجموعات الكبيرة: الحذف التدريجي عبر HSCAN/SSCAN + HDEL/SREM redis-cli HSCAN bigkey:user:prefs 0 COUNT 100 # معالجة 100 عنصر في كل مرة، ثم HDEL عليها عبر pipeline
استخدم دائماً UNLINK بدلاً من DEL لأي مفتاح أكبر من ~100 كيلوبايت. DEL متزامن ويُعطّل الخيط الرئيسي أثناء تحرير الذاكرة. UNLINK (Redis 4.0+) يُجري نفس العملية المنطقية لكنه يُؤجّل استرداد الذاكرة الفعلي لخيط خلفية. للمفاتيح الكبيرة جداً (عشرات الميجابايت)، يمكن أن يصل الفرق إلى 50–200 ميلي ثانية من الإغلاق. في بيئة إنتاج حيث SLO الـ p99 لك هو 10 ميلي ثانية، استدعاء DEL واحد يمكن أن يتجاوز ميزانية الخطأ بأكملها.
Redis production operational concerns: hot keys, big keys, eviction, and latency spikes Redis Production: Four Operational Concerns Hot Key Single key >> avg QPS Saturates single CPU core Fix: key sharding, local L1 cache, client-side tracking Detect: --hotkeys / LFU Big Key Value > 1 MB or > 5k elements Blocks event loop on DEL Fix: UNLINK, SCAN incremental delete, redesign data model Eviction Wrong policy = data loss or write rejections Cache: allkeys-lfu/lru Mixed: volatile-lru Queue: noeviction Monitor: evicted_keys/s Latency Spike Slow commands Fork for RDB/AOF Memory defrag Fix: LATENCY HISTORY, slowlog, tune fork, disable KEYS/SORT Redis Instance Single-threaded event loop Monitor: instantaneous_ops_per_sec · used_memory_rss · evicted_keys · latency_p99 · connected_clients · rejected_connections
المخاوف التشغيلية الأربعة الرئيسية التي تؤثر على حلقة أحداث Redis أحادية الخيط والمقاييس الرئيسية لمراقبة كل منها.

تشخيص ارتفاعات الكمون

ارتفاعات كمون Redis تأتي من مجموعة محدودة من الأسباب. معرفة التصنيف يُمكّنك من الانتقال من "Redis بطيء" إلى السبب الجذري في دقائق، وليس ساعات.

1. الأوامر البطيئة. الأوامر O(N) مثل KEYS *، وSMEMBERS على مجموعة كبيرة، وSORT، وLRANGE بنطاقات كبيرة تُعطّل حلقة الأحداث. يلتقطها السجل البطيء (slowlog) تلقائياً:

# سجل Redis البطيء — الأوامر التي تجاوزت الحد (الافتراضي: 10ms) # اضبط الحد أقل في الإنتاج: 1ms يلتقط الذيل الطويل redis-cli CONFIG SET slowlog-log-slower-than 1000 # ميكروثانية (1ms) redis-cli CONFIG SET slowlog-max-len 256 # قراءة السجل البطيء redis-cli SLOWLOG GET 20 # الإخراج: معرّف، طابع زمني، ميكروثانية، [أمر، وسائط...] # استخدام إطار مراقبة الكمون (Redis 2.8.13+) redis-cli CONFIG SET latency-monitor-threshold 10 # ms redis-cli LATENCY LATEST # آخر ارتفاع لكل فئة حدث redis-cli LATENCY HISTORY command # السلسلة الزمنية لحدث محدد # إعادة تعيين عينات الكمون (مفيد بعد إصلاح السبب الجذري) redis-cli LATENCY RESET

2. كمون Fork (RDB/AOF rewrite). عندما يتفرّع Redis للمثابرة، يجب على نظام التشغيل نسخ جدول الصفحات. على نسخة بحجم 20 جيجابايت مع جداول صفحات 512 ميجابايت، يمكن أن يستغرق Fork نفسه 200–500 ميلي ثانية ويُعطّل الخيط الرئيسي. الأعراض: ارتفاع كمون منتظم عند كل فترة حفظ أو دورة إعادة كتابة AOF. التخفيف: استخدم نسخ Redis أصغر (حافظ على مجموعات البيانات تحت 10 جيجابايت لكل شريحة)، وعطّل Transparent Huge Pages — يوصي Redis صراحةً بـ echo never > /sys/kernel/mm/transparent_hugepage/enabled — وفضّل النسخ المتماثلة لحفظ RDB.

3. إزالة تجزئة الذاكرة النشطة. تتراكم تجزئة الذاكرة في نسخ Redis طويلة الأمد. عندما تتجاوز mem_fragmentation_ratio نسبة 1.5، فأنت تُهدر ذاكرة كبيرة. Redis 4.0+ يحتوي على إزالة تجزئة عبر الإنترنت، لكنها تستهلك وحدة المعالجة المركزية ويمكن أن تُسبب ارتفاعات كمون قصيرة خلال نوافذ الضغط:

# فحص نسبة التجزئة (used_memory_rss / used_memory) redis-cli INFO memory | grep -E "used_memory:|mem_fragmentation_ratio" # used_memory: 8589934592 (8 جيجابايت منطقية) # mem_fragmentation_ratio: 1.62 (>1.5 يعني ~5 جيجابايت مُهدَرة على RSS) # تفعيل إزالة التجزئة النشطة (اضبط ميزانية وحدة المعالجة بعناية) redis-cli CONFIG SET activedefrag yes redis-cli CONFIG SET active-defrag-ignore-bytes 100mb # ابدأ فقط عند >100 ميجابايت مجزأ redis-cli CONFIG SET active-defrag-enabled yes redis-cli CONFIG SET active-defrag-cpu-pct 25 # أقصى 25% وحدة معالجة لإزالة التجزئة redis-cli CONFIG SET active-defrag-threshold-lower 10 # % تجزئة للبدء redis-cli CONFIG SET active-defrag-threshold-upper 30 # % للانطلاق بالسرعة الكاملة

4. الأسباب على مستوى الشبكة ونظام التشغيل. الكمون الذي لا يستطيع Redis نفسه تفسيره — تحقق بـ redis-cli --latency (يقيس الرحلة ذهاباً وإياباً، وليس تنفيذ الأمر) مقابل LATENCY HISTORY (يقيس تنفيذ الأمر). إذا كان كمون الرحلة ذهاباً وإياباً مرتفعاً لكن كمون الأمر منخفضاً، فالسبب هو الشبكة أو جدولة نظام التشغيل. شغّل redis-cli --intrinsic-latency 30 على الخادم لتحديد خط أساس نظام التشغيل.

مراقبة Redis في الإنتاج

منظومة مراقبة Redis في الإنتاج تحتاج ثلاث طبقات: مقاييس فورية (Prometheus)، تنبيه منظّم (قواعد Alertmanager على المقاييس)، ولوحة قيادة السعة (Grafana). redis_exporter بواسطة Oliver006 هو المعيار الإنتاجي — يكشف كل قسم من أقسام INFO كمقاييس Prometheus بدون إعداد سوى سلسلة اتصال Redis.

# نشر redis_exporter كـ sidecar أو بتعليق توضيحي DaemonSet في Kubernetes # إعداد Prometheus scrape البسيط - job_name: redis static_configs: - targets: - redis-primary:9121 - redis-replica-0:9121 - redis-replica-1:9121 relabel_configs: - source_labels: [__address__] regex: "(.*):9121" target_label: instance # قواعد تنبيه حرجة (PrometheusRule CRD أو ملف قواعد) groups: - name: redis.production rules: # ضغط الذاكرة: >90% من maxmemory - alert: RedisHighMemoryUsage expr: redis_memory_used_bytes / redis_memory_max_bytes > 0.90 for: 5m labels: severity: critical annotations: summary: "Redis memory >90% on {{ $labels.instance }}" # ارتفاع معدل الإخلاء (>0 علامة صفراء؛ >100/s علامة حمراء) - alert: RedisHighEvictionRate expr: rate(redis_evicted_keys_total[2m]) > 100 for: 2m labels: severity: warning # الاتصالات المرفوضة (قائمة انتظار العميل ممتلئة) - alert: RedisRejectedConnections expr: increase(redis_rejected_connections_total[5m]) > 0 labels: severity: critical # تأخر النسخ المتماثلة >10 ثوان - alert: RedisReplicationLag expr: redis_replication_offset_lag > 10 for: 1m labels: severity: warning

المقاييس الرئيسية للتتبع ونطاقاتها الصحية على نطاق الإنتاج:

  • instantaneous_ops_per_sec — يتفاوت الخط الأساسي؛ تابع معدل التغيير لا القيمة المطلقة. ارتفاع مفاجئ 5x أكثر إفادة من أي حد.
  • used_memory_rss — يجب أن يبقى تحت الذاكرة الفعلية. نمو RSS بدون نمو الذاكرة المنطقية يشير إلى تجزئة.
  • معدل evicted_keys — الصفر هو الهدف لذاكرة مؤقتة بـ maxmemory مُحدَّد بشكل صحيح. الإخلاء المستمر تحت حمل مستقر يعني أن النسخة أصغر من المطلوب.
  • blocked_clients — العملاء المنتظرون على BLPOP/BRPOP/BZPOPMIN. القيمة غير الصفرية متوقعة لمستهلكي قوائم الانتظار؛ القيمة المتزايدة تشير إلى اختلال توازن المنتج/المستهلك.
  • rejected_connections — أي قيمة هي حادثة شديدة الأولوية. رفض Redis اتصال عميل لأن حد maxclients وُصل إليه.
  • rdb_last_bgsave_status / aof_last_rewrite_status — إخفاقات المثابرة صامتة بالافتراضي؛ بدون تنبيه عليها، يمكنك فقدان شبكة أمان المثابرة دون أن تلاحظ.
استخدم Grafana Dashboard ID 11835 (لوحة Redis Exporter بواسطة Perfilov) كنقطة بداية. يغطي جميع الألواح الحرجة جاهزة. في الشركات التي تشغّل Redis على نطاق ضخم (Twitter وUber وAirbnb)، تتضمن منظومة المراقبة أيضاً مئويات كمون لكل أمر عبر قسم commandstats من INFO، ولوحات المفاتيح الساخنة المبنية من بيانات LFU، ودفاتر تشغيل آلية تُشغّلها Alertmanager webhooks للإنذار المناوب مع دفتر Jupyter يعرض نافذة المقاييس ذات الصلة.

الممارسات التشغيلية التي يغطيها هذا الدرس — اختيار سياسة الإخلاء، والتخفيف من المفاتيح الساخنة، وUNLINK للمفاتيح الكبيرة، وتحليل السجل البطيء، ومنظومة مراقبة Prometheus/Alertmanager — تشكّل الكفاءة الأساسية المتوقعة من أي فريق يشغّل Redis في بيئة الإنتاج بـ SLA. أتقن هذه الأدوات قبل اللجوء إلى حلول أكثر تعقيداً مثل Redis Cluster أو طبقات الوكيل الخارجية؛ معظم حوادث Redis على نطاق واسع قابلة للوقاية بتطبيق هذه الأساسيات بشكل صحيح.