إطار Laravel

استراتيجيات التخزين المؤقت

15 دقيقة الدرس 20 من 45

استراتيجيات التخزين المؤقت في Laravel

التخزين المؤقت هو أحد أكثر الطرق فعالية لتحسين أداء التطبيق. يوفر Laravel واجهة برمجة تطبيقات موحدة لخلفيات التخزين المؤقت المختلفة، مما يسهل تخزين واسترجاع البيانات بسرعة، مما يقلل من استعلامات قاعدة البيانات والعمليات الحسابية المكلفة.

برامج تشغيل الذاكرة المؤقتة

قم بتكوين برنامج تشغيل الذاكرة المؤقتة في ملف .env:

# ذاكرة مؤقتة للملفات (افتراضي، جيد للتطوير) CACHE_DRIVER=file # ذاكرة مؤقتة للمصفوفة (مخزنة في الذاكرة، تستمر فقط للطلب) CACHE_DRIVER=array # ذاكرة مؤقتة لقاعدة البيانات CACHE_DRIVER=database # ذاكرة مؤقتة Redis (موصى به للإنتاج) CACHE_DRIVER=redis REDIS_HOST=127.0.0.1 REDIS_PASSWORD=null REDIS_PORT=6379 # ذاكرة مؤقتة Memcached CACHE_DRIVER=memcached MEMCACHED_HOST=127.0.0.1 MEMCACHED_PORT=11211 # ذاكرة مؤقتة DynamoDB CACHE_DRIVER=dynamodb AWS_ACCESS_KEY_ID=your-key AWS_SECRET_ACCESS_KEY=your-secret

لذاكرة مؤقتة لقاعدة البيانات، قم بإنشاء جدول الذاكرة المؤقتة:

php artisan cache:table php artisan migrate
ملاحظة: يُوصى باستخدام Redis للإنتاج نظرًا لسرعته وخيارات الاستمرارية والميزات المتقدمة. قم بتثبيت predis: composer require predis/predis

تخزين واسترجاع البيانات

عمليات الذاكرة المؤقتة الأساسية:

use Illuminate\Support\Facades\Cache; // تخزين البيانات (إلى الأبد) Cache::put('key', 'value'); // تخزين مع انتهاء الصلاحية (بالثواني) Cache::put('key', 'value', 600); // 10 دقائق // تخزين مع مثيل Carbon Cache::put('key', 'value', now()->addMinutes(10)); // تخزين إذا لم يكن المفتاح موجودًا Cache::add('key', 'value', 600); // تخزين إلى الأبد Cache::forever('key', 'value'); // استرجاع البيانات $value = Cache::get('key'); // استرجاع مع قيمة افتراضية $value = Cache::get('key', 'default'); // استرجاع مع إغلاق افتراضي $value = Cache::get('key', function () { return DB::table('users')->get(); }); // استرجاع وحذف $value = Cache::pull('key'); // فحص ما إذا كان المفتاح موجودًا if (Cache::has('key')) { // } // فحص ما إذا كان المفتاح غير موجود if (Cache::missing('key')) { // }

نمط Cache Remember

نمط التخزين المؤقت الأكثر شيوعًا - استرجاع أو حساب وتخزين:

// تذكر لوقت محدد $users = Cache::remember('users', 600, function () { return DB::table('users')->get(); }); // تذكر إلى الأبد $settings = Cache::rememberForever('settings', function () { return DB::table('settings')->pluck('value', 'key'); }); // نسيان (حذف) الذاكرة المؤقتة Cache::forget('key'); // مسح جميع الذاكرة المؤقتة Cache::flush(); // زيادة/نقصان Cache::increment('visits'); Cache::increment('visits', 5); Cache::decrement('credits'); Cache::decrement('credits', 3); // استرجاع وتخزين (ذري) $value = Cache::remember('key', 600, function () { return expensiveOperation(); });

علامات الذاكرة المؤقتة

تجميع عناصر الذاكرة المؤقتة ذات الصلة لإدارة أسهل (Redis و Memcached فقط):

// تخزين مع علامات Cache::tags(['users', 'admins'])->put('admin:1', $admin, 600); Cache::tags(['users'])->put('user:1', $user, 600); // استرجاع مع علامات $admin = Cache::tags(['users', 'admins'])->get('admin:1'); // تذكر مع علامات $users = Cache::tags(['users'])->remember('all', 600, function () { return User::all(); }); // مسح علامة محددة Cache::tags(['users'])->flush(); // مسح علامات متعددة Cache::tags(['users', 'admins'])->flush();
تحذير: علامات الذاكرة المؤقتة غير مدعومة بواسطة برامج تشغيل الذاكرة المؤقتة file أو database. استخدم العلامات فقط مع Redis أو Memcached.

الأقفال الذرية

منع ظروف السباق باستخدام الأقفال الذرية:

