المستوى المطلوب: متوسط (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 (SET، GET، INCR، HSET...) لازم يعمل round-trip كامل: العميل يكتب على الـ socket، البايتات تعدّي الشبكة، السيرفر يستقبل، يعالج، يرد. حتى لو الـ RTT داخل نفس الـ availability zone حوالي 0.2 مللي ثانية، فاضرب في 1000 عملية، هتلاقي 200 مللي ثانية ضايعة في الانتظار وحده. Redis بياكل أقل من 1% من الزمن ده. الـ 99% الباقية انت بتدفعها للشبكة على الفاضي.
تمثيل قريب للمبتدئ: طلبات الديليفري
تخيّل إنك عايز تطلب 10 منتجات من البقالة. عندك طريقتين:
- تكلّم البقالة، تطلب اللبن، يجي الديليفري، تستلمه. تكلمه تاني، تطلب الخبز، يجي تاني... 10 رحلات منفصلة. كل رحلة 5 دقايق رايح وجاي. الإجمالي: 50 دقيقة.
- تبعت الـ 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):
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