Laravel المتقدم

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

18 دقيقة الدرس 11 من 40

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

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

علامات التخزين المؤقت لإدارة منظمة

تسمح لك علامات التخزين المؤقت بتجميع عناصر التخزين المؤقت ذات الصلة ومسحها معاً. هذا مفيد بشكل خاص لإبطال البيانات ذات الصلة بكفاءة.

// تخزين مع علامات Cache::tags(['users', 'premium'])->put('user:123', $user, 3600); Cache::tags(['users', 'admin'])->put('user:456', $admin, 3600); // استرجاع التخزين المؤقت المعلّم $user = Cache::tags(['users', 'premium'])->get('user:123'); // مسح جميع عناصر التخزين المؤقت بعلامة معينة Cache::tags(['users'])->flush(); // يزيل كلا المستخدمين // علامات متعددة Cache::tags(['products', 'category:5'])->put('products:category:5', $products, 7200);
ملاحظة: علامات التخزين المؤقت غير مدعومة بواسطة محركات التخزين المؤقت file أو database. استخدم redis أو memcached لدعم العلامات.

الأقفال الذرية لمنع حالات السباق

تضمن الأقفال الذرية أن عملية واحدة فقط يمكنها تنفيذ قسم حرج من الكود في وقت واحد، مما يمنع حالات السباق في التطبيقات ذات الحركة المرورية العالية.

use Illuminate\Support\Facades\Cache; // استخدام أساسي للقفل $lock = Cache::lock('process-orders', 10); // 10 ثوانٍ if ($lock->get()) { try { // قسم حرج - عملية واحدة فقط يمكن أن تكون هنا $this->processOrders(); } finally { $lock->release(); } } else { // لم يتم الحصول على القفل Log::info('عملية أخرى تقوم بالفعل بمعالجة الطلبات'); } // حظر والانتظار للقفل $lock = Cache::lock('generate-report', 30); $lock->block(5, function () { // انتظر حتى 5 ثوانٍ للقفل $this->generateReport(); }); // إصدار تلقائي مع callback Cache::lock('send-notifications', 10)->get(function () { $this->sendNotifications(); // يتم إطلاق القفل تلقائياً بعد callback });

نمط مزخرفات التخزين المؤقت

يسمح لك نمط المزخرف بتغليف طرق المستودع أو الخدمة بمنطق التخزين المؤقت، مما يحافظ على نظافة وصيانة الكود الخاص بك.

