Redis: خط أنابيب Redis

خط الأنابيب يسمح بإرسال الأوامر دفعات، مما يقلل من جولات الشبكة ويحسن الأداء بشكل كبير.

1. ما هو خط الأنابيب؟

(1) النهج التقليدي

كل أمر يتطلب جولة شبكة واحدة:

BASH
العميل                   خادم Redis
  |                         |
  |----- SET key1 -------->|
  |<----- OK --------------|
  |                         |
  |----- SET key2 -------->|
  |<----- OK --------------|
  |                         |
  |----- SET key3 -------->|
  |<----- OK --------------|

3 أوامر = 6 عمليات نقل شبكة (3 طلبات + 3 استجابات)

(2) نهج خط الأنابيب

أوامر متعددة ترسل مرة واحدة:

BASH
العميل                   خادم Redis
  |                         |
  |----- SET key1 -------->|
  |----- SET key2 -------->|
  |----- SET key3 -------->|
  |                         |
  |<----- OK --------------|
  |<----- OK --------------|
  |<----- OK --------------|

3 أوامر = 2 عمليتي نقل شبكة (1 طلب دفعة + 1 استجابة دفعة)

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


2. كيف يعمل خط الأنابيب؟

(1) تأثير زمن انتقال الشبكة

بافتراض جولة شبكة 1ms:

BASH
النهج التقليدي:
- 100 أمر = 100 جولة = 100ms زمن انتقال شبكة
- بالإضافة إلى وقت معالجة Redis (افترض 0.1ms/أمر) = 10ms
- الوقت الإجمالي = 100ms + 10ms = 110ms

خط الأنابيب:
- 100 أمر = 1 جولة = 1ms زمن انتقال شبكة
- بالإضافة إلى وقت معالجة Redis = 10ms
- الوقت الإجمالي = 1ms + 10ms = 11ms

تحسين الأداء: 110ms → 11ms، زيادة 10 أضعاف!

(2) خصائص خط الأنابيب

⚠️ ملاحظة: خط الأنابيب ليس معاملة ولا يضمن الذرية. استخدم MULTI/EXEC للذرية.


3. استخدام خط الأنابيب

(1) وضع خط أنابيب redis-cli

BASH
# استخدام وضع خط أنابيب redis-cli
echo -e "SET key1 value1\nSET key2 value2\nSET key3 value3" | redis-cli

# أو القراءة من ملف
cat commands.txt | redis-cli

(2) خط أنابيب Python

PYTHON
import redis

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

# إنشاء خط أنابيب
pipe = r.pipeline()

# إضافة أوامر إلى خط الأنابيب
pipe.set('key1', 'value1')
pipe.set('key2', 'value2')
pipe.set('key3', 'value3')

# تنفيذ خط الأنابيب
results = pipe.execute()
print(results)  # [True, True, True]

(3) خط أنابيب Java (Jedis)

JAVA
Jedis jedis = new Jedis("localhost", 6379);

Pipeline pipeline = jedis.pipelined();

pipeline.set("key1", "value1");
pipeline.set("key2", "value2");
pipeline.set("key3", "value3");

List<Object> results = pipeline.syncAndReturnAll();

(4) خط أنابيب Node.js (ioredis)

JAVASCRIPT
const Redis = require('ioredis');
const redis = new Redis();

const pipeline = redis.pipeline();

pipeline.set('key1', 'value1');
pipeline.set('key2', 'value2');
pipeline.set('key3', 'value3');

const results = await pipeline.exec();

4. خط الأنابيب مقابل المعاملات

(1) خط الأنابيب + المعاملات

يمكن دمج خط الأنابيب مع المعاملات:

PYTHON
import redis

r = redis.Redis()

# إنشاء خط أنابيب مع تمكين المعاملة
pipe = r.pipeline(transaction=True)

pipe.set('key1', 'value1')
pipe.set('key2', 'value2')
pipe.incr('counter')

# تنفيذ المعاملة
results = pipe.execute()

(2) خط الأنابيب مقابل المعاملة

