قوائم Redis (الجزء 2)

يغطي هذا الدرس عمليات القوائم المتقدمة بما في ذلك الاستعلام والتعديل وإدراج العناصر.

LINDEX: الحصول على عنصر حسب الفهرس

LINDEX يسترجع عنصرًا حسب فهرسه دون إزالته.

الاستخدام الأساسي

REDIS
LPUSH mylist "one" "two" "three" "four" "five"

# الحصول على العنصر الأول (الفهرس 0)
LINDEX mylist 0
"five"

# الحصول على العنصر الثاني
LINDEX mylist 1
"four"

# الحصول على العنصر الأخير
LINDEX mylist -1
"one"

# الحصول على العنصر قبل الأخير
LINDEX mylist -2
"two"

الفهرس خارج النطاق

REDIS
# يُرجع nil عندما يكون الفهرس خارج النطاق
LINDEX mylist 10
(nil)

LINDEX mylist -10
(nil)
💡 الأداء: LINDEX له تعقيد زمني O(N)، حيث N هي قيمة الفهرس المطلقة. الوصول إلى الرأس والذيل سريع؛ الوصول إلى الوسط بطيء.

LSET: تعيين عنصر حسب الفهرس

LSET يعدّل العنصر في فهرس محدد.

الاستخدام الأساسي

REDIS
LRANGE mylist 0 -1
1) "five"
2) "four"
3) "three"
4) "two"
5) "one"

# تعديل العنصر الأول
LSET mylist 0 "FIRST"
OK

# تعديل العنصر الأخير
LSET mylist -1 "LAST"
OK

LRANGE mylist 0 -1
1) "FIRST"
2) "four"
3) "three"
4) "two"
5) "LAST"

الفهرس خارج النطاق

REDIS
LSET mylist 10 "value"
(error) ERR index out of range
⚠️ ملاحظة: LSET يمكنه فقط تعديل الفهارس الموجودة — لا يمكنه إدراج عناصر جديدة.

LINSERT: إدراج قبل أو بعد عنصر محوري

LINSERT يدرج عنصرًا جديدًا قبل أو بعد عنصر محوري محدد.

LINSERT BEFORE: إدراج قبل

REDIS
LRANGE mylist 0 -1
1) "FIRST"
2) "four"
3) "three"
4) "two"
5) "LAST"

# إدراج "BEFORE_THREE" قبل "three"
LINSERT mylist BEFORE "three" "BEFORE_THREE"
(integer) 6  # يُرجع طول القائمة

LRANGE mylist 0 -1
1) "FIRST"
2) "four"
3) "BEFORE_THREE"
4) "three"
5) "two"
6) "LAST"

LINSERT AFTER: إدراج بعد

REDIS
# إدراج "AFTER_THREE" بعد "three"
LINSERT mylist AFTER "three" "AFTER_THREE"
(integer) 7

LRANGE mylist 0 -1
1) "FIRST"
2) "four"
3) "BEFORE_THREE"
4) "three"
5) "AFTER_THREE"
6) "two"
7) "LAST"

العنصر المحوري غير موجود

REDIS
# العنصر المحوري المحدد غير موجود
LINSERT mylist BEFORE "notexist" "value"
(integer) -1  # يُرجع -1 عندما لا يتم العثور على العنصر المحوري
⚠️ تحذير أداء: LINSERT له تعقيد زمني O(N) — يحتاج إلى اجتياز القائمة للعثور على العنصر المحوري. هذا بطيء في القوائم الطويلة جدًا.

LREM: إزالة العناصر حسب القيمة

LREM يزيل العناصر من القائمة التي تطابق قيمة محددة.

الاستخدام الأساسي

REDIS
LPUSH mylist "a" "b" "a" "c" "a" "d"

LRANGE mylist 0 -1
1) "d"
2) "a"
3) "c"
4) "a"
5) "b"
6) "a"

# إزالة تكرارين من "a" من الرأس
LREM mylist 2 "a"
(integer) 2  # تم حذف 2

LRANGE mylist 0 -1
1) "d"
2) "c"
3) "b"
4) "a"