namespace App\Services; class CachedUserRepository { public function __construct( protected UserRepository $repository ) {} public function find($id) { return Cache::remember( "users:{$id}", 3600, fn() => $this->repository->find($id) ); } public function findByEmail($email) { return Cache::tags(['users'])->remember( "users:email:{$email}", 1800, fn() => $this->repository->findByEmail($email) ); } public function update($id, array $data) { $user = $this->repository->update($id, $data); // إبطال التخزين المؤقت Cache::forget("users:{$id}"); Cache::tags(['users'])->flush(); return $user; } public function getActive() { return Cache::tags(['users', 'active'])->remember( 'users:active', 600, fn() => $this->repository->getActive() ); } }
نصيحة: استخدم نمط المزخرف لإضافة التخزين المؤقت إلى الخدمات الموجودة دون تعديل الكود الخاص بها. اربط الإصدار المخزن مؤقتاً في مزود الخدمة الخاص بك للتبديل السهل بين التطبيقات المخزنة مؤقتاً وغير المخزنة مؤقتاً.

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

خزن نتائج استعلامات قاعدة البيانات مؤقتاً لتقليل الحمل على قاعدة البيانات. يوفر Laravel طرقاً أنيقة لتخزين استعلامات Eloquent مؤقتاً.

use Illuminate\Support\Facades\DB; // تخزين نتائج الاستعلام مؤقتاً $users = Cache::remember('active-users', 600, function () { return DB::table('users') ->where('status', 'active') ->get(); }); // التخزين المؤقت مع query builder $products = Cache::remember('featured-products', 3600, function () { return Product::where('featured', true) ->with(['category', 'images']) ->get(); }); // تخزين نموذج فردي مؤقتاً $user = Cache::remember("user:{$id}", 1800, function () use ($id) { return User::with(['profile', 'roles'])->findOrFail($id); }); // التخزين المؤقت الشرطي public function getProducts($categoryId = null) { $key = $categoryId ? "products:category:{$categoryId}" : 'products:all'; return Cache::tags(['products'])->remember($key, 1800, function () use ($categoryId) { $query = Product::query(); if ($categoryId) { $query->where('category_id', $categoryId); } return $query->with('category')->get(); }); }

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

خزن استجابات HTTP الكاملة مؤقتاً لخدمة الطلبات المتكررة فوراً دون تنفيذ أي منطق تطبيق.

// في routes/web.php Route::get('/blog/{slug}', [BlogController::class, 'show']) ->middleware('cache.headers:public;max_age=3600'); // في المتحكم public function show($slug) { return Cache::remember("blog:{$slug}", 3600, function () use ($slug) { $post = Post::where('slug', $slug)->firstOrFail(); return view('blog.show', compact('post')); }); } // وسيط مخصص للتخزين المؤقت للاستجابة namespace App\Http\Middleware; class CacheResponse { public function handle($request, Closure $next, $minutes = 60) { $key = 'response:' . $request->fullUrl(); if ($cachedResponse = Cache::get($key)) { return response($cachedResponse['content']) ->withHeaders($cachedResponse['headers']); } $response = $next($request); if ($response->isSuccessful()) { Cache::put($key, [ 'content' => $response->getContent(), 'headers' => $response->headers->all(), ], $minutes * 60); } return $response; } }
تحذير: كن حذراً عند تخزين الاستجابات مؤقتاً للمستخدمين المصادق عليهم. لا تخزن المحتوى المخصص مؤقتاً على طبقة HTTP. استخدم مفاتيح تخزين مؤقت خاصة بالمستخدم أو استبعد المسارات المصادق عليها من التخزين المؤقت للاستجابة.

التكامل مع CDN وتسخين التخزين المؤقت

التكامل مع شبكات توصيل المحتوى وتنفيذ استراتيجيات تسخين التخزين المؤقت للأداء الأمثل.

// أمر تسخين التخزين المؤقت namespace App\Console\Commands; use Illuminate\Console\Command; class WarmCache extends Command { protected $signature = 'cache:warm'; protected $description = 'تسخين التخزين المؤقت للتطبيق'; public function handle() { $this->info('جارٍ تسخين التخزين المؤقت...'); // تسخين بيانات الصفحة الرئيسية Cache::remember('homepage:featured', 3600, function () { return Product::featured()->limit(8)->get(); }); // تسخين بيانات الفئة $categories = Category::all(); foreach ($categories as $category) { Cache::tags(['products'])->remember( "products:category:{$category->id}", 3600, fn() => $category->products->load('images') ); } // تسخين قائمة التنقل Cache::remember('menu:main', 7200, function () { return Menu::with(['items' => function ($query) { $query->orderBy('order'); }])->where('slug', 'main')->first(); }); $this->info('اكتمل تسخين التخزين المؤقت!'); } } // مسح تخزين CDN المؤقت use Illuminate\Support\Facades\Http; class CdnCachePurger { public function purgeUrl($url) { // مثال CloudFlare Http::withHeaders([ 'X-Auth-Email' => config('services.cloudflare.email'), 'X-Auth-Key' => config('services.cloudflare.key'), ])->post('https://api.cloudflare.com/client/v4/zones/' . config('services.cloudflare.zone') . '/purge_cache', [ 'files' => [$url], ]); } public function purgeAll() { Http::withHeaders([ 'X-Auth-Email' => config('services.cloudflare.email'), 'X-Auth-Key' => config('services.cloudflare.key'), ])->post('https://api.cloudflare.com/client/v4/zones/' . config('services.cloudflare.zone') . '/purge_cache', [ 'purge_everything' => true, ]); } }

مراقبة أداء التخزين المؤقت

راقب معدلات نجاح التخزين المؤقت والأداء لتحسين استراتيجية التخزين المؤقت الخاصة بك.

namespace App\Services; class CacheMonitor { public function recordHit($key) { Cache::increment("cache:hits:{$key}"); Cache::increment('cache:hits:total'); } public function recordMiss($key) { Cache::increment("cache:misses:{$key}"); Cache::increment('cache:misses:total'); } public function getHitRate() { $hits = Cache::get('cache:hits:total', 0); $misses = Cache::get('cache:misses:total', 0); $total = $hits + $misses; return $total > 0 ? ($hits / $total) * 100 : 0; } public function remember($key, $ttl, $callback) { $value = Cache::get($key); if ($value !== null) { $this->recordHit($key); return $value; } $this->recordMiss($key); $value = $callback(); Cache::put($key, $value, $ttl); return $value; } public function getStats() { return [ 'hit_rate' => round($this->getHitRate(), 2), 'total_hits' => Cache::get('cache:hits:total', 0), 'total_misses' => Cache::get('cache:misses:total', 0), ]; } }
تمرين 1: أنشئ مستودعاً مخزناً مؤقتاً لمنشورات المدونة يستخدم علامات التخزين المؤقت لتنظيم المنشورات حسب الفئة. نفذ طرقاً لاسترجاع المنشورات، وتحديث التخزين المؤقت عند التغييرات، ومسح التخزين المؤقت الخاص بفئة معينة.
تمرين 2: ابنِ نظام تسخين التخزين المؤقت الذي يحمّل مسبقاً البيانات الأكثر وصولاً (منتجات الصفحة الرئيسية، قوائم التنقل، المنشورات الشائعة) أثناء النشر أو وفقاً لجدول زمني.
تمرين 3: نفذ أقفالاً ذرية لميزة إنشاء تقرير كثيفة الموارد. تأكد من إمكانية إنشاء تقرير واحد فقط في وقت واحد وضع الطلبات الإضافية في قائمة الانتظار بأمان.