اجعل تطبيقك قابلاً للتطوير من خلال تحسين أداء ORM

تم إعداد ترجمة المقال عشية بدء الدورة التدريبية "Backend developer in PHP" .










مرحبا! أنا فاليريو ، مطور إيطالي ورئيس قسم التكنولوجيا بالمنصة Inspector.dev.



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



أنا متأكد من أن كل واحد منا كان عليه أن يشتكي من أن الخادم أو التطبيق يعمل ببطء (أو حتى لا يعمل على الإطلاق) ، وأثناء قضاء الوقت في آلة القهوة ، في انتظار نتائج طلب طويل.



كيف تصلحها؟

هيا نكتشف!



قاعدة البيانات هي مورد مشترك



لماذا تسبب قاعدة البيانات الكثير من مشاكل الأداء؟

غالبًا ما ننسى أنه لا يوجد استعلام مستقل عن الآخرين.

نعتقد أنه حتى لو كان بعض الاستعلام بطيئًا ، فإنه لا يؤثر على الآخرين ... لكن هل هو كذلك حقًا؟



قاعدة البيانات هي مورد مشترك تستخدمه جميع العمليات التي يتم تشغيلها في تطبيقك. حتى طريقة واحدة سيئة التصميم للوصول إلى قاعدة البيانات يمكن أن تعطل أداء النظام بأكمله.



لذلك ، لا تنس العواقب المحتملة ، التفكير: "لا بأس أن هذا الجزء من التعليمات البرمجية لم يتم تحسينه!" يمكن أن يؤدي الوصول البطيء إلى قاعدة البيانات إلى التحميل الزائد ، وهذا بدوره يمكن أن يؤثر سلبًا على تجربة المستخدم.



مشكلة استعلام قاعدة بيانات N + 1



ما هي مشكلة N + 1؟



هذه مشكلة شائعة عند استخدام ORM للتفاعل مع قاعدة بيانات. لا يتعلق الأمر بكتابة كود SQL.



عند استخدام نظام ORM مثل Eloquent ، ليس من الواضح دائمًا ما هي الاستعلامات التي سيتم تشغيلها ومتى. في سياق هذه المشكلة بالذات ، لنتحدث عن العلاقات والتحميل الشغوف.



يسمح لك أي نظام ORM بإعلان العلاقات بين الكيانات ويوفر واجهة برمجة تطبيقات ممتازة للتنقل في بنية قاعدة البيانات الخاصة بك.

يوجد أدناه مثال جيد لكيانات "المقالة" و "المؤلف".



/*
 * Each Article belongs to an Author
 */
$article = Article::find("1");
echo $article->author->name; 
/*
 * Each Author has many Articles
 */
foreach (Article::all() as $article)
{
    echo $article->title;
}


ومع ذلك ، عند استخدام العلاقات داخل حلقة ، يجب عليك كتابة التعليمات البرمجية بعناية.



ألق نظرة على المثال أدناه.



نريد إضافة اسم المؤلف بجانب عنوان المقالة. باستخدام ORM ، يمكنك الحصول على اسم المؤلف باستخدام علاقة رأس برأس بين المقالة والمؤلف.



يبدو أن كل شيء بسيط:



// Initial query to grab all articles
$articles = Article::all();
foreach ($articles as $article)
{
    // Get the author to print the name.
    echo $article->title . ' by ' . $article->author->name;
}


ولكن بعد ذلك وقعنا في الفخ!



تنشئ هذه الحلقة طلبًا أوليًا واحدًا للحصول على جميع المقالات:



SELECT * FROM articles;


و N المزيد من الاستعلامات للحصول على مؤلف كل مقال وعرض قيمة حقل "الاسم" ، حتى لو كان المؤلف دائمًا هو نفسه.



SELECT * FROM author WHERE id = [articles.author_id]


نتلقى طلبات N + 1 بالضبط.



قد لا يبدو هذا مشكلة كبيرة. حسنًا ، دعنا نقدم خمسة عشر أو عشرين طلبًا إضافيًا - ليس بالأمر المهم. ومع ذلك ، دعنا نعود إلى الجزء الأول من هذه المقالة:



  • — , .
  • , , .
  • , .


:



وفقًا لمستندات Laravel ، هناك فرصة جيدة أن تواجه مشكلة الاستعلام N + 1 ، لأنه عندما تصل إلى علاقات Eloquent كخصائص ( $article->author) ، يتم تحميل بيانات العلاقة كسولًا.



وهذا يعني أنه لا يتم تحميل بيانات العلاقة حتى تقوم بالوصول إلى الخاصية لأول مرة.



ومع ذلك ، باستخدام طريقة بسيطة ، يمكننا تحميل جميع بيانات العلاقة مرة واحدة. بعد ذلك ، عند الوصول إلى علاقة Eloquent كخاصية ، لن يقوم نظام ORM بتنفيذ استعلام جديد لأن البيانات قد تم تحميلها بالفعل.



يُطلق على هذا التكتيك "التحميل الحثيث" وتدعمه جميع أجهزة إدارة السجلات (ORM).



// Eager load authors using "with".
$articles = Article::with('author')->get();
foreach ($articles as $article)
{
    // Author will not run a query on each iteration.
    echo $article->author->name;
}


يوفر Eloquent طريقة with()لتحميل العلاقات بشغف.



في هذه الحالة ، سيتم تنفيذ استعلامين فقط.

مطلوب أول واحد لتنزيل جميع المقالات:



SELECT * FROM articles;


سيتم تنفيذ الثاني بواسطة التابع with()وسيجلب جميع المؤلفين:



SELECT * FROM authors WHERE id IN (1, 2, 3, 4, ...);


ستعمل آلية Eloquent الداخلية على تعيين البيانات ويمكن الوصول إليها بالطريقة المعتادة:



$article->author->name;


تحسين المشغلين لديك select



لفترة طويلة ، اعتقدت أن الإعلان صراحة عن عدد الحقول في استعلام جلب لا يؤدي إلى تحسين كبير في الأداء ، لذلك من أجل البساطة حصلت على جميع الحقول في استفساراتي.



كما أن التحديد الصارم لقائمة الحقول في عبارة تحديد محددة يجعل من الصعب الحفاظ على مثل هذا الجزء من الكود.



أكبر مأزق في هذه الحجة هو أنه من منظور قاعدة البيانات ، قد يكون هذا صحيحًا بالفعل.



ومع ذلك ، نحن نعمل مع ORM ، لذلك سيتم تحميل البيانات المحددة من قاعدة البيانات في الذاكرة على جانب PHP حتى يتمكن نظام ORM من إدارتها بشكل أكبر. كلما تم التقاط المزيد من الحقول ، زادت مساحة الذاكرة التي ستستغرقها العملية.



يوفر Laravel Eloquent التابع select لقصر الاستعلام على الأعمدة التي نحتاجها فقط:



$articles = Article::query()
    ->select('id', 'title', 'content') // The fields you need
    ->latest()
    ->get();


من خلال استبعاد الحقول ، لا يتعين على مترجم PHP معالجة البيانات غير الضرورية ، لذلك يمكنك تقليل استهلاك الذاكرة بشكل كبير.



يمكن أن يؤدي تجنب الجلب الكامل أيضًا إلى تحسين أداء الفرز والتجميع والدمج ، حيث يمكن لقاعدة البيانات نفسها توفير الذاكرة نتيجة لذلك.



استخدم طرق العرض في MySQL



طرق العرض عبارة عن استعلامات SELECT تستند إلى جداول أخرى ويتم تخزينها في قاعدة البيانات.



عندما نحدد جدولًا واحدًا أو أكثر ، تقوم قاعدة البيانات أولاً بتجميع عبارة SQL الخاصة بنا ، وتتأكد من خلوها من الأخطاء ، ثم تقوم بجلب البيانات.



العرض هو عبارة SELECT مترجمة مسبقًا ، والتي عند معالجتها ، تنفذ MySQL الاستعلام الداخلي الأساسي للعرض على الفور.



أيضًا ، عادةً ما تكون MySQL أكثر ذكاءً من PHP عندما يتعلق الأمر بتصفية البيانات. هناك مكاسب كبيرة في الأداء عند استخدام طرق العرض حول استخدام وظائف PHP لمعالجة المجموعات أو المصفوفات.



إذا كنت ترغب في معرفة المزيد حول إمكانيات MySQL لتطوير تطبيقات كثيفة قواعد البيانات ، تحقق من هذا الموقع الرائع: www.mysqltutorial.org



اربط نموذج Eloquent بعرض



يُشار إلى طرق العرض أيضًا باسم "الجداول الافتراضية". من وجهة نظر ORM ، تبدو مثل الجداول العادية.



لذلك ، يمكنك إنشاء نموذج Eloquent للاستعلام عن البيانات الموجودة في العرض.



class ArticleStats extends Model
{
    /**
     * The name of the view is the table name.
     */
    protected $table = "article_stats_view";
    /**
     * If the resultset of the View include the "author_id"
     * we can use it to retrieve the author as normal relation.
     */
    public function author()
    {
        return $this->belongsTo(Author::class);
    }
}


العلاقات تعمل كالمعتاد ، مثل الإكراه ، ترقيم الصفحات ، وما إلى ذلك. ولا توجد عقوبة على الأداء.



خاتمة



آمل أن تساعدك هذه النصائح في تطوير برامج أكثر موثوقية وقابلة للتطوير.



تمت كتابة جميع أمثلة التعليمات البرمجية باستخدام Eloquent كـ ORM ، ولكن ضع في اعتبارك أن هذه الاستراتيجيات تعمل بنفس الطريقة مع جميع ORMs الرئيسية.



كما أقول كثيرًا ، نحن بحاجة إلى أدوات لتنفيذ استراتيجيات فعالة. وإذا لم تكن هناك استراتيجية ، فلا يوجد ما يمكن الحديث عنه.



شكرا جزيلا لقراءة المقال حتى النهاية. إذا كنت تريد معرفة المزيد عن المفتش ، فأنا أدعوك إلى موقعنا على الإنترنت www.inspector.dev . لا تتردد في الكتابة إلى الدردشة إذا كان لديك أي أسئلة!



نشرت سابقًا هنا: www.inspector.dev/make-your-application-scalable-optimizing-the-orm-performance





اقرأ أكثر:






All Articles