مرجع المعاملات

المعامل الوصف
count > 0 إزالة count تكرارًا من الرأس
count < 0 إزالة abs(count) تكرارًا من الذيل
count = 0 إزالة جميع العناصر المطابقة

إزالة من الذيل

REDIS
LPUSH mylist "a" "b" "a" "c" "a"

# إزالة تكرار واحد من "a" من الذيل
LREM mylist -1 "a"
(integer) 1

إزالة جميع العناصر المطابقة

REDIS
LPUSH mylist "a" "b" "a" "c" "a"

# إزالة جميع "a"
LREM mylist 0 "a"
(integer) 3

LRANGE mylist 0 -1
1) "c"
2) "b"

RPOPLPUSH: نقل العناصر

RPOPLPUSH يخرج العنصر الخلفي من قائمة ويدفعه إلى رأس قائمة أخرى.

الاستخدام الأساسي

REDIS
# القائمة المصدر
LPUSH source "one" "two" "three"

# القائمة الوجهة
LPUSH destination "a" "b"

# نقل ذيل المصدر إلى رأس الوجهة
RPOPLPUSH source destination
"one"  # يُرجع العنصر المنقول

LRANGE source 0 -1
1) "three"
2) "two"

LRANGE destination 0 -1
1) "one"
2) "a"
3) "b"

حالة الاستخدام: قائمة انتظار دائرية

REDIS
# نقل مهمة من الذيل إلى الرأس للمعالجة الدائرية
RPOPLPUSH queue:task queue:task

حالة الاستخدام: قائمة انتظار احتياطية

REDIS
# نقل المهمة إلى قائمة الانتظار الاحتياطية أثناء المعالجة
RPOPLPUSH queue:task queue:backup

# إذا فشلت المعالجة، يمكن استرداد المهمة من قائمة الانتظار الاحتياطية

BRPOPLPUSH: نقل محظور

BRPOPLPUSH هو الإصدار المحظور من RPOPLPUSH.

REDIS
# إذا كانت القائمة المصدر فارغة، انتظر حتى 10 ثوانٍ
BRPOPLPUSH source destination 10
💡 حالة الاستخدام: BRPOPLPUSH يُستخدم عادةً لقوائم انتظار الرسائل الموثوقة لضمان عدم فقدان الرسائل.

LMOVE: نقل العناصر (Redis 6.2+)

LMOVE هو إصدار معمم من RPOPLPUSH يسمح لك بتحديد موضعي المصدر والوجهة.

REDIS
# إخراج من يمين المصدر، دفع إلى يسار الوجهة
LMOVE source destination RIGHT LEFT

# إخراج من يسار المصدر، دفع إلى يمين الوجهة
LMOVE source destination LEFT RIGHT

تطبيقات القوائم المتقدمة

حالة الاستخدام 1: الترقيم

REDIS
# قائمة المقالات
LPUSH articles "article:1" "article:2" ... "article:100"

# الصفحة 1 (10 عناصر لكل صفحة)
LRANGE articles 0 9

# الصفحة 2
LRANGE articles 10 19

# الصفحة 3
LRANGE articles 20 29

حالة الاستخدام 2: جهات الاتصال الأخيرة

REDIS
# إضافة جهة اتصال إلى القائمة الأخيرة
LPUSH contacts:user:1 "user:2"

# إذا كانت موجودة بالفعل، أزل أولاً ثم أضف (نقل إلى المقدمة)
LREM contacts:user:1 0 "user:2"
LPUSH contacts:user:1 "user:2"

# الحصول على آخر 10 جهات اتصال
LRANGE contacts:user:1 0 9

حالة الاستخدام 3: جدولة دائرية

REDIS
# قائمة المهام
LPUSH tasks "task1" "task2" "task3"

# دائري: إخراج مهمة، معالجتها، ثم إعادتها إلى الذيل
RPOPLPUSH tasks tasks
"task1"  # معالجة task1

# الاستعلام التالي سيحصل على task2

حالة الاستخدام 4: سجل محدود

REDIS
# إضافة إدخال سجل
LPUSH log:app "log message"

# الاحتفاظ بأحدث 1000 إدخال سجل
LTRIM log:app 0 999

# عرض السجلات الأخيرة
LRANGE log:app 0 99

تحسين أداء القوائم

1. تجنب العمليات في المنتصف

REDIS
# ❌ أداء ضعيف: إدراج أو حذف في المنتصف
LINSERT mylist BEFORE "middle" "new"
LREM mylist 1 "middle"

# ✅ أداء جيد: العمل فقط عند الرأس والذيل
LPUSH mylist "new"
LPOP mylist

2. تجنب القوائم الكبيرة

REDIS
# ❌ قائمة كبيرة جدًا
LLEN mylist
(integer) 1000000

# ✅ استخدم LTRIM للحد من الطول
LTRIM mylist 0 9999

3. الجلب على دفعات باستخدام LRANGE

REDIS
# ❌ جلب نطاق ضخم مرة واحدة
LRANGE mylist 0 -1

# ✅ الجلب على دفعات
LRANGE mylist 0 99
LRANGE mylist 100 199

4. اختيار هيكل البيانات المناسب

المتطلب هيكل البيانات الموصى به
قائمة انتظار/مكدس List
أحدث N عنصر List + LTRIM
ترقيم List + LRANGE
قائمة بدون تكرار Set أو Sorted Set
فرز مرجح Sorted Set

قيود القوائم

1. بطء البحث عن العناصر

REDIS
# العثور على قيمة محددة يتطلب اجتياز القائمة بأكملها
LINDEX mylist 500000  # عملية O(N)، بطيئة جدًا

2. بطء العمليات في المنتصف

REDIS
# الإدراج أو الحذف في المنتصف يتطلب نقل العناصر
LINSERT mylist BEFORE "middle" "new"  # O(N)

3. لا يمكن العثور على الفهرس حسب القيمة

لا يوفر Redis أمرًا للعثور على فهرس قيمة حسب محتواها. يجب تنفيذ ذلك على مستوى التطبيق.

❓ أسئلة شائعة

س ما الفرق بين LINDEX و LRANGE؟
ج LINDEX يُرجع عنصرًا واحدًا؛ LRANGE يُرجع قائمة من العناصر. LINDEX mylist 0 مكافئ لـ LRANGE mylist 0 0
س كيف أدرج عنصرًا في منتصف القائمة؟
ج استخدم LINSERT، لكن الأداء ضعيف. إذا كنت تحتاج إدراجًا متكررًا في المنتصف، فكر في استخدام هيكل بيانات مختلف。
س هل RPOPLPUSH ذري؟
ج نعم. RPOPLPUSH ذري — إما أن تنجح بالكامل أو تفشل بالكامل。
س كيف أنفذ قائمة انتظار ذات أولوية؟
ج استخدم قوائم متعددة (مثل queue:high، queue:low). تحقق من قوائم الأولوية العالية أولاً، ثم المنخفضة。
س ما الفرق بين القائمة والمجموعة؟
ج القوائم مرتبة وتسمح بالتكرار؛ المجموعات غير مرتبة بعناصر فريدة. استخدم القوائم للترتيب، المجموعات لإزالة التكرار。

📖 ملخص

📝 تمارين

  1. عمليات الفهرس: أنشئ قائمة، استخدم LINDEX للحصول على عناصر في مواضع مختلفة، استخدم LSET لتعديل العناصر
  2. إدراج وحذف: استخدم LINSERT للإدراج في المنتصف، استخدم LREM لإزالة عناصر حسب القيمة
  3. نقل العناصر: استخدم RPOPLPUSH لتنفيذ قائمة انتظار دائرية للمهام الدائرية
  4. الترقيم: أنشئ قائمة بـ 20 عنصرًا ونفذ الترقيم بـ 5 عناصر لكل صفحة

الدرس التالي

في الدرس التالي، سنتعلم مجموعات Redis (الجزء 1)، والتي تغطي عمليات المجموعات الأساسية.

100%