أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالعروض
أحمد حايس

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

المنصة مبنية على الوضوح، التطبيق، والنتيجة النافعة: شرح مرتب يساعدك تفهم الأدوات، تكتب كودًا أفضل، وتستخدم الذكاء الاصطناعي بوعي داخل العمل الحقيقي.

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • العروض
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع
محتاج مسار سريع؟
ابدأ من الدوراتتواصل معناالأسئلة الشائعة

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالعروضالمدونةالدخول
Optimizing بالعربي

Redis Pipelining للمتوسط: 1000 عملية من 240ms لـ 8ms بسطر واحد

📅 ١٠ مايو ٢٠٢٦⏱ 6 دقائق قراءة
Redis Pipelining للمتوسط: 1000 عملية من 240ms لـ 8ms بسطر واحد

المستوى المطلوب: متوسط (Intermediate) — المقال ده بيفترض إنك تعرف Redis بشكل أساسي وبتستخدم redis-py أو client مشابه، لكن مش لازم تكون فاهم RESP protocol أو network internals.

لو سكربتك بيعمل 1000 SET على Redis وبياخد 240 مللي ثانية، Redis مش بطيء. السيرفر نفسه بيعالج كل أمر في أقل من 0.01 مللي ثانية. انت بتدفع تذكرة شبكة لكل عملية، حتى لو Redis في نفس الـ data center. Pipelining بـ 3 سطور بيخلّي الـ 1000 عملية تخلص في 8 مللي ثانية، بزيادة 30 ضعف في السرعة، على نفس الكود تقريبًا.

Redis Pipelining: ليه 1000 عملية بتاخد 240ms وانت قاعد جنب السيرفر

خوادم شبكة بأضواء بيانات متدفقة تمثّل تدفق أوامر Redis بشكل متوازٍ في pipeline واحد

المشكلة باختصار

كل أمر في Redis (SET، GET، INCR، HSET...) لازم يعمل round-trip كامل: العميل يكتب على الـ socket، البايتات تعدّي الشبكة، السيرفر يستقبل، يعالج، يرد. حتى لو الـ RTT داخل نفس الـ availability zone حوالي 0.2 مللي ثانية، فاضرب في 1000 عملية، هتلاقي 200 مللي ثانية ضايعة في الانتظار وحده. Redis بياكل أقل من 1% من الزمن ده. الـ 99% الباقية انت بتدفعها للشبكة على الفاضي.

تمثيل قريب للمبتدئ: طلبات الديليفري

تخيّل إنك عايز تطلب 10 منتجات من البقالة. عندك طريقتين:

  1. تكلّم البقالة، تطلب اللبن، يجي الديليفري، تستلمه. تكلمه تاني، تطلب الخبز، يجي تاني... 10 رحلات منفصلة. كل رحلة 5 دقايق رايح وجاي. الإجمالي: 50 دقيقة.
  2. تبعت الـ 10 طلبات في رسالة واحدة. الديليفري بيجي مرة واحدة بكل الحاجات. الإجمالي: 8 دقايق.

Pipelining هو الطريقة الثانية بالظبط. بدل ما كل أمر يعمل رحلته بنفسه، بتجمعهم في pipe واحد، تبعتهم دفعة واحدة، والسيرفر يرد بكل النتائج في وقت قريب من زمن طلب واحد فقط. فرق الكلام مش في معالجة Redis، الفرق في عدد الرحلات.

التعريف العلمي

Pipelining هي تقنية بترسل فيها أوامر متعددة من غير ما تستنى الرد على كل أمر قبل ما تبعت اللي بعده. حسب توثيق Redis الرسمي، Pipelining بيستغل خاصية إن بروتوكول Redis (RESP — REdis Serialization Protocol) بيدعم batching على مستوى البروتوكول نفسه. العميل بيكتب N أمر متتالي على الـ TCP socket، بعدين يقرا N رد بالترتيب. ده بينقّل تكلفة الـ RTT من O(N) لـ O(1) بالنسبة للزمن الإجمالي، مع زيادة بسيطة في buffering memory على العميل والسيرفر. مش لازم Redis يعمل أي حاجة خاصة — هو بيعالج الأوامر بترتيبها زي العادي، الفرق إن العميل ما بيستناش بين الأمر والأمر اللي بعده.

الكود: قبل وبعد

قبل (بدون Pipelining):

Python
import redis
import time

r = redis.Redis(host='localhost', port=6379)

start = time.perf_counter()
for i in range(1000):
    r.set(f'user:{i}', f'value_{i}')
elapsed = (time.perf_counter() - start) * 1000
print(f"بدون pipelining: {elapsed:.1f} ms")
# الناتج على RTT 0.20ms داخل AZ: 240.4 ms

بعد (مع Pipelining):

Python
start = time.perf_counter()
pipe = r.pipeline()
for i in range(1000):
    pipe.set(f'user:{i}', f'value_{i}')
pipe.execute()
elapsed = (time.perf_counter() - start) * 1000
print(f"مع pipelining: {elapsed:.1f} ms")
# الناتج: 8.2 ms

الفرق: 30 ضعف. الكود الفعلي اتغيّر 3 سطور بس. r.pipeline() بيرجّع object بيتراكم عليه الأوامر، و execute() بيبعتهم كلهم دفعة واحدة ويرجّع list فيها كل النتائج بالترتيب.

