Redis Pipelining بالعربي: 1000 طلب في رحلة شبكة واحدة
مستوى المقال: متوسط — يفترض إنك مستخدم Redis قبل كده وعارف أوامر زي SET / GET / MGET، لكن مش لازم تكون عملت optimization جدي قبل كده.
لو تطبيقك بيعمل 1000 طلب SET على Redis في loop وبياخد 4 ثواني، Redis نفسه مش هو المشكلة. كل أمر بيستنى رد كامل عبر الشبكة قبل ما اللي بعده يتبعت. Pipelining بينزّل الزمن ده لـ 80ms على نفس السيرفر بدون تغيير كود الـ business، وده اللي هنفصّله بالظبط.
المشكلة باختصار
Redis سريع جدًا داخليًا. الأمر الواحد بياخد جزء من الميكروثانية على السيرفر نفسه. لكن بين كل أمر وأمر فيه latency شبكة. لو السيرفر والتطبيق على نفس الـ data center، الـ RTT (round-trip time) حوالي 0.4ms. لو على Cloud مختلفة أو بين منطقتين، ممكن يوصل 4ms أو أكتر.
اعمل الحسبة: 1000 أمر × 0.5ms انتظار = 500ms ضائعين على الشبكة، حتى لو Redis نفسه عمل الـ 1000 عملية في 5ms. المشكلة كلها في فكرة "إبعت أمر، استنى الرد، بعدين إبعت اللي بعده".
المثال البسيط: زيارة السوبر ماركت
تخيّل عايز تشتري 100 منتج من سوبر ماركت. عندك طريقتين:
- الطريقة الغلط: تروح، تشتري منتج واحد، تدفع، تخرج، ترجع، تشتري التاني، تدفع، تخرج. 100 مرة. كل مرة بتستنى دور الكاشير وبتمشي على القدم. الزمن الإجمالي = (دقيقة للطابور + دقيقة للمشي) × 100 = 200 دقيقة.
- الطريقة الصح: تحط الـ 100 منتج في عربية واحدة، تروح للكاشير مرة واحدة، يحسبهم كلهم، تخرج. زمن الطابور حصل مرة واحدة بدل 100 مرة. الإجمالي = 5 دقائق.
Pipelining في Redis هو نفس فكرة العربية الواحدة. بدل ما الـ client يبعت أمر ويستنى الرد قبل التالي، بيلمّ مجموعة أوامر مع بعض، يبعتهم في طلب شبكة واحد، ويستقبل الردود كلها سوا.
التعريف العلمي الدقيق
Pipelining هو إرسال مجموعة أوامر متتالية على نفس الـ TCP connection بدون انتظار الرد على كل أمر قبل إرسال اللي بعده. الأوامر بتتراص في buffer العميل، تتبعت دفعة واحدة، والسيرفر بيرجّع كل الردود في buffer واحد. التقنية موجودة في Redis من إصدار 1.0 وموثّقة في "Using pipelining to speedup Redis queries" على الـ docs الرسمية.
فيه فرق مهم بين 3 مفاهيم بيتلخبطوا في بعض، ركّز:
- Pipelining: توفير latency الشبكة فقط. الأوامر مش atomic — ممكن يدخل بينهم أوامر من client تاني.
- MULTI/EXEC (Transactions): الأوامر atomic لكن ما بتقللش latency لو الـ client بيستنى رد قبل ما يبعت EXEC.
- Lua Scripts (EVAL): الأوامر atomic + بتنفّذ كلها على السيرفر، بدون أي round-trip بين كل أمر.
الـ Pipelining بيتعامل مع أعراض الـ network round-trip بس. لو محتاج atomicity حقيقية، خد قرار تاني.
الحل العملي بكود Python شغّال
الكود ده مكتوب على redis-py 5.0.1 وRedis 7.2. شغّل الـ server محليًا أو على Docker قبل ما تجرّب: