Redis والتخزين المؤقت المتقدم

Redis مع Laravel

20 دقيقة الدرس 25 من 30

Redis مع Laravel

يوفر Laravel دعمًا من الدرجة الأولى لـ Redis من خلال واجهات برمجة التطبيقات الأنيقة بما في ذلك Cache و Redis facade والقوائم والجلسات والبث. تعلم كيفية الاستفادة من Redis لبناء تطبيقات Laravel عالية الأداء.

تكوين Laravel Redis

قم بتكوين اتصالات Redis في config/database.php:

config/database.php:
'redis' => [
'client' => env('REDIS_CLIENT', 'phpredis'),

'options' => [
'cluster' => env('REDIS_CLUSTER', 'redis'),
'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
],

'default' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', 6379),
'database' => env('REDIS_DB', 0),
],

'cache' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', 6379),
'database' => env('REDIS_CACHE_DB', 1),
],
],
تكوين .env:
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
REDIS_DB=0
REDIS_CACHE_DB=1

# أو استخدم تنسيق URL
REDIS_URL=redis://user:password@127.0.0.1:6379
عملاء Redis:
  • phpredis: امتداد PHP، أداء أسرع (موصى به للإنتاج)
  • predis: مكتبة PHP نقية، تثبيت أسهل، لا يتطلب امتداد
تثبيت phpredis: pecl install redis && echo "extension=redis.so" > /etc/php/conf.d/redis.ini

واجهة Laravel Cache

استخدم واجهة Cache مع برنامج تشغيل Redis للتخزين المؤقت عالي الأداء:

config/cache.php:
'default' => env('CACHE_DRIVER', 'redis'),

'stores' => [
'redis' => [
'driver' => 'redis',
'connection' => 'cache',
'lock_connection' => 'default',
],
],
عمليات التخزين المؤقت الأساسية:
use Illuminate\Support\Facades\Cache;

# تخزين القيمة لمدة ساعة واحدة
Cache::put('key', 'value', now()->addHours(1));

# تخزين للأبد (بدون انتهاء صلاحية)
Cache::forever('key', 'value');

# استرداد القيمة
$value = Cache::get('key');

# استرداد مع افتراضي
$value = Cache::get('key', 'default');

# استرداد وحذف
$value = Cache::pull('key');

# التحقق من الوجود
if (Cache::has('key')) {
// المفتاح موجود
}

# حذف المفتاح
Cache::forget('key');

# مسح جميع ذاكرة التخزين المؤقت
Cache::flush();
نمط Cache Remember:
# استرداد أو تخزين إذا لم يكن موجودًا
$users = Cache::remember('users', 3600, function () {
return DB::table('users')->get();
});

# تذكر للأبد
$settings = Cache::rememberForever('settings', function () {
return DB::table('settings')->get();
});

# زيادة/إنقاص
Cache::increment('counter');
Cache::decrement('counter');
Cache::increment('counter', 5);
Cache::decrement('counter', 5);
نصيحة التخزين المؤقت: استخدم Cache::remember() لاستعلامات قاعدة البيانات لتخزين النتائج تلقائيًا والاستعلام فقط من قاعدة البيانات إذا فشل التخزين المؤقت.

علامات التخزين المؤقت (Redis فقط)

تسمح لك علامات التخزين المؤقت بتجميع عناصر التخزين المؤقت ذات الصلة ومسحها معًا:

استخدام علامات التخزين المؤقت:
# تخزين مع علامات
Cache::tags(['users', 'authors'])->put('user:1', $user, 3600);
Cache::tags(['users'])->put('user:2', $user2, 3600);

# استرداد مع علامات
$user = Cache::tags(['users'])->get('user:1');

# مسح جميع العناصر بعلامة محددة
Cache::tags(['users'])->flush();

# مسح علامات متعددة
Cache::tags(['users', 'authors'])->flush();
مثال عملي:
# في UserController
public function index()
{
return Cache::tags(['users', 'index'])->remember('users.all', 3600, function () {
return User::all();
});
}

# بعد تحديث المستخدم، إبطال التخزين المؤقت
public function update(Request $request, User $user)
{
$user->update($request->all());
Cache::tags(['users'])->flush();
return redirect()->back();
}

واجهة Redis - أوامر Redis المباشرة

استخدم واجهة Redis للوصول المباشر إلى أوامر Redis:

عمليات Redis الأساسية:
use Illuminate\Support\Facades\Redis;

# عمليات السلسلة
Redis::set('name', 'John');
$name = Redis::get('name');

# عمليات الهاش
Redis::hset('user:1', 'name', 'John');
Redis::hset('user:1', 'email', 'john@example.com');
$user = Redis::hgetall('user:1');

# عمليات القائمة
Redis::lpush('queue:jobs', json_encode($job));
$job = Redis::rpop('queue:jobs');

# عمليات المجموعة
Redis::sadd('tags', 'php', 'laravel', 'redis');
$tags = Redis::smembers('tags');

# عمليات المجموعة المرتبة
Redis::zadd('leaderboard', 100, 'user:1');
Redis::zadd('leaderboard', 200, 'user:2');
$top = Redis::zrevrange('leaderboard', 0, 9);
اتصالات Redis متعددة:
# استخدام الاتصال الافتراضي
Redis::get('key');

# استخدام اتصال التخزين المؤقت
Redis::connection('cache')->get('key');

# تحديد اتصال مخصص
$redis = Redis::connection('custom');
$redis->set('key', 'value');

معاملات وخطوط أنابيب Redis

نفذ أوامر متعددة بشكل ذري أو دفعة:

المعاملات (MULTI/EXEC):
Redis::transaction(function ($redis) {
$redis->set('key1', 'value1');
$redis->set('key2', 'value2');
$redis->incr('counter');
});
# يتم تنفيذ جميع الأوامر بشكل ذري

خطوط الأنابيب (أوامر الدفعة):
Redis::pipeline(function ($pipe) {
for ($i = 0; $i < 1000; $i++) {
$pipe->set("key:$i", "value$i");
}
});
# يتم إرسال الأوامر في رحلة شبكة واحدة

تحديد المعدل مع Redis

يوفر Laravel تحديد معدل يعتمد على Redis لنقاط نهاية API والإجراءات:

وسيطة Throttle:
use Illuminate\Routing\Middleware\ThrottleRequests;

# في routes/api.php
Route::middleware(['throttle:60,1'])->group(function () {
Route::get('/user', [UserController::class, 'index']);
});
# 60 طلب في دقيقة واحدة

# محدد المعدل المسمى
Route::middleware(['throttle:api'])->group(function () {
// المسارات
});
محدد المعدل المخصص:
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;

# في AppServiceProvider
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});

# حد معدل مخصص لكل مستوى مستخدم
RateLimiter::for('api', function (Request $request) {
return $request->user()->isPremium()
? Limit::none()
: Limit::perMinute(100);
});
تحديد المعدل اليدوي:
use Illuminate\Support\Facades\RateLimiter;

$executed = RateLimiter::attempt(
'send-email:'.$user->id,
$maxAttempts = 5,
function () use ($user) {
Mail::to($user)->send(new Newsletter());
},
$decaySeconds = 60
);

if (!$executed) {
return 'تم إرسال عدد كبير جدًا من رسائل البريد الإلكتروني!';
}

القوائم مع Redis

استخدم Redis كبرنامج تشغيل قائمة انتظار لمعالجة الوظائف في الخلفية:

config/queue.php:
'default' => env('QUEUE_CONNECTION', 'redis'),

'connections' => [
'redis' => [
'driver' => 'redis',
'connection' => 'default',
'queue' => env('REDIS_QUEUE', 'default'),
'retry_after' => 90,
'block_for' => null,
],
],
إرسال الوظائف:
use App\Jobs\ProcessPodcast;

# إرسال إلى قائمة الانتظار
ProcessPodcast::dispatch($podcast);

# إرسال إلى قائمة انتظار محددة
ProcessPodcast::dispatch($podcast)->onQueue('processing');

# تأخير التنفيذ
ProcessPodcast::dispatch($podcast)->delay(now()->addMinutes(10));

# تشغيل عامل قائمة الانتظار
php artisan queue:work redis --queue=high,default,low
فوائد القائمة مع Redis:
  • إدراج سريع للوظائف ومعالجتها
  • قوائم انتظار متعددة مع الأولوية
  • معالجة إعادة المحاولة والفشل
  • وظائف مؤجلة
  • رؤية في الوقت الفعلي مع Horizon

البث مع Redis

استخدم Redis لبث الأحداث في الوقت الفعلي مع WebSockets:

config/broadcasting.php:
'default' => env('BROADCAST_DRIVER', 'redis'),

'connections' => [
'redis' => [
'driver' => 'redis',
'connection' => 'default',
],
],
بث الأحداث:
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class OrderShipped implements ShouldBroadcast
{
public $order;

public function broadcastOn()
{
return new Channel('orders.' . $this->order->id);
}
}

# تشغيل الحدث
event(new OrderShipped($order));

# الاستماع مع Laravel Echo + Socket.io
Echo.channel('orders.1')
.listen('OrderShipped', (e) => {
console.log(e.order);
});

برنامج تشغيل الجلسة مع Redis

قم بتخزين جلسات المستخدم في Redis للسرعة وقابلية التوسع:

config/session.php:
'driver' => env('SESSION_DRIVER', 'redis'),
'connection' => 'default',

.env:
SESSION_DRIVER=redis
SESSION_LIFETIME=120
فوائد الجلسة: جلسات Redis أسرع من الجلسات المستندة إلى الملفات وتعمل بسلاسة عبر خوادم متعددة في البيئات متوازنة الحمل.

قفل التخزين المؤقت (العمليات الذرية)

منع ظروف السباق بالأقفال الموزعة:

أقفال التخزين المؤقت:
use Illuminate\Support\Facades\Cache;

$lock = Cache::lock('process-orders', 10);

if ($lock->get()) {
try {
// معالجة الطلبات (وصول حصري لمدة 10 ثوانٍ)
processOrders();
} finally {
$lock->release();
}
} else {
// عملية أخرى قيد التشغيل بالفعل
return 'المعالجة جارية بالفعل';
}

# حظر حتى يتوفر القفل
$lock->block(5); // انتظر حتى 5 ثوانٍ
// قسم حرج
$lock->release();

Laravel Horizon - مراقبة قائمة انتظار Redis

يوفر Horizon لوحة تحكم لمراقبة قوائم انتظار Redis:

تثبيت Horizon:
composer require laravel/horizon
php artisan horizon:install
php artisan migrate

# بدء Horizon
php artisan horizon

# الوصول إلى لوحة التحكم في /horizon
Route::get('/horizon', function () {
return view('horizon::layout');
});
ميزات Horizon:
  • مقاييس قائمة الانتظار في الوقت الفعلي
  • إدارة الوظائف الفاشلة
  • رسوم بيانية لإنتاجية الوظائف
  • مراقبة العاملين
  • توسيع تلقائي للعاملين
  • وضع علامات والبحث عن الوظائف

أفضل الممارسات

أفضل ممارسات Laravel + Redis:
  • ✅ استخدام Cache::remember() لتخزين استعلامات قاعدة البيانات مؤقتًا
  • ✅ استخدام علامات التخزين المؤقت لتجميع العناصر ذات الصلة
  • ✅ تكوين قواعد بيانات Redis منفصلة للتخزين المؤقت والجلسات والقوائم
  • ✅ استخدام امتداد phpredis في الإنتاج لأداء أفضل
  • ✅ تنفيذ تحديد المعدل لنقاط نهاية API
  • ✅ استخدام قوائم الانتظار للعمليات البطيئة (رسائل البريد الإلكتروني، معالجة الصور، إلخ.)
  • ✅ مراقبة Redis باستخدام Laravel Horizon
  • ✅ استخدام أقفال التخزين المؤقت لمنع ظروف السباق
  • ✅ تعيين قيم TTL مناسبة لتجنب تضخم الذاكرة
  • ✅ استخدام Redis للجلسات في عمليات نشر متعددة الخوادم
تمرين: قم ببناء تطبيق Laravel مع Redis: 1) قم بتكوين Redis للتخزين المؤقت والجلسة وقائمة الانتظار، 2) أنشئ نقطة نهاية API مع تحديد المعدل (60 طلب/دقيقة)، 3) قم بتخزين استعلامات قاعدة البيانات مؤقتًا باستخدام Cache::remember()، 4) أنشئ وظيفة في الخلفية لإرسال رسائل البريد الإلكتروني، 5) قم بتنفيذ علامات التخزين المؤقت لبيانات المستخدم والمسح عند التحديث، 6) راقب قوائم الانتظار باستخدام لوحة تحكم Horizon.