الجانب خط الأنابيب المعاملة
الذرية ❌ لا ✅ نعم
العزل ❌ لا ✅ نعم
تحسين الأداء ✅ كبير ✅ كبير
إرسال دفعات ✅ نعم ✅ نعم
حالة الاستخدام عمليات دفعات، أداء عمليات دفعات تحتاج ذرية
💡 الاختيار: استخدم خط الأنابيب عندما لا تكون الذرية مطلوبة. استخدم المعاملات أو خط الأنابيب+المعاملة عندما تكون الذرية مطلوبة.


5. حالات استخدام خط الأنابيب

(1) حالة الاستخدام 1: تعيين دفعات

PYTHON
import redis

r = redis.Redis()
pipe = r.pipeline()

# تعيين 1000 مفتاح دفعة واحدة
for i in range(1000):
    pipe.set(f'key:{i}', f'value:{i}')

results = pipe.execute()
print(f'Set {len(results)} keys')

(2) حالة الاستخدام 2: الحصول على دفعات

PYTHON
import redis

r = redis.Redis()
pipe = r.pipeline()

# الحصول على 1000 مفتاح دفعة واحدة
keys = [f'key:{i}' for i in range(1000)]
for key in keys:
    pipe.get(key)

values = pipe.execute()
print(f'Got {len(values)} values')

(3) حالة الاستخدام 3: حذف دفعات

PYTHON
import redis

r = redis.Redis()
pipe = r.pipeline()

# حذف دفعات
keys = ['key1', 'key2', 'key3', 'key4', 'key5']
for key in keys:
    pipe.delete(key)

results = pipe.execute()
print(f'Deleted {sum(results)} keys')

(4) حالة الاستخدام 4: استيراد بيانات

PYTHON
import redis
import json

r = redis.Redis()
pipe = r.pipeline()

# استيراد بيانات من ملف JSON
with open('data.json', 'r') as f:
    data = json.load(f)

    for item in data:
        key = f"user:{item['id']}"
        value = json.dumps(item)
        pipe.set(key, value)

pipe.execute()
print('Import complete')

(5) حالة الاستخدام 5: تحديث عدادات دفعات

PYTHON
import redis

r = redis.Redis()
pipe = r.pipeline()

# تحديث عدادات دفعات
counters = {
    'article:1:views': 10,
    'article:2:views': 20,
    'article:3:views': 30,
}

for key, increment in counters.items():
    pipe.incrby(key, increment)

pipe.execute()
print('Counters updated')

6. مقارنة أداء خط الأنابيب

(1) كود الاختبار

PYTHON
import redis
import time

r = redis.Redis()

# بيانات الاختبار
n = 10000

# الطريقة 1: النهج العادي
start = time.time()
for i in range(n):
    r.set(f'key:{i}', f'value:{i}')
end = time.time()
print(f'Regular: {end - start:.2f} seconds')

# الطريقة 2: خط الأنابيب
start = time.time()
pipe = r.pipeline()
for i in range(n):
    pipe.set(f'key:{i}', f'value:{i}')
pipe.execute()
end = time.time()
print(f'Pipeline: {end - start:.2f} seconds')

(2) نتائج نموذجية

BASH
Regular: 5.23 seconds
Pipeline: 0.51 seconds

تحسين الأداء: حوالي 10 أضعاف

7. أفضل ممارسات خط الأنابيب

(1) تعيين حجم دفعة معقول

PYTHON
# ❌ إرسال عدد كبير جدًا من الأوامر مرة واحدة
pipe = r.pipeline()
for i in range(100000):
    pipe.set(f'key:{i}', f'value:{i}')
pipe.execute()  # قد يستخدم الكثير من الذاكرة

# ✅ التنفيذ على دفعات
batch_size = 1000
for batch in range(0, 100000, batch_size):
    pipe = r.pipeline()
    for i in range(batch, batch + batch_size):
        pipe.set(f'key:{i}', f'value:{i}')
    pipe.execute()

(2) معالجة الأخطاء

PYTHON
import redis

r = redis.Redis()
pipe = r.pipeline()

pipe.set('key1', 'value1')
pipe.incr('key1')  # سيفشل (key1 ليس رقمًا)
pipe.set('key2', 'value2')

try:
    results = pipe.execute()
    for i, result in enumerate(results):
        if isinstance(result, Exception):
            print(f'Command {i} failed: {result}')
except Exception as e:
    print(f'Pipeline execution failed: {e}')

(3) استخدام المعاملات للذرية

PYTHON
import redis

r = redis.Redis()

# استخدم المعاملة عند الحاجة للذرية
pipe = r.pipeline(transaction=True)

pipe.set('account:a', '100')
pipe.set('account:b', '50')

try:
    results = pipe.execute()
    print('Transaction executed successfully')
except Exception as e:
    print(f'Transaction failed: {e}')

(4) مراقبة أداء خط الأنابيب

PYTHON
import redis
import time

r = redis.Redis()

start = time.time()
pipe = r.pipeline()

for i in range(10000):
    pipe.set(f'key:{i}', f'value:{i}')

results = pipe.execute()
end = time.time()

print(f'Execution time: {end - start:.2f} seconds')
print(f'QPS: {10000 / (end - start):.0f}')

8. قيود خط الأنابيب

(1) استخدام الذاكرة

PYTHON
# خط الأنابيب يخزّن جميع الأوامر والنتائج مؤقتًا
# دفعة كبيرة جدًا تستخدم الكثير من الذاكرة

# الحل: تنفيذ على دفعات

(2) غير ذري

PYTHON
# الأوامر في خط الأنابيب يمكن مقاطعتها من قبل عملاء آخرين
# استخدم المعاملات عندما تكون الذرية مطلوبة

(3) لا يمكن استخدام النتائج الوسيطة

PYTHON
# لا يمكن استخدام نتيجة أمر سابق في خط الأنابيب

# ❌ مثال غير صحيح
pipe = r.pipeline()
pipe.incr('counter')
# لا يمكن الحصول على نتيجة incr للأوامر اللاحقة
pipe.set('result', ???)  # لا يمكن استخدام نتيجة incr
pipe.execute()

# ✅ الحل: استخدم برامج Lua النصية

▶ مثال

عمليات الدفعات عبر خط الأنابيب:

BASH
# الطريقة العادية: رحلة لكل أمر
127.0.0.1:6379> SET key1 value1
OK
127.0.0.1:6379> SET key2 value2
OK

# خط الأنابيب: إرسال أوامر متعددة دفعة واحدة
echo -e "SET key1 value1\nSET key2 value2\nGET key1" | redis-cli --pipe
# جميع العمليات في رحلة واحدة

❓ أسئلة شائعة

س ما الفرق بين خط الأنابيب والمعاملة؟
ج خط الأنابيب يقلل جولات الشبكة لكنه لا يضمن الذرية. المعاملات تضمن الذرية وتقلل أيضًا جولات الشبكة.
س كم تحسين في الأداء يمكن أن يوفره خط الأنابيب؟
ج يعتمد على زمن انتقال الشبكة. زمن انتقال أعلى يعني تحسينًا أكبر. عادة 5-10 أضعاف.
س ما حجم الدفعة الذي يجب استخدامه؟
ج يُوصى بـ 100-1000 أمر لكل دفعة. كبير جدًا يستخدم الكثير من الذاكرة؛ صغير جدًا يعطي تحسينًا ضئيلًا.
س ماذا يحدث إذا فشل أمر في خط الأنابيب؟
ج فشل أمر واحد لا يؤثر على الأوامر الأخرى. تحقق من النتائج المرجعة بحثًا عن أخطاء.
س متى يجب استخدام خط الأنابيب؟
ج عمليات دفعات، استيراد/تصدير بيانات، وأي سيناريو يحتاج تحسين الأداء.

📖 ملخص

📝 تمارين

  1. خط أنابيب أساسي: استخدم خط الأنابيب لتعيين 100 مفتاح دفعة واحدة، قارن الأداء مع النهج العادي
  2. عمليات دفعات: نفذ الحصول على دفعات وحذف دفعات باستخدام خط الأنابيب
  3. مقارنة أداء: اختبر الأداء بأحجام دفعات مختلفة (10، 100، 1000)
  4. خط أنابيب + معاملة: نفذ عمليات دفعات ذرية باستخدام خط الأنابيب مع المعاملات

9. الدرس التالي

في الدرس التالي، سنتعلم Python مع Redis، والذي يغطي عمليات Python مع Redis.

Web-Tutorial.com

فريق Web-Tutorial التقني

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

100%