سلاسل Redis النصية
السلاسل هي نوع البيانات الأساسي والأكثر تنوعاً في Redis. على الرغم من الاسم، فإن سلاسل Redis آمنة ثنائياً ويمكن أن تحتوي على أي نوع من البيانات - نص، أرقام، كائنات متسلسلة، أو حتى صور (حتى 512 ميجابايت).
آمن ثنائياً: يمكن أن تحتوي سلاسل Redis على أي تسلسل من البايتات، بما في ذلك البايتات الفارغة. هذا يجعلها مناسبة لتخزين البيانات الثنائية مثل الصور والملفات المضغوطة أو الكائنات المتسلسلة.
أوامر SET و GET
العمليات الأساسية للعمل مع السلاسل هي SET (الكتابة) و GET (القراءة):
// SET و GET الأساسية\n127.0.0.1:6379> SET name "Edrees Salih"\nOK\n\n127.0.0.1:6379> GET name\n"Edrees Salih"\n\n// SET يستبدل القيم الموجودة\n127.0.0.1:6379> SET name "Ahmed Ali"\nOK\n\n127.0.0.1:6379> GET name\n"Ahmed Ali"\n\n// GET لمفتاح غير موجود يرجع nil\n127.0.0.1:6379> GET nonexistent\n(nil)
استخدام SET و GET في Laravel
use Illuminate\Support\Facades\Redis;\n\n// تعيين قيمة\nRedis::set('user:name', 'Edrees Salih');\n\n// الحصول على قيمة\n$name = Redis::get('user:name');\necho $name; // "Edrees Salih"\n\n// التحقق من وجود المفتاح\nif (Redis::exists('user:name')) {\n echo "المفتاح موجود!";\n}
MSET و MGET - مفاتيح متعددة
تعيين أو الحصول على مفاتيح متعددة في عملية ذرية واحدة - أسرع بكثير من استدعاءات SET/GET متعددة:
// MSET - تعيين مفاتيح متعددة في وقت واحد\n127.0.0.1:6379> MSET user:1:name "Edrees" user:1:email "edrees@example.com" user:1:role "admin"\nOK\n\n// MGET - الحصول على مفاتيح متعددة في وقت واحد\n127.0.0.1:6379> MGET user:1:name user:1:email user:1:role\n1) "Edrees"\n2) "edrees@example.com"\n3) "admin"
// مثال Laravel\nRedis::mset([\n 'product:100:name' => 'Laptop',\n 'product:100:price' => '999.99',\n 'product:100:stock' => '50'\n]);\n\n$values = Redis::mget(['product:100:name', 'product:100:price', 'product:100:stock']);\n// يرجع: ["Laptop", "999.99", "50"]
الأداء: MSET/MGET تقلل من رحلات الشبكة ذهاباً وإياباً. تعيين 100 مفتاح باستخدام MSET أسرع بحوالي 100 مرة من 100 أمر SET فردي!
INCR و DECR - العدادات الذرية
يوفر Redis عمليات زيادة ونقصان ذرية - مثالية للعدادات والمشاهدات والإعجابات وتحديد المعدل:
// INCR - الزيادة بمقدار 1\n127.0.0.1:6379> SET page_views 100\nOK\n\n127.0.0.1:6379> INCR page_views\n(integer) 101\n\n127.0.0.1:6379> INCR page_views\n(integer) 102\n\n// DECR - النقصان بمقدار 1\n127.0.0.1:6379> DECR page_views\n(integer) 101\n\n// INCRBY - الزيادة بمقدار محدد\n127.0.0.1:6379> INCRBY page_views 10\n(integer) 111\n\n// DECRBY - النقصان بمقدار محدد\n127.0.0.1:6379> DECRBY page_views 5\n(integer) 106\n\n// INCR على مفتاح غير موجود يبدأ من 0\n127.0.0.1:6379> INCR new_counter\n(integer) 1
حالات استخدام عملية للعدادات
// تتبع مشاهدات الصفحة\nRedis::incr('page:home:views');\n\n// تتبع طلبات API (تحديد المعدل)\n$key = 'rate_limit:user:' . $userId . ':' . date('Y-m-d-H-i');\nRedis::incr($key);\nRedis::expire($key, 60); // تنتهي الصلاحية بعد 60 ثانية\n\nif (Redis::get($key) > 100) {\n throw new Exception('تجاوز حد المعدل');\n}\n\n// تتبع مخزون المنتج\nRedis::decrby('product:' . $productId . 'stock', $quantity);\n\n// تتبع الإعجابات/الأصوات\nRedis::incr('post:' . $postId . ':likes');
الذرية: عمليات INCR/DECR ذرية - لا توجد شروط سباق حتى مع الطلبات المتزامنة. هذا يجعل Redis مثالياً للعدادات في الأنظمة الموزعة.
APPEND و STRLEN
بناء السلاسل تدريجياً والتحقق من طولها:
// APPEND - إضافة إلى نهاية السلسلة\n127.0.0.1:6379> SET message "Hello"\nOK\n\n127.0.0.1:6379> APPEND message " World"\n(integer) 11\n\n127.0.0.1:6379> GET message\n"Hello World"\n\n// STRLEN - الحصول على طول السلسلة\n127.0.0.1:6379> STRLEN message\n(integer) 11\n\n// APPEND لمفتاح غير موجود ينشئه\n127.0.0.1:6379> APPEND newkey "value"\n(integer) 5
// مثال Laravel - بناء السجلات\n$logKey = 'app:log:' . date('Y-m-d');\nRedis::append($logKey, date('H:i:s') . ' - User logged in\n');\nRedis::append($logKey, date('H:i:s') . ' - Order created\n');\n\n$logSize = Redis::strlen($logKey);\necho "حجم السجل: " . $logSize . " بايت";
TTL و EXPIRE - انتهاء صلاحية المفتاح
تعيين انتهاء صلاحية تلقائي على المفاتيح - ضروري للتخزين المؤقت والبيانات المؤقتة:
// EXPIRE - تعيين انتهاء الصلاحية بالثواني\n127.0.0.1:6379> SET session:abc123 "user_data"\nOK\n\n127.0.0.1:6379> EXPIRE session:abc123 3600\n(integer) 1 // 1 = نجاح\n\n// TTL - التحقق من الوقت المتبقي (الثواني المتبقية)\n127.0.0.1:6379> TTL session:abc123\n(integer) 3595\n\n// EXPIREAT - انتهاء الصلاحية في طابع زمني Unix محدد\n127.0.0.1:6379> EXPIREAT session:abc123 1735689600\n(integer) 1\n\n// PERSIST - إزالة انتهاء الصلاحية\n127.0.0.1:6379> PERSIST session:abc123\n(integer) 1\n\n// TTL يرجع -1 للمفاتيح بدون انتهاء صلاحية\n127.0.0.1:6379> TTL session:abc123\n(integer) -1\n\n// TTL يرجع -2 للمفاتيح غير الموجودة\n127.0.0.1:6379> TTL nonexistent\n(integer) -2
// مثال Laravel\n// التعيين مع انتهاء الصلاحية (بالثواني)\nRedis::setex('cache:product:100', 3600, $productData);\n\n// أو استخدام set مع expire\nRedis::set('cache:product:100', $productData);\nRedis::expire('cache:product:100', 3600);\n\n// التحقق من TTL\n$ttl = Redis::ttl('cache:product:100');\necho "تنتهي الصلاحية في {$ttl} ثانية";\n\n// إزالة انتهاء الصلاحية\nRedis::persist('cache:product:100');
SETEX - التعيين مع انتهاء الصلاحية
دمج SET و EXPIRE في عملية ذرية واحدة:
// SETEX - التعيين مع انتهاء الصلاحية (بالثواني)\n127.0.0.1:6379> SETEX cache:homepage 300 "<html>...</html>"\nOK\n\n// يعادل:\n127.0.0.1:6379> SET cache:homepage "<html>...</html>"\n127.0.0.1:6379> EXPIRE cache:homepage 300\n\n// PSETEX - التعيين مع انتهاء الصلاحية بالميلي ثانية\n127.0.0.1:6379> PSETEX cache:api_response 5000 '{"status":"ok"}'\nOK
أفضل ممارسة: استخدم دائماً SETEX بدلاً من SET + EXPIRE منفصلة. إنها ذرية (لا شرط سباق) وأسرع (أمر واحد بدلاً من اثنين).
أعلام NX و XX - SET الشرطي
التحكم في وقت نجاح SET بناءً على وجود المفتاح:
// SETNX - التعيين إذا لم يكن موجوداً (علم NX)\n127.0.0.1:6379> SETNX lock:process "locked"\n(integer) 1 // نجاح - المفتاح لم يكن موجوداً\n\n127.0.0.1:6379> SETNX lock:process "locked"\n(integer) 0 // فشل - المفتاح موجود بالفعل\n\n// SET مع علم NX (صيغة حديثة)\n127.0.0.1:6379> SET lock:process "locked" NX\nOK\n\n127.0.0.1:6379> SET lock:process "locked" NX\n(nil) // فشل\n\n// SET مع علم XX - التعيين فقط إذا كان المفتاح موجوداً\n127.0.0.1:6379> SET existing_key "new_value" XX\nOK // نجاح إذا كان المفتاح موجوداً\n\n127.0.0.1:6379> SET nonexistent_key "value" XX\n(nil) // فشل - المفتاح غير موجود\n\n// دمج NX مع EX لقفل ذري مع انتهاء الصلاحية\n127.0.0.1:6379> SET lock:process "locked" NX EX 30\nOK // تم الحصول على القفل لمدة 30 ثانية
نمط القفل الموزع
// الحصول على قفل مع انتهاء صلاحية تلقائي\n$lockKey = 'lock:import_products';\n$lockValue = uniqid(); // قيمة فريدة لتحديد حامل القفل هذا\n\n$acquired = Redis::set($lockKey, $lockValue, 'NX', 'EX', 300);\n\nif ($acquired) {\n try {\n // قسم حرج - يمكن لعملية واحدة فقط أن تكون هنا\n $this->importProducts();\n } finally {\n // تحرير القفل (فقط إذا كنا لا نزال نمتلكه)\n if (Redis::get($lockKey) === $lockValue) {\n Redis::del($lockKey);\n }\n }\n} else {\n throw new Exception('تعذر الحصول على القفل - عملية أخرى قيد التشغيل');\n}
ترميز السلسلة وتحسين الذاكرة
يقوم Redis تلقائياً بتحسين تخزين السلاسل بناءً على المحتوى:
// الأعداد الصحيحة مخزنة كأعداد صحيحة (وليس سلاسل) - يوفر الذاكرة\n127.0.0.1:6379> SET count 12345\nOK\n\n127.0.0.1:6379> OBJECT ENCODING count\n"int"\n\n// السلاسل الصغيرة مخزنة بكفاءة\n127.0.0.1:6379> SET short "hello"\nOK\n\n127.0.0.1:6379> OBJECT ENCODING short\n"embstr" // سلسلة مضمنة - محسنة\n\n// السلاسل الكبيرة تستخدم ترميزاً خاماً\n127.0.0.1:6379> SET large "very long string..."\nOK\n\n127.0.0.1:6379> OBJECT ENCODING large\n"raw"
تحسين الذاكرة: يستخدم Redis ما يصل إلى 3 ترميزات مختلفة للسلاسل:
- int: عدد صحيح موقع 64 بت (8 بايت)
- embstr: سلاسل ≤ 44 بايت (محسنة، ثابتة)
- raw: سلاسل > 44 بايت (ترميز قياسي)
أنماط السلاسل الشائعة
// 1. التخزين المؤقت لاستعلامات قاعدة البيانات\n$cacheKey = 'users:active:list';\n$users = Redis::get($cacheKey);\n\nif (!$users) {\n $users = User::where('active', true)->get()->toJson();\n Redis::setex($cacheKey, 600, $users); // التخزين المؤقت لمدة 10 دقائق\n}\n\n// 2. تخزين الجلسات\n$sessionId = session()->getId();\nRedis::setex('session:' . $sessionId, 7200, serialize($sessionData));\n\n// 3. التخزين المؤقت لاستجابة API\n$apiKey = 'api:weather:' . $city;\n$response = Redis::get($apiKey);\n\nif (!$response) {\n $response = $this->callWeatherAPI($city);\n Redis::setex($apiKey, 1800, $response); // التخزين المؤقت لمدة 30 دقيقة\n}\n\n// 4. أعلام الميزات\nRedis::set('feature:new_dashboard', 'enabled');\n\nif (Redis::get('feature:new_dashboard') === 'enabled') {\n // إظهار لوحة تحكم جديدة\n}
حدود حجم السلسلة:- الحد الأقصى لحجم السلسلة: 512 ميجابايت
- الحد العملي: احتفظ بالسلاسل أقل من 1 ميجابايت لأفضل أداء
- السلاسل الكبيرة تبطئ التسلسل ونقل الشبكة
- فكر في استخدام الهاشات للبيانات المنظمة بدلاً من ذلك
تمرين: نفذ نظام عداد مشاهدات الصفحة:
- أنشئ دالة تزيد عدد المشاهدات لصفحة معينة
- تتبع المشاهدات اليومية والإجمالية (استخدم مفتاحين)
- اضبط العداد اليومي لتنتهي صلاحيته في نهاية اليوم
- أضف دالة للحصول على عدد المشاهدات الحالي
- اختبر بمعرفات صفحة متعددة وتحقق من عمل العدادات بشكل صحيح
- إضافي: نفذ محدد معدل يحجب المستخدمين الذين يشاهدون أكثر من 100 صفحة في الدقيقة
في الدرس التالي، سنستكشف قوائم ومجموعات Redis - بنيات بيانات قوية لإدارة مجموعات من العناصر.