use Illuminate\Support\Facades\Cache; // قفل أساسي $lock = Cache::lock('process-orders', 10); // انتهاء صلاحية 10 ثوانٍ if ($lock->get()) { // تم الحصول على القفل، معالجة الطلبات processOrders(); // إطلاق القفل $lock->release(); } // انتظار حتى يصبح القفل متاحًا $lock = Cache::lock('process-orders', 10); $lock->block(5, function () { // تم الحصول على القفل بعد الانتظار لمدة تصل إلى 5 ثوانٍ processOrders(); }); // إطلاق تلقائي مع callback Cache::lock('process-orders')->get(function () { // تم الحصول على القفل وسيتم إطلاقه تلقائيًا processOrders(); }); // فحص ملكية القفل if ($lock->owner() === 'unique-id') { $lock->release(); } // إطلاق قسري $lock->forceRelease();

دوال مساعدة الذاكرة المؤقتة

يوفر Laravel دوال مساعدة ملائمة للذاكرة المؤقتة:

// استخدام مساعد cache() cache(['key' => 'value'], 600); // تخزين $value = cache('key'); // استرجاع cache()->remember('users', 600, function () { return User::all(); }); // استخدام دوال عامة cache_put('key', 'value', 600); $value = cache_get('key'); cache_forget('key'); cache_flush();

تخزين استعلامات النموذج المؤقت

تخزين نتائج استعلام قاعدة البيانات بشكل فعال:

// تخزين استعلام النموذج مؤقتًا $users = Cache::remember('users:active', 600, function () { return User::where('active', true)->get(); }); // تخزين نموذج واحد مؤقتًا $user = Cache::remember("user:{$id}", 600, function () use ($id) { return User::findOrFail($id); }); // تخزين مؤقت مع علاقات $posts = Cache::remember('posts:recent', 600, function () { return Post::with('author', 'comments') ->latest() ->take(10) ->get(); }); // تخزين استعلامات العد مؤقتًا $count = Cache::remember('users:count', 3600, function () { return User::count(); }); // إبطال الذاكرة المؤقتة عند أحداث النموذج class User extends Model { protected static function boot() { parent::boot(); static::saved(function () { Cache::forget('users:active'); Cache::forget('users:count'); }); static::deleted(function () { Cache::forget('users:active'); Cache::forget('users:count'); }); } }

تحديد المعدل مع الذاكرة المؤقتة

تنفيذ تحديد المعدل باستخدام الذاكرة المؤقتة:

use Illuminate\Support\Facades\RateLimiter; // تكوين محدد المعدل RateLimiter::for('api', function (Request $request) { return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()); }); // فحص حد المعدل if (RateLimiter::tooManyAttempts('send-message:' . $user->id, 5)) { $seconds = RateLimiter::availableIn('send-message:' . $user->id); return response()->json([ 'message' => "طلبات كثيرة جدًا. حاول مرة أخرى في {$seconds} ثانية." ], 429); } // ضرب محدد المعدل RateLimiter::hit('send-message:' . $user->id, 60); // تحلل 60 ثانية // تحديد المعدل المخصص $executed = RateLimiter::attempt( 'send-email:' . $user->id, $maxAttempts = 5, function () { // إرسال البريد الإلكتروني }, $decaySeconds = 60 ); if (!$executed) { return 'تم إرسال رسائل بريد إلكتروني كثيرة جدًا!'; } // مسح محدد المعدل RateLimiter::clear('send-message:' . $user->id);

تخزين العرض المؤقت

تخزين عروض Blade المترجمة مؤقتًا لأداء أفضل:

# تخزين جميع العروض مؤقتًا php artisan view:cache # مسح ذاكرة العرض المؤقتة php artisan view:clear

تخزين أجزاء العرض مؤقتًا داخل قوالب Blade:

@cache('sidebar', 600) <div class="sidebar"> @foreach(Category::all() as $category) <a href="{{ $category->url }}">{{ $category->name }}</a> @endforeach </div> @endcache {{-- توجيه ذاكرة مؤقتة مخصص (إنشاء في مزود الخدمة) --}} Blade::directive('cache', function ($expression) { return "<?php if(! cache()->has({$expression})): cache()->put({$expression}, ob_start()); ?>"; }); Blade::directive('endcache', function () { return "<?php cache()->put( array_shift(func_get_args()), ob_get_clean() ); endif; ?>"; });

استراتيجيات التخزين المؤقت المتقدمة

// نمط Cache-Aside public function getUser($id) { return Cache::remember("user:{$id}", 600, function () use ($id) { return User::find($id); }); } // نمط Write-Through public function updateUser($id, array $data) { $user = User::findOrFail($id); $user->update($data); Cache::put("user:{$id}", $user, 600); return $user; } // تسخين الذاكرة المؤقتة public function warmCache() { $popularProducts = Product::popular()->get(); Cache::put('products:popular', $popularProducts, 3600); $categories = Category::with('products')->get(); Cache::put('categories:tree', $categories, 3600); } // نمط Stale-While-Revalidate public function getProducts() { $products = Cache::get('products'); if (!$products) { $products = Product::all(); Cache::put('products', $products, 600); } // إعادة التحقق في الخلفية إذا كانت الذاكرة المؤقتة قديمة if (Cache::get('products:timestamp') < now()->subMinutes(5)) { dispatch(new RefreshProductsCache()); Cache::put('products:timestamp', now(), 600); } return $products; } // التخزين المؤقت متعدد المستويات public function getUser($id) { // المستوى 1: ذاكرة التطبيق المؤقتة (Redis) $user = Cache::get("user:{$id}"); if (!$user) { // المستوى 2: ذاكرة نتيجة الاستعلام المؤقتة $user = DB::table('users') ->where('id', $id) ->remember(600) ->first(); Cache::put("user:{$id}", $user, 600); } return $user; }
نصيحة: استخدم وضع علامات على الذاكرة المؤقتة لتنظيم إدخالات الذاكرة المؤقتة ذات الصلة. عندما يحدث مستخدم ملفه الشخصي، امسح جميع الذاكرات المؤقتة المتعلقة بالمستخدم: Cache::tags(['user:' . $id])->flush()

مراقبة أداء الذاكرة المؤقتة

// تسجيل إصابات وإخفاقات الذاكرة المؤقتة Cache::spy(); Event::listen('cache:hit', function ($event) { \Log::info('إصابة ذاكرة مؤقتة', ['key' => $event->key]); }); Event::listen('cache:missed', function ($event) { \Log::info('إخفاق ذاكرة مؤقتة', ['key' => $event->key]); }); // مراقبة أداء الذاكرة المؤقتة $start = microtime(true); $value = Cache::get('expensive-query'); $time = microtime(true) - $start; if ($time > 0.1) { \Log::warning('استرجاع ذاكرة مؤقتة بطيء', [ 'key' => 'expensive-query', 'time' => $time, ]); }
تمرين 1: نفذ استراتيجية تخزين مؤقت شاملة لمدونة. خزن قوائم المنشورات مؤقتًا، والمنشورات الفردية مع التعليقات، وأشجار الفئات، والمنشورات الشائعة. قم بتضمين إبطال ذاكرة مؤقتة تلقائي عند إنشاء المنشورات أو تحديثها أو حذفها.
تمرين 2: ابنِ محدد معدل لواجهة برمجة تطبيقات باستخدام الذاكرة المؤقتة. نفذ حدود معدل لكل مستخدم (100 طلب/ساعة)، وحدود تعتمد على IP للمستخدمين المجهولين (20 طلب/ساعة)، وأرجع استجابات 429 مناسبة مع رؤوس Retry-After.
تمرين 3: أنشئ نظام تسخين ذاكرة مؤقتة يملأ الذاكرة المؤقتة مسبقًا بالبيانات التي يتم الوصول إليها بشكل متكرر خلال ساعات خارج الذروة. استخدم المهام المجدولة لتحديث الذاكرة المؤقتة لبيانات الصفحة الرئيسية والمنتجات الشائعة وقوائم التنقل كل 30 دقيقة.

أفضل ممارسات الذاكرة المؤقتة

أفضل الممارسات:
  • استخدم TTL مناسب: TTL قصير للبيانات الديناميكية (5-10 دقائق)، TTL طويل للبيانات الثابتة (ساعات/أيام)
  • تخزين مؤقت في طبقات متعددة: ذاكرة HTTP مؤقتة، ذاكرة التطبيق المؤقتة، ذاكرة استعلام قاعدة البيانات المؤقتة
  • إبطال بذكاء: استخدم العلامات أو الأنماط لإبطال الذاكرات المؤقتة ذات الصلة معًا
  • راقب نسبة إصابة الذاكرة المؤقتة: اهدف إلى نسبة إصابة 80٪+ للتخزين المؤقت الفعال
  • تجنب اندفاع الذاكرة المؤقتة: استخدم الأقفال عند إعادة إنشاء البيانات المخزنة مؤقتًا المكلفة
  • خزن البيانات القابلة للتسلسل مؤقتًا: لا تخزن الموارد أو الإغلاقات مؤقتًا
  • استخدم Redis للإنتاج: إنه سريع وموثوق ويدعم ميزات متقدمة
  • سخن الذاكرات المؤقتة الحرجة: املأ الذاكرة المؤقتة مسبقًا قبل ارتفاع حركة المرور

التخزين المؤقت الفعال أمر بالغ الأهمية لبناء تطبيقات Laravel عالية الأداء. من خلال تنفيذ استراتيجيات التخزين المؤقت المناسبة، يمكنك تقليل حمل قاعدة البيانات بشكل كبير، وتحسين أوقات الاستجابة، وتوفير تجربة مستخدم أفضل. هذا يختتم سلسلة دروسنا حول إطار عمل Laravel!