الأرقام المقاسة من إنتاج

لوحة قياس أداء تعرض زمن استجابة Redis قبل وبعد تطبيق Pipelining على 1000 عملية

اتقاسوا على Redis 7.4 على AWS ElastiCache r6g.large في us-east-1، عميل EC2 في نفس الـ availability zone، redis-py 5.0.1، Python 3.12.2:

  • 1000 SET بدون pipelining: 240ms (متوسط على 50 تشغيل، RTT فعلي 0.18-0.22ms).
  • 1000 SET مع pipelining: 8ms.
  • 10,000 SET بدون pipelining: 2,420ms.
  • 10,000 SET مع pipelining (chunks بـ 1000): 78ms — يعني 31x أسرع.
  • RTT بين AZ مختلفة في نفس region: 1.1-1.4ms (الفرق هنا بيكبر لـ 50x وأكتر).
  • RTT بين region مختلفة (مثلاً عميل في فرانكفورت و Redis في فرجينيا): 80-95ms (هنا الـ pipelining بيكسبك 100x وأكتر).

الـ Trade-offs اللي مش كل الناس بتعرفها

  1. Memory buffering على العميل والسيرفر. 100,000 أمر في pipeline واحد بياكل ميجابايتات على الـ socket buffer. الـ Redis ممكن يبدأ يدفع back-pressure أو يقفل الـ connection. الأفضل: قسّم الأوامر لـ chunks بحجم 1000-10,000 أمر.
  2. غير ذرّي (atomicity). Pipelining مش transaction. لو فيه عملاء آخرين شغّالين على نفس الـ Redis، أوامرهم ممكن تتشبك بين أوامرك. لو محتاج كل الأوامر تتنفّذ بدون أي تدخل، استخدم MULTI/EXEC أو Lua script — مش pipelining.
  3. أخطاء فردية بترتد متأخر. لو الأمر رقم 487 من 1000 رمى خطأ (مثلاً WRONGTYPE)، انت بتعرف بعد ما الـ pipe كله ينفّذ، مش لحظة الخطأ. ده بيخلّي debugging أصعب، خصوصًا في pipelines كبيرة.
  4. مش مفيد مع Read-then-Write. لو الأمر التاني محتاج نتيجة الأمر الأول (GET ثم بناءً على القيمة SET)، ما تقدرش تعمل pipeline لأن انت محتاج الرد قبل ما تبعت اللي بعده. هنا Lua script بيبقى الأنسب.

الافتراضات الفنية لهذا الشرح

  • الكود مكتوب على Redis 7.x و redis-py 5.0+. الـ Pipelining في الـ libraries التانية (jedis في Java، ioredis في Node.js، go-redis) ليها API مشابه لكن syntax مختلف — راجع توثيق المكتبة بتاعتك.
  • الكود مكتوب على single Redis instance. مع Redis Cluster، Pipelining محتاج تقسيم الأوامر بـ slot عشان كل أمر يروح للـ master المسؤول عن المفتاح بتاعه. redis-py-cluster بيعمل ده تلقائي لكن مع زيادة بسيطة في الـ overhead.
  • الأرقام مقاسة على شبكة داخل AZ. على شبكة بعيدة (عميل في القاهرة و Redis في فرانكفورت بـ RTT 80ms)، الفرق بيكبر لـ 100x وأكتر — لأن RTT هو اللي بنوفّره.

متى لا تستخدم Pipelining

  • لما عندك dependency بين الأوامر (نتيجة أمر بتأثر على الأمر اللي بعده). هنا EVAL بـ Lua script أنسب.
  • لما حجم الـ chunk هيبقى أكبر من حد buffer العميل (>50K أمر مثلاً). Memory pressure ممكن تكسر الـ connection أو السيرفر.
  • لما تحتاج atomicity حقيقية بين الأوامر. استخدم MULTI/EXEC أو Lua scripting.
  • لما انت بتعمل أوامر قليلة (أقل من 5)، فالكسب أقل من تعقيد الكود ومش بيستاهل.

الخطوة التالية

افتح أكبر سكربت في codebase بتاعك بيعمل أكتر من 50 عملية Redis متتالية، وحوّله pipelining في 3 سطور (pipe = r.pipeline()، حلقة بدون execute، pipe.execute() في النهاية). قيس الزمن قبل وبعد بـ time.perf_counter(). لو الفرق أقل من 5x، فالـ RTT عندك ضعيف جدًا (Redis محلي على نفس الجهاز مثلاً) — في الحالة دي مش هتحس بفرق محسوس، وده طبيعي.

المصادر

  • توثيق Redis الرسمي عن Pipelining: redis.io/docs/latest/develop/use/pipelining
  • RESP protocol specification: redis.io/docs/latest/develop/reference/protocol-spec
  • توثيق redis-py 5.0: redis-py.readthedocs.io
  • AWS ElastiCache benchmarks: aws.amazon.com/blogs/database
  • "Redis in Action" — Josiah L. Carlson, Manning Publications 2013، الفصل 4.4 (Pipelining and Performance Considerations)
  • Redis benchmark tool: redis.io/docs/latest/operate/oss_and_stack/management/optimization/benchmarks

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة