العودة إلى المدونة

حيل أداء Eloquent: جعل Laravel يطير مع آلاف السجلات

2024-02-05 * 9 دقائق قراءة min de lecture Web Development
عندما تبدأ مع Laravel، يبدو Eloquent كالسحر. بضعة أسطر من الكود، ويمكنك الاستعلام عن قاعدة البيانات الخاصة بك ببناء جملة جميل ومعبر. ولكن مع نمو مشروعك وبدء التعامل مع آلاف (أو حتى ملايين) الصفوف، يمكن أن يتحول هذا السحر فجأة إلى استعلامات بطيئة وارتفاعات في الذاكرة وصفحات متأخرة. الأخبار الجيدة؟ يمكن لـ Eloquent ORM في Laravel أن يتوسع بشكل جيد حقاً - إذا كنت تعرف بعض حيل الأداء. في هذه المقالة، سأشارك التقنيات التي ساعدتني وفريقي على تحسين التطبيقات واسعة النطاق دون التخلي عن أناقة Eloquent.

1. استخدم select() لاسترداد ما تحتاجه فقط

افتراضياً، يسترد Eloquent جميع الأعمدة (SELECT *) من قاعدة البيانات الخاصة بك. للجداول الصغيرة، هذا جيد. ولكن مع الجداول الكبيرة والمخططات الواسعة، هذا يهدر الذاكرة ويبطئ الاستعلامات.
// ❌ تجنب: يسحب جميع الأعمدة
$users = User::all();

// ✅ أفضل: جلب الحقول المطلوبة فقط
$users = User::select('id', 'name', 'email')->get();
👉 هذا التغيير الصغير يمكن أن يوفر ميجابايت من الذاكرة في الاستعلامات الكبيرة.

2. قم بمعالجة الاستعلامات الكبيرة على دفعات

عندما تحتاج إلى معالجة آلاف السجلات، لا تحملها كلها مرة واحدة. استخدم المعالجة على دفعات لمعالجتها في مجموعات.
User::chunk(500, function ($users) {
    foreach ($users as $user) {
        // معالجة المستخدم
    }
});
بدلاً من سحب 100k صف في الذاكرة، هذا يأخذ 500 في المرة الواحدة. Laravel يتولى الترقيم في الخلفية. إذا كان الترتيب مهم (مثلاً، للمعالجة المتسقة على دفعات)، لا تنس الترتيب حسب id.

3. استخدم lazy() أو cursor() للتدفق

إذا كنت تعمل مع مجموعات بيانات ضخمة حقاً، قد تستهلك المعالجة على دفعات الكثير من الذاكرة. في هذه الحالة، استخدم المؤشرات:
foreach (User::cursor() as $user) {
    // تدفق عبر السجلات واحداً تلو الآخر
}
هذا يتجنب تحميل الدفعات الكبيرة في الذاكرة، ويتدفق بالصفوف مباشرة من قاعدة البيانات. إنه أبطأ لكل صف ولكنه فعال جداً في الذاكرة.

4. حمّل العلاقات مسبقاً (لكن لا تفرط في التحميل)

مشكلة الاستعلام N+1 هي واحدة من أكبر قتلة الأداء في تطبيقات Laravel.
// ❌ سيء: يشغل استعلام جديد لكل منشور
$posts = Post::all();
foreach ($posts as $post) {
    echo $post->user->name;
}

// ✅ جيد: حمّل المستخدمين مسبقاً
$posts = Post::with('user')->get();
هذا يقلل العشرات من الاستعلامات إلى اثنين فقط. ⚠️ لكن لا تحمل كل شيء مسبقاً. حمّل فقط العلاقات التي تحتاجها حقاً، وإلا ستطغى على الذاكرة ببيانات غير مستخدمة.

5. فهرس أعمدة قاعدة البيانات الخاصة بك

أحياناً، الاختناق ليس Eloquent - إنه قاعدة البيانات نفسها. إضافة الفهارس المناسبة يمكن أن تقطع أوقات الاستعلام من ثوانٍ إلى ميلي ثوانٍ.
Schema::table('users', function (Blueprint $table) {
    $table->index('email');
    $table->index(['status', 'created_at']);
});
👉 فهرس دائماً الأعمدة التي تُستخدم بشكل متكرر في شروط WHERE أو ORDER BY أو JOIN.

6. استخدم الترقيم بدلاً من جلب كل شيء

لل قوائم واللوحات، لا تحمل آلاف السجلات مرة واحدة أبداً. بدلاً من ذلك، رقمها.
$users = User::paginate(50);
هذا يعطيك روابط ترقيم نظيفة ويبقي استخدام الذاكرة صغيراً. إذا كنت تحتاج تمرير لا نهائي، simplePaginate() أسرع حتى.

7. استخدم update() و delete() بكميات كبيرة

إذا كنت تحتاج إلى تحديث أو حذف الكثير من السجلات، لا تعبرها في حلقة. افعلها في استعلام واحد.
// ❌ سيء: يحدث صفاً بصف
foreach ($users as $user) {
    $user->update(['active' => false]);
}

// ✅ جيد: تحديث بكمية كبيرة واحد
User::where('active', true)->update(['active' => false]);
هذا يتجنب آلاف الاستعلامات ويجعل تطبيقك يطير.

8. استخدم التخزين المؤقت عندما يكون منطقياً

إذا كنت تشغل نفس الاستعلام الثقيل بشكل متكرر، فكر في تخزين النتائج مؤقتاً.
$users = Cache::remember('active_users', 600, function () {
    return User::where('active', true)->get();
});
Laravel يجعل هذا سهلاً، والتخزين المؤقت يمكن أن يحول استعلام DB بطيء إلى بحث ذاكرة سريع.

9. تحليل الاستعلامات

لا تخمن أين هي الاختناقات. استخدم DB::enableQueryLog() أو Laravel Telescope لتحليل الاستعلامات.
DB::enableQueryLog();
$users = User::where('status', 'active')->get();
dd(DB::getQueryLog());
أو استخدم toSql() لرؤية ما يولده Eloquent:
dd(User::where('status', 'active')->toSql());

10. عند الحاجة... انزل إلى الاستعلامات الخام

Eloquent أنيق، لكن أحياناً تحتاج أداء خام. Laravel يجعل من السهل العودة إلى منشئ الاستعلامات أو حتى SQL خام.
// منشئ الاستعلامات
$users = DB::table('users')->where('status', 'active')->get();

// SQL خام
$users = DB::select('SELECT id, name FROM users WHERE status = ?', ['active']);
خلط الأساليب ليس فشلاً - إنه هندسة عملية.

Conclusion

Eloquent ORM في Laravel ليس فقط للمشاريع الصغيرة. مع الحيل الصحيحة - من المعالجة على دفعات والمؤشرات، إلى التحميل المسبق والتحديثات بكميات كبيرة - يمكنك التعامل مع ملايين الصفوف بكفاءة دون التخلي عن القابلية للقراءة والصيانة التي تجعل Laravel مميزاً. في نهاية المطاف، تحسين الأداء في Laravel ليس حول التخلي عن Eloquent. إنه حول فهم نقاط قوته، وتجنب مآزقه، ومعرفة متى تخلط القوة الخام. ونعم، حتى بعد كل هذه السنوات، ما زلت أحب البرمجة بـ Laravel. لقد نما معي من مشاريعي الجانبية الأولى إلى دوري كـ CTO اليوم. تماماً مثل PHP نفسه، يرفض الموت - وبصراحة، أنا بخير مع ذلك. 🚀