تحليل الأداء والتحسين
تحليل الأداء والتحسين
أداء التطبيق أمر بالغ الأهمية لتجربة المستخدم وتكاليف الخادم. يغطي هذا الدرس أدوات التحليل في Laravel، تحديد الاختناقات، وتنفيذ تقنيات التحسين المتقدمة لبناء تطبيقات فائقة السرعة.
Laravel Telescope للرؤى العميقة
Telescope هي أداة Laravel الرسمية لتصحيح الأخطاء والتحليل التي توفر نظرة ثاقبة على الطلبات والاستثناءات واستعلامات قاعدة البيانات والوظائف المنتظرة والمزيد.
# تثبيت Telescope
composer require laravel/telescope --dev
# نشر الأصول والترحيلات
php artisan telescope:install
php artisan migrate
# التكوين في config/telescope.php
return [
'enabled' => env('TELESCOPE_ENABLED', true),
'storage' => [
'database' => [
'connection' => env('DB_CONNECTION', 'mysql'),
'chunk' => 1000,
],
],
'watchers' => [
Watchers\QueryWatcher::class => [
'enabled' => env('TELESCOPE_QUERY_WATCHER', true),
'slow' => 100, // تسجيل الاستعلامات الأبطأ من 100 مللي ثانية
],
Watchers\RequestWatcher::class => [
'enabled' => env('TELESCOPE_REQUEST_WATCHER', true),
'size_limit' => env('TELESCOPE_RESPONSE_SIZE_LIMIT', 64),
],
Watchers\ExceptionWatcher::class => true,
Watchers\MailWatcher::class => true,
Watchers\JobWatcher::class => true,
Watchers\CacheWatcher::class => true,
],
];
<?php
// التسجيل المخصص باستخدام Telescope
use Laravel\Telescope\Telescope;
Telescope::tag(function () {
return ['user:' . auth()->id()];
});
// تسجيل إدخالات مخصصة
Telescope::recordQuery([
'sql' => $sql,
'bindings' => $bindings,
'time' => $time,
'connection' => $connection,
]);
// تمكين Telescope بشكل مشروط
// app/Providers/TelescopeServiceProvider.php
protected function gate()
{
Gate::define('viewTelescope', function ($user) {
return in_array($user->email, [
'admin@example.com',
]) || $user->isAdmin();
});
}
// استبعاد المسارات من التسجيل
protected function hideSensitiveRequestDetails()
{
Telescope::hideRequestParameters(['password', 'password_confirmation']);
Telescope::hideRequestHeaders(['authorization', 'cookie']);
}
// الوصول إلى Telescope على /telescope
Laravel Debugbar للتحليل في الوقت الفعلي
توفر Debugbar شريط أدوات مع معلومات في الوقت الفعلي حول الطلب الحالي، بما في ذلك الاستعلامات والعروض والمسارات واستخدام الذاكرة.
# تثبيت Debugbar
composer require barryvdh/laravel-debugbar --dev
# نشر التكوين
php artisan vendor:publish --provider="Barryvdh\Debugbar\ServiceProvider"
# التكوين في config/debugbar.php
return [
'enabled' => env('DEBUGBAR_ENABLED', null),
'collectors' => [
'phpinfo' => true,
'messages' => true,
'time' => true,
'memory' => true,
'exceptions' => true,
'log' => true,
'db' => true,
'views' => true,
'route' => true,
'auth' => true,
'gate' => true,
'session' => true,
'symfony_request' => true,
'mail' => true,
'laravel' => true,
'events' => true,
'default_request' => true,
'logs' => true,
'files' => true,
'config' => false,
'cache' => true,
'models' => true,
],
];
<?php
// إضافة رسائل مخصصة إلى Debugbar
use Debugbar;
Debugbar::info('رسالة معلومات');
Debugbar::warning('رسالة تحذير');
Debugbar::error('رسالة خطأ');
// قياس وقت التنفيذ
Debugbar::startMeasure('complex-operation', 'عملية معقدة');
// ... الكود الخاص بك
Debugbar::stopMeasure('complex-operation');
// إضافة بيانات مخصصة
Debugbar::addMessage($data, 'custom-data');
// قياس أقسام محددة
$debugbar = app('debugbar');
$debugbar['time']->measure('my task', function() {
// مهمة معقدة
});
تحليل الاستعلامات واكتشاف N+1
مشكلة استعلام N+1 هي واحدة من أكثر مشاكل الأداء شيوعاً في التطبيقات التي تستخدم ORMs. توفر Laravel أدوات للكشف عن هذه المشاكل وإصلاحها.
<?php
// اكتشاف استعلامات N+1 مع تسجيل الاستعلامات
use Illuminate\Support\Facades\DB;
// تمكين سجل الاستعلامات
DB::enableQueryLog();
// الكود الذي قد يكون لديه مشاكل N+1
$posts = Post::all();
foreach ($posts as $post) {
echo $post->author->name; // مشكلة N+1!
}
// الحصول على جميع الاستعلامات
$queries = DB::getQueryLog();
dd($queries);
// استخدام Laravel Debugbar - يعرض تلقائياً الاستعلامات المكررة
// الحل: التحميل المسبق
$posts = Post::with('author')->get();
foreach ($posts as $post) {
echo $post->author->name; // استعلام واحد!
}
// منع التحميل الكسول في الإنتاج
// app/Providers/AppServiceProvider.php
use Illuminate\Database\Eloquent\Model;
public function boot()
{
Model::preventLazyLoading(!app()->isProduction());
// أو منع دائماً
Model::preventLazyLoading();
}
// سيرمي استثناءً إذا حدث تحميل كسول
// LazyLoadingViolationException
// عد العلاقات بكفاءة
// سيء - يحمل جميع التعليقات
$posts = Post::all();
foreach ($posts as $post) {
echo $post->comments->count();
}
// جيد - يستخدم SQL COUNT
$posts = Post::withCount('comments')->get();
foreach ($posts as $post) {
echo $post->comments_count;
}
withCount() بدلاً من تحميل العلاقات فقط لعدها. تحميل 1000 تعليق لعدها يعد هدراً شديداً مقارنة باستعلام COUNT بسيط.
تقنيات تحسين الذاكرة
إدارة الذاكرة أمر بالغ الأهمية عند العمل مع مجموعات البيانات الكبيرة. توفر Laravel عدة تقنيات لمعالجة البيانات بكفاءة دون نفاد الذاكرة.
<?php
// سيء - يحمل كل شيء في الذاكرة
$users = User::all(); // يمكن أن يتعطل مع 100 ألف+ مستخدم
foreach ($users as $user) {
// معالجة المستخدم
}
// جيد - التجزئة لمجموعات البيانات الكبيرة
User::chunk(200, function ($users) {
foreach ($users as $users) {
// معالجة 200 مستخدم في كل مرة
}
});
// أفضل - المجموعات الكسولة (Laravel 6+)
User::cursor()->each(function ($user) {
// معالجة مستخدم واحد في كل مرة
// يستخدم مولدات PHP، ذاكرة أدنى
});
// التجزئة مع التحديثات (يتجنب انجراف المؤشر)
User::chunkById(200, function ($users) {
foreach ($users as $user) {
$user->update(['processed' => true]);
}
});
// التجميعات الفعالة
// سيء
$total = Order::all()->sum('amount');
// جيد
$total = Order::sum('amount');
// صادرات فعالة للذاكرة
use Illuminate\Support\LazyCollection;
LazyCollection::make(function () {
$handle = fopen('large-file.csv', 'r');
while (($line = fgets($handle)) !== false) {
yield $line;
}
fclose($handle);
})->chunk(1000)->each(function ($chunk) {
// معالجة 1000 سطر في كل مرة
});
// مسح مجموعات النموذج عند الانتهاء
$users = User::all();
// معالجة المستخدمين
unset($users); // تحرير الذاكرة
cursor() لعمليات القراءة فقط على مجموعات البيانات الكبيرة. للعمليات التي تعدل السجلات، استخدم chunkById() لتجنب مشاكل انجراف المؤشر.
استراتيجيات التخزين المؤقت للاستجابة
يمكن أن يحسن التخزين المؤقت للاستجابات الأداء بشكل كبير من خلال تجنب الحسابات باهظة الثمن واستعلامات قاعدة البيانات.
<?php
// التخزين المؤقت البسيط
use Illuminate\Support\Facades\Cache;
public function dashboard()
{
$stats = Cache::remember('dashboard-stats', 3600, function () {
return [
'total_users' => User::count(),
'total_posts' => Post::count(),
'revenue' => Order::sum('amount'),
];
});
return view('dashboard', compact('stats'));
}
// وسوم التخزين المؤقت لإدارة منظمة (Redis/Memcached فقط)
Cache::tags(['users', 'posts'])->put('key', $value, 3600);
Cache::tags(['users'])->flush(); // مسح جميع التخزين المؤقت المتعلق بالمستخدمين
// التخزين المؤقت الكامل لاستجابة HTTP
Route::get('/api/posts', function () {
return cache()->remember('api.posts', 600, function () {
return Post::with('author')->latest()->get();
});
});
// إبطال التخزين المؤقت عند تغييرات النموذج
class Post extends Model
{
protected static function booted()
{
static::created(function () {
Cache::forget('api.posts');
});
static::updated(function () {
Cache::forget('api.posts');
});
static::deleted(function () {
Cache::forget('api.posts');
});
}
}
// استخدام مراقبي النموذج لإبطال التخزين المؤقت
// app/Observers/PostObserver.php
class PostObserver
{
public function saved(Post $post)
{
Cache::tags(['posts'])->flush();
}
public function deleted(Post $post)
{
Cache::tags(['posts'])->flush();
}
}
// التخزين المؤقت للعروض
// تخزين العروض المقدمة مؤقتاً
php artisan view:cache
// مسح التخزين المؤقت للعروض
php artisan view:clear
// التخزين المؤقت للمسارات (تعزيز كبير للأداء)
php artisan route:cache
// التخزين المؤقت للتكوين
php artisan config:cache
// تحسين المحمل التلقائي (الإنتاج)
composer install --optimize-autoloader --no-dev
التحميل الكسول والتحميل المؤجل
يمكن أن يؤدي التحميل الكسول للعلاقات وتأجيل العمليات المكلفة إلى تحسين أوقات تحميل الصفحة الأولية بشكل كبير.
<?php
// التحميل المسبق الكسول - تحميل العلاقات بشكل مشروط
$posts = Post::all();
if ($someCondition) {
$posts->load('comments');
}
// تحميل العلاقات المتداخلة
$posts->load('comments.author');
// تحميل علاقات متعددة
$posts->load(['author', 'comments', 'tags']);
// التحميل الكسول مع القيود
$posts->load([
'comments' => function ($query) {
$query->where('approved', true)->orderBy('created_at', 'desc');
}
]);
// استعلامات الوجود - أكثر كفاءة من التحميل
// سيء
if ($post->comments->count() > 0) {
// لديه تعليقات
}
// جيد
if ($post->comments()->exists()) {
// لديه تعليقات
}
// موفرو الخدمة المؤجلون
// app/Providers/DeferredServiceProvider.php
use Illuminate\Contracts\Support\DeferrableProvider;
class DeferredServiceProvider extends ServiceProvider implements DeferrableProvider
{
public function register()
{
$this->app->singleton(ExpensiveService::class, function ($app) {
return new ExpensiveService();
});
}
public function provides()
{
return [ExpensiveService::class];
}
}
// اختر فقط الأعمدة المطلوبة
// سيء
$users = User::all(); // يحمل جميع الأعمدة
// جيد
$users = User::select(['id', 'name', 'email'])->get();
// مع العلاقات
$posts = Post::with(['author:id,name'])->get();
// أعمدة JSON - استخرج فقط البيانات المطلوبة
// سيء
$user->settings; // كائن JSON بالكامل
// جيد
$darkMode = $user->settings->theme->dark_mode;
// أو استخدم accessor
public function getDarkModeAttribute()
{
return $this->settings['theme']['dark_mode'] ?? false;
}
تمرين 1: لوحة تحكم الأداء
أنشئ لوحة تحكم مراقبة أداء شاملة تعرض:
- متوسط وقت الاستجابة لآخر 24 ساعة (مجمع حسب الساعة)
- أبطأ نقاط النهاية مع عدد الاستعلامات
- اتجاهات استخدام الذاكرة
- نسب إصابة/فقد التخزين المؤقت
- أبطأ 10 استعلامات قاعدة بيانات
- أوقات معالجة وظائف قائمة الانتظار
- تنفيذ التحديث التلقائي كل 30 ثانية باستخدام Livewire أو Vue
تمرين 2: تدقيق تحسين الاستعلامات
ابنِ أداة سطر أوامر:
- تفحص جميع نماذج Eloquent بحثاً عن مشاكل N+1 المحتملة
- تحدد فهارس قاعدة البيانات المفقودة بناءً على الاستعلامات الشائعة
- تكتشف الاستعلامات غير الفعالة (SELECT *، عبارات WHERE المفقودة على الجداول الكبيرة)
- تقترح فرص التحميل المسبق
- تولد تقريراً مع توصيات تحسين قابلة للتنفيذ
- تطبق الإصلاحات الآمنة تلقائياً (إضافة فهارس) اختيارياً
تمرين 3: نظام تخزين مؤقت ذكي
طور نظام تخزين مؤقت متقدم مع:
- توليد مفتاح تخزين مؤقت تلقائي بناءً على معاملات الطلب
- إبطال تخزين مؤقت ذكي باستخدام مراقبي النموذج والوسوم
- استراتيجية تسخين التخزين المؤقت للبيانات المُستخدمة بشكل متكرر
- اختبار A/B لقياس تأثير أداء التخزين المؤقت
- واجهة مسؤول لعرض إحصائيات التخزين المؤقت ومسحه يدوياً
- TTL قابل للتكوين بناءً على نوع البيانات وتكرار التحديث
- تنفيذ منع تدافع التخزين المؤقت باستخدام الأقفال
قائمة تحقق أداء الإنتاج
# التحسينات الأساسية للإنتاج
# 1. تخزين التكوين والمسارات مؤقتاً
php artisan config:cache
php artisan route:cache
php artisan view:cache
# 2. تحسين المحمل التلقائي لـ Composer
composer install --optimize-autoloader --no-dev
# 3. تمكين OPcache في php.ini
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0
# 4. استخدام عمال قائمة الانتظار للمهام غير المتزامنة
php artisan queue:work --sleep=3 --tries=3 --max-time=3600
# 5. تنفيذ Redis للتخزين المؤقت والجلسات
CACHE_DRIVER=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redis
# 6. تمكين HTTP/2 و gzip في خادم الويب الخاص بك
# 7. استخدام CDN للأصول الثابتة
# 8. تنفيذ تجميع اتصال قاعدة البيانات
# 9. المراقبة باستخدام أدوات APM (New Relic، Datadog، إلخ.)
# 10. التحليل المنتظم باستخدام Telescope/Debugbar في التدريج