تحسين الأداء وأفضل الممارسات
تحسين تطبيقات Laravel للإنتاج يتطلب فهم آليات التخزين المؤقت المختلفة، وتقنيات تحسين الاستعلامات، واستراتيجيات إدارة الأصول، وأدوات المراقبة. في هذا الدرس الشامل، سنستكشف التقنيات المثبتة لجعل تطبيقات Laravel الخاصة بك سريعة وفعالة بشكل مذهل.
التخزين المؤقت للتكوين
يقوم Laravel بتحميل ملفات التكوين في كل طلب. قم بتخزينها مؤقتاً للإنتاج للتخلص من حمل إدخال/إخراج الملفات:
// تخزين جميع ملفات التكوين مؤقتاً في ملف واحد
php artisan config:cache
// مسح ذاكرة التكوين المؤقتة (أثناء التطوير)
php artisan config:clear
// مهم: بعد التخزين المؤقت، تُرجع استدعاءات env() قيمة null
// استخدم دائماً config() في كودك، ولا تستخدم env() خارج ملفات التكوين أبداً
// ❌ سيء - سينكسر بعد config:cache
$apiKey = env('API_KEY');
// ✅ جيد - يعمل مع التكوين المخزن مؤقتاً
$apiKey = config('services.api.key');
// config/services.php
return [
'api' => [
'key' => env('API_KEY'),
'secret' => env('API_SECRET'),
],
];
// نص النشر
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan event:cache
حرج: لا تستخدم أبداً env() في كود تطبيقك بعد تشغيل config:cache. يجب الوصول إلى جميع متغيرات البيئة من خلال مساعدات config(). ستُرجع استدعاءات env() المباشرة قيمة null عند تخزين التكوين مؤقتاً.
التخزين المؤقت للمسارات
يمكن أن يكون تسجيل المسارات بطيئاً للتطبيقات الكبيرة. قم بتخزين المسارات مؤقتاً للتحميل الفوري:
// تخزين جميع المسارات مؤقتاً
php artisan route:cache
// مسح ذاكرة المسارات المؤقتة
php artisan route:clear
// القيود مع التخزين المؤقت للمسارات:
// ❌ الإغلاقات في المسارات غير مدعومة مع التخزين المؤقت للمسارات
Route::get('/user', function () {
return 'Hello';
}); // هذا سيكسر route:cache
// ✅ استخدم طرق المتحكم بدلاً من ذلك
Route::get('/user', [UserController::class, 'index']);
// ✅ أو استخدم المتحكمات القابلة للاستدعاء
Route::get('/user', UserController::class);
// التحقق مما إذا كانت المسارات مخزنة مؤقتاً
if (app()->routesAreCached()) {
// المسارات مخزنة مؤقتاً
}
// الجمع مع التخزين المؤقت للتكوين في النشر
php artisan optimize
// هذا يشغل: config:cache, route:cache, view:cache
التخزين المؤقت للعروض
قم بتجميع جميع قوالب Blade مسبقاً لتسريع العرض:
// تجميع وتخزين جميع العروض مؤقتاً
php artisan view:cache
// مسح ذاكرة العروض المؤقتة
php artisan view:clear
// يتم تجميع العروض تلقائياً عند الوصول الأول
// التخزين المؤقت يقوم بتجميع مسبق لجميع العروض لوقت تجميع صفري
// التحقق من موقع العرض المجمع
storage/framework/views/
// التخزين المؤقت المخصص للعروض للعروض المحددة
public function cacheImportantViews()
{
$views = ['home', 'dashboard', 'products.index'];
foreach ($views as $view) {
view($view)->render();
}
}
تكوين OPcache
يخزن OPcache كود PHP المُجمع في الذاكرة، مما يحسن الأداء بشكل كبير:
// تكوين php.ini للإنتاج
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0 // لا تتحقق من تغييرات الملفات (للإنتاج فقط!)
opcache.save_comments=1
opcache.fast_shutdown=1
// للتطوير، اسمح بالتحقق من الطابع الزمني
opcache.validate_timestamps=1
opcache.revalidate_freq=2
// بعد النشر، أعد تحميل OPcache
php artisan opcache:clear // إذا كنت تستخدم حزمة Laravel OPcache
// أو يدوياً
opcache_reset();
// التحقق من حالة OPcache
<?php
phpinfo(); // ابحث عن قسم OPcache
?>
// مراقبة OPcache
$status = opcache_get_status();
echo "Hits: " . $status['opcache_statistics']['hits'];
echo "Misses: " . $status['opcache_statistics']['misses'];
echo "Memory: " . $status['memory_usage']['used_memory'];
نصائح OPcache:
- اضبط validate_timestamps=0 في الإنتاج لأقصى أداء
- امسح دائماً OPcache بعد النشر
- اضبط max_accelerated_files أعلى من إجمالي ملفات PHP الخاصة بك
- راقب نسبة الإصابة في الذاكرة المؤقتة (يجب أن تكون >95%)
تحسين استعلامات قاعدة البيانات
حسّن استعلامات قاعدة البيانات للتخلص من مشاكل N+1 وتقليل عدد الاستعلامات:
// ❌ سيء - مشكلة استعلام N+1
$posts = Post::all();
foreach ($posts as $post) {
echo $post->user->name; // ينفذ استعلاماً لكل منشور
}
// النتيجة: 1 + N استعلامات
// ✅ جيد - التحميل المسبق
$posts = Post::with('user')->get();
foreach ($posts as $post) {
echo $post->user->name;
}
// النتيجة: استعلامان فقط
// علاقات متعددة
$posts = Post::with(['user', 'comments', 'tags'])->get();
// علاقات متداخلة
$posts = Post::with('comments.user')->get();
// التحميل المسبق الشرطي
$posts = Post::with([
'comments' => function ($query) {
$query->where('approved', true)
->orderBy('created_at', 'desc')
->limit(5);
}
])->get();
// التحميل المسبق الكسول (عندما تنسى التحميل المسبق)
$posts = Post::all();
$posts->load('user'); // تحميل العلاقة بعد الجلب
// تحديد الأعمدة المطلوبة فقط
$users = User::select('id', 'name', 'email')->get();
// استخدم الفهارس على الأعمدة المستعلمة بشكل متكرر
Schema::table('posts', function (Blueprint $table) {
$table->index('user_id');
$table->index('status');
$table->index(['user_id', 'created_at']); // فهرس مركب
});
// تجزئة مجموعات النتائج الكبيرة
User::chunk(1000, function ($users) {
foreach ($users as $user) {
// معالجة المستخدم
}
});
// استخدم cursor لكفاءة الذاكرة
foreach (User::cursor() as $user) {
// نموذج واحد فقط في الذاكرة في وقت واحد
}
// استعلامات خام للعمليات المعقدة
$results = DB::select('
SELECT u.name, COUNT(p.id) as post_count
FROM users u
LEFT JOIN posts p ON u.id = p.user_id
GROUP BY u.id
HAVING post_count > 10
');
التخزين المؤقت في Redis
استفد من Redis للتخزين المؤقت عالي الأداء وتخزين الجلسات:
// تكوين Redis (config/database.php)
'redis' => [
'client' => 'phpredis', // أسرع من 'predis'
'default' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'port' => env('REDIS_PORT', 6379),
'database' => 0,
],
'cache' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'port' => env('REDIS_PORT', 6379),
'database' => 1,
],
],
// استخدام Redis للذاكرة المؤقتة (config/cache.php)
'default' => env('CACHE_DRIVER', 'redis'),
// تخزين استعلامات قاعدة البيانات مؤقتاً
$users = Cache::remember('users:active', 3600, function () {
return User::where('active', true)->get();
});
// التخزين المؤقت مع العلامات
Cache::tags(['users', 'premium'])->put('premium-users', $users, 3600);
$premiumUsers = Cache::tags(['users', 'premium'])->get('premium-users');
// إبطال الذاكرة المؤقتة الموسومة
Cache::tags(['users'])->flush();
// التخزين المؤقت للأبد (حتى الحذف يدوياً)
Cache::forever('settings', $settings);
// العمليات الذرية
Cache::increment('page_views');
Cache::decrement('remaining_slots');
// آلية القفل لمنع حالات السباق
$lock = Cache::lock('process-order:' . $orderId, 10);
if ($lock->get()) {
// معالجة الطلب
// سيتم إطلاق القفل تلقائياً بعد 10 ثوانٍ
$lock->release();
}
// استخدام Redis للجلسات (config/session.php)
'driver' => env('SESSION_DRIVER', 'redis'),
تحسين الأصول
حسّن CSS و JavaScript والصور لتحميل الصفحة بشكل أسرع:
// تثبيت Laravel Mix أو Vite لتجميع الأصول
npm install
// تصغير الأصول في الإنتاج (vite.config.js)
export default defineConfig({
build: {
minify: 'terser',
cssMinify: true,
rollupOptions: {
output: {
manualChunks: {
vendor: ['vue', 'axios'],
},
},
},
},
});
// البناء للإنتاج
npm run build
// تحسين الصور
composer require spatie/laravel-image-optimizer
// config/image-optimizer.php
'optimizers' => [
Jpegoptim::class => [
'--strip-all',
'--all-progressive',
'--max=85',
],
Pngquant::class => [
'--force',
'--quality=85-100',
],
],
// تحسين الصور عند التحميل
use Spatie\ImageOptimizer\OptimizerChainFactory;
public function store(Request $request)
{
$path = $request->file('image')->store('images');
$optimizerChain = OptimizerChainFactory::create();
$optimizerChain->optimize(storage_path('app/' . $path));
}
// التحميل الكسول للصور في العروض
<img src="{{ $image }}" loading="lazy" alt="Description">
// استخدام CDN للأصول الثابتة
// config/filesystems.php
'cdn' => [
'driver' => 's3',
'url' => env('CDN_URL'),
],
// الإصدار لإزالة الذاكرة المؤقتة
<link rel="stylesheet" href="{{ mix('css/app.css') }}">
<script src="{{ mix('js/app.js') }}"></script>
// أو مع Vite
@vite(['resources/css/app.css', 'resources/js/app.js'])
ضغط الاستجابة
قم بتمكين ضغط gzip لتقليل استخدام النطاق الترددي:
// Apache (.htaccess)
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css
AddOutputFilterByType DEFLATE application/javascript application/json
</IfModule>
// Nginx (nginx.conf)
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
// برنامج Laravel الوسيط لضغط الاستجابة
namespace App\Http\Middleware;
class CompressResponse
{
public function handle($request, $next)
{
$response = $next($request);
if (!$request->wantsJson()) {
return $response;
}
if (!str_contains($request->header('Accept-Encoding'), 'gzip')) {
return $response;
}
$content = $response->getContent();
$compressed = gzencode($content, 9);
return $response
->setContent($compressed)
->header('Content-Encoding', 'gzip')
->header('Content-Length', strlen($compressed));
}
}
المراقبة والتحليل
راقب أداء التطبيق وحدد الاختناقات:
// Laravel Telescope للتطوير
composer require laravel/telescope --dev
php artisan telescope:install
php artisan migrate
// الوصول على /telescope
// المراقبة: الطلبات، الأوامر، الاستعلامات، الوظائف، البريد، الإخطارات، الذاكرة المؤقتة
// Laravel Debugbar للتطوير
composer require barryvdh/laravel-debugbar --dev
// مراقبة الإنتاج مع Laravel Pulse
composer require laravel/pulse
php artisan vendor:publish --provider="Laravel\Pulse\PulseServiceProvider"
php artisan migrate
// config/pulse.php
'ingest' => [
'requests' => true,
'slow_queries' => true,
'exceptions' => true,
'queues' => true,
],
// تسجيل الأداء المخصص
use Illuminate\Support\Facades\Log;
$startTime = microtime(true);
// الكود الخاص بك هنا
$executionTime = microtime(true) - $startTime;
if ($executionTime > 1) {
Log::warning('تم اكتشاف عملية بطيئة', [
'execution_time' => $executionTime,
'action' => 'ProcessUserData',
]);
}
// تسجيل الاستعلامات في التطوير
DB::listen(function ($query) {
if ($query->time > 100) { // تسجيل الاستعلامات الأبطأ من 100ms
Log::warning('استعلام بطيء', [
'sql' => $query->sql,
'bindings' => $query->bindings,
'time' => $query->time,
]);
}
});
// APM (مراقبة أداء التطبيق)
// استخدم خدمات مثل: New Relic, Datadog, Scout APM
composer require scoutapp/scout-apm-laravel
قائمة أفضل ممارسات الإنتاج
// 1. تكوين البيئة
APP_ENV=production
APP_DEBUG=false
APP_URL=https://yourdomain.com
// 2. تخزين كل شيء مؤقتاً
php artisan optimize
php artisan view:cache
php artisan event:cache
// 3. استخدام عمال الطابور
QUEUE_CONNECTION=redis
php artisan queue:work --daemon
// 4. تمكين OPcache
// تحرير php.ini كما هو موضح سابقاً
// 5. استخدام Redis للذاكرة المؤقتة والجلسات
CACHE_DRIVER=redis
SESSION_DRIVER=redis
// 6. تحسين محمل Composer التلقائي
composer install --optimize-autoloader --no-dev
// 7. تعيين أذونات الملفات الصحيحة
sudo chown -R www-data:www-data storage bootstrap/cache
sudo chmod -R 775 storage bootstrap/cache
// 8. استخدام HTTPS
// تكوين شهادة SSL في خادم الويب
// 9. إعداد المراقبة
// تثبيت Laravel Pulse أو APM خارجي
// 10. الصيانة الدورية
php artisan schedule:run
php artisan queue:restart
php artisan telescope:prune
قائمة نشر الإنتاج:
- ✅ اضبط APP_ENV=production و APP_DEBUG=false
- ✅ شغّل php artisan optimize
- ✅ مكّن OPcache مع validate_timestamps=0
- ✅ استخدم Redis للذاكرة المؤقتة والجلسات
- ✅ شغّل عمال الطابور كعمليات خلفية
- ✅ حسّن محمل Composer التلقائي
- ✅ مكّن ضغط gzip
- ✅ استخدم CDN للأصول الثابتة
- ✅ أعدّ المراقبة والتسجيل
- ✅ كوّن النسخ الاحتياطية التلقائية
اختبار الأداء
// اختبار الحمل مع Apache Bench
ab -n 1000 -c 100 https://yourdomain.com/
// اختبار الحمل مع wrk
wrk -t12 -c400 -d30s https://yourdomain.com/
// Laravel Dusk لاختبار المتصفح
php artisan dusk
// قياس عمليات محددة
use Illuminate\Support\Benchmark;
Benchmark::dd([
'Scenario 1' => fn () => User::all(),
'Scenario 2' => fn () => User::cursor()->count(),
]);
// تحليل استخدام الذاكرة
$memory = memory_get_peak_usage(true) / 1024 / 1024;
echo "Peak memory: {$memory} MB";
// عدد استعلامات قاعدة البيانات
DB::connection()->enableQueryLog();
// الكود الخاص بك
$queries = DB::getQueryLog();
echo "Total queries: " . count($queries);
التمرين 1: تحسين تطبيق بطيء
بالنظر إلى تطبيق Laravel به مشاكل في الأداء:
- ثبّت Laravel Telescope وحدد الاستعلامات البطيئة
- أصلح مشاكل استعلام N+1 مع التحميل المسبق
- نفذ التخزين المؤقت في Redis للبيانات المتكررة الوصول
- أضف فهارس قاعدة البيانات للاستعلامات الشائعة
- خزن التكوين والمسارات والعروض مؤقتاً
- قِس التحسن مع معايير قبل/بعد
التمرين 2: نص نشر الإنتاج
أنشئ نص نشر آلي:
- اسحب أحدث كود من git
- شغّل composer install --optimize-autoloader --no-dev
- شغّل الترحيلات
- امسح وأعد بناء جميع الذاكرة المؤقتة
- أعد تشغيل عمال الطابور
- امسح OPcache
- شغّل اختبارات الدخان
- التراجع عند الفشل
التمرين 3: تنفيذ لوحة تحكم المراقبة
ابن نظام مراقبة أداء مخصص:
- تتبع متوسط وقت الاستجابة لكل نقطة نهاية
- راقب عدد ومدة استعلامات قاعدة البيانات
- سجّل العمليات البطيئة (>100ms)
- تتبع نسبة الإصابة/الإخفاق في الذاكرة المؤقتة
- راقب وقت معالجة وظائف الطابور
- أنشئ لوحة تحكم مسؤول تعرض المقاييس
- أعدّ تنبيهات لتدهور الأداء
الخلاصة
في هذا الدرس الشامل، تعلمت تقنيات تحسين الأداء الأساسية لتطبيقات Laravel. استكشفت التخزين المؤقت للتكوين والمسارات والعروض، وإعداد OPcache، وتحسين استعلامات قاعدة البيانات، والتخزين المؤقت في Redis، وتحسين الأصول، وضغط الاستجابة، واستراتيجيات المراقبة. تعلمت أيضاً أفضل ممارسات الإنتاج وقوائم النشر. هذه التقنيات، عند تطبيقها بشكل صحيح، يمكن أن تحسن أداء تطبيقك بمقدار 10-100 مرة، مما يؤدي إلى تحميل صفحات أسرع، وتكاليف خادم أقل، ومستخدمين أكثر سعادة.
تهانينا على إكمال برنامج Laravel Framework التعليمي! لقد أتقنت Laravel من الأساسيات إلى الموضوعات المتقدمة بما في ذلك التوجيه والمتحكمات والنماذج والمصادقة والتفويض والطوابير والأعلام المميزة وتحسين الأداء. أنت الآن مجهز لبناء تطبيقات ويب احترافية وقابلة للتطوير وعالية الأداء باستخدام Laravel.