قوائم 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). تحقق من قوائم الأولوية العالية أولاً، ثم المنخفضة。
س ما الفرق بين القائمة والمجموعة؟
ج القوائم مرتبة وتسمح بالتكرار؛ المجموعات غير مرتبة بعناصر فريدة. استخدم القوائم للترتيب، المجموعات لإزالة التكرار。
📖 ملخص
- LINDEX الحصول على عنصر حسب الفهرس، LSET تعيين عنصر حسب الفهرس
- LINSERT إدراج قبل أو بعد عنصر محوري (أداء O(N))
- LREM إزالة عناصر حسب القيمة، مع التحكم في العدد والاتجاه
- RPOPLPUSH نقل العناصر، يُستخدم لقوائم الانتظار الدائرية والاحتياطية
- BRPOPLPUSH نقل محظور، يُستخدم لقوائم انتظار الرسائل الموثوقة
- تطبيقات القوائم المتقدمة: الترقيم، جهات الاتصال الأخيرة، الجدولة الدائرية
- تحسين الأداء: تجنب العمليات في المنتصف، الحد من طول القائمة، الجلب على دفعات
📝 تمارين
- عمليات الفهرس: أنشئ قائمة، استخدم LINDEX للحصول على عناصر في مواضع مختلفة، استخدم LSET لتعديل العناصر
- إدراج وحذف: استخدم LINSERT للإدراج في المنتصف، استخدم LREM لإزالة عناصر حسب القيمة
- نقل العناصر: استخدم RPOPLPUSH لتنفيذ قائمة انتظار دائرية للمهام الدائرية
- الترقيم: أنشئ قائمة بـ 20 عنصرًا ونفذ الترقيم بـ 5 عناصر لكل صفحة
الدرس التالي
في الدرس التالي، سنتعلم مجموعات Redis (الجزء 1)، والتي تغطي عمليات المجموعات الأساسية.



