هندسة أداء التطبيقات
أداء Laravel والتوسع
تطبيقات تصمد تحت الحمل - مقيسة من الإنتاج لا من حاسوب محمول، مصلَّحة عند السبب، ومحميّة بميزانيات في التكامل المستمر كي تبقى الأرقام ثابتة.
لعمل الأداء نهاية يمكن توقعها. تُخصَّص دورة للتحسين، وتنزل الرسوم البيانية، وبعد شهر ونصف تعود إلى حيث بدأت تمامًا. لم تكن الإصلاحات خاطئة. لم يكن هناك ما يثبّتها في مكانها.
كيف يجري عمل الأداء في Laravel
القياس أولًا، من الإنتاج. حركة حقيقية، بعيّنات، مقسّمة حسب نقطة النهاية. أن يكون المئين الخامس والتسعون لقائمة الطلبات 2.4 ثانية عبارة قابلة للتنفيذ؛ أما "التطبيق يبدو بطيئًا" فليست كذلك، ولا كذلك ملف تعريف مأخوذ على جهاز مطوّر مقابل قاعدة بيانات مزروعة.
إيجاد السبب لا العَرَض. نقطة النهاية البطيئة في Laravel تكاد تكون دائمًا واحدة من خمسة: عدد الاستعلامات، أو استعلام واحد بلا فهرس صالح، أو عمل كان ينبغي وضعه في طابور، أو استدعاء طرف ثالث في مسار الطلب، أو تسلسل بيانات أكثر بكثير مما تحتاجه الاستجابة. نتتبع كل واحد إلى السطر الذي أنتجه.
الإصلاح عند المستوى الصحيح. نقل العمل إلى مهمة، وإضافة الفهرس الذي تريده خطة الاستعلام فعلًا، وتحميل العلاقة مسبقًا، واستبدال خط معالجة مجموعات بتجميع، وتخزين قيمة تبرر كلفتُها فعلًا مشكلةَ الإبطال التي تخلقها. هذه هي التغييرات التي تصمد.
حماية النتيجة. ميزانية في التكامل المستمر: تأكيد على عدد الاستعلامات في نقاط النهاية المهمة، وبناء فاشل لطلب الدمج الذي يتجاوزها. الميزانية هي المُخرَج؛ والرسم البياني أثر جانبي.
لماذا يعود الرسم البياني
لأن الرسم البياني نتيجة ولا أحد يملك نتيجة. أما الميزانية فرقم في اختبار، يفحصه حاسوب، في كل طلب دمج، ويُفشل بناء من تجاوزه وهو لا يزال يتذكر ما غيّره.
لهذا لا يحتاج أغلب العملاء إلينا مرة ثانية في هذا. التحسين بلا ميزانية اقتراضٌ على حساب الربع القادم: العلاقة نفسها تفقد تحميلها المسبق بهدوء، والتقرير نفسه يكتسب عمودًا إضافيًا يتبين أنه accessor وراءه استعلام، وتصل الانتكاسة إيداعًا معقولًا تلو آخر، من أشخاص لا سبيل لديهم لمعرفة ذلك.
أين يذهب الوقت فعلًا في طلب Laravel
مرتبًا بحسب تكرار كونه الجواب:
قاعدة البيانات. عدد الاستعلامات عادةً لا أي استعلام بعينه - ولهذا يكون تعاقدًا قائمًا بذاته حين يكون المشكلة كلها لا جزءًا منها.
عمل لا ينبغي أن يكون في الطلب. ملف PDF يُرسَم، وصورة يُعاد تحجيمها، وwebhook يُسلَّم، وتقرير يُولَّد. كل واحد من هذه ينتمي إلى طابور، ونقلها عادةً أكبر تحسين منفرد متاح.
طرف ثالث في المسار الحرج. بحث عن عنوان، أو خدمة ضرائب، أو استدعاء حالة لدى مزوّد دفع. زمن استجابتهم هو زمن استجابتك، وانقطاعهم انقطاعك، ولا يظهر أي منهما في تحليلك حتى تبحث عنه.
التسلسل. نقطة نهاية تعيد مئتي حقل لأن المورد كُتب على أنه "كل شيء في النموذج"، فيُهيّئ علاقات لينتج قائمة أسماء.
التخزين المؤقت كلصقة. التخزين المؤقت ليس مجانيًا - يشتري السرعة بقِدَم البيانات، والتطبيق الذي فيه عشرات المفاتيح غير المترابطة بلا استراتيجية إبطال قد قايض مشكلة أداء بمشكلة صحة.
التوسع، بعد أن يصير كفؤًا
عندها فقط تكون البنية التحتية هي الجواب، والترتيب مهم: تطبيق غير كفؤ يُوسَّع أفقيًا هو تطبيق غير كفؤ تدفع ثمنه عدة مرات.
وحين يكون مبرَّرًا، فالعمل نسخ قراءة بتوجيه يُقرَّر صراحةً لا عالميًا، ومخازن جلسات وتخزين مؤقت ليست قاعدة البيانات الأساسية، وعمّال طوابير يُوسَّعون بمعزل عن سعة الويب لأنهم يفشلون بشكل مختلف، ونشرٌ لا يُسقِط العمل الجاري. وOctane حيث يقول القياس إن إقلاع الإطار يشكّل فعلًا نسبة معتبرة من الطلب، وهو أقل تكرارًا مما توحي به سمعته.
أين تكلّف تسهيلات Laravel نفسها بصورة قابلة للقياس
الكتابة في الإطار ممتعة، ولحفنة من الأشياء التي تجعلها ممتعة ثمنٌ لا يظهر إلا مع الحجم. ولا شيء من هذا حجةٌ ضدّ استخدامها - إنه معرفة أين تنظرون حين تسوء الأرقام.
مجموعة حيث كان الاستعلام هو المكان. Model::all() يتبعه تصفية يُحمّل كل صف
في الذاكرة ليطرح أكثرها. عند عشرة آلاف صف هو نقطة نهاية بطيئة، وعند مليون هو
عملية تُقتَل. و chunk و cursor موجودان للحالات التي تحتاج كل صف فعلًا.
مُلحَق خلفه استعلام. سمة محسوبة تمتدّ إلى علاقة غير مرئية في النموذج ومدمّرة في حلقة - قائمة بمئتي صف تناديها مرة لكل صف هي مئتا استعلام لا يصلحها أي eager load، لأن الاستعلام داخل المُلحَق لا في العلاقة.
أحداث النموذج في عملية جماعية. update() على باني الاستعلام يتخطّى
الأحداث كليًا، وهذا خطأ إن كان شيء يعتمد عليها؛ والتكرار ومناداة save()
يُطلِقها كلها، وهو السلوك الصحيح وألفا استعلام. والاختيار عن قصد، وكتابة ما
تحتاجه هذه العملية، هو الإصلاح كلّه.
تحديد كل شيء. with('orders') على قائمة تُظهِر عددًا يُحمّل كل طلب ليعدّها.
و withCount، أو تحديد الأعمدة التي تستخدمها الواجهة، يحوّل حمولة تُقاس
بالميغابايت إلى واحدة تُقاس بالكيلوبايت - وتسلسل بيانات لا يعرضها شيء حصةٌ
مفاجئة من ردٍّ بطيء.
تقرير Laravel الذي يجب أن يكون سريعًا ولا يُمكِن تخزينه
بعض الأرقام لا يجوز أن تكون قديمة ولا يمكن حسابها عند الطلب، وذلك الاقتران هو حيث تسقط معظم اللوحات.
الشكل المعتاد شاشةٌ تجمع عبر عدة جداول كبيرة، يفتحها عشرون شخصًا كل صباح، وعليها أن تعكس ما حدث في الليل. حسابها لكل طلب يعني استعلامًا من ثلاثين ثانية يُشغَّل عشرين مرة. وتخزينها ساعةً يعني أن أحدهم يتّخذ قرارًا على رقم خاطئ، وهذا أسوأ من البطء.
وما ينجح هو التجميع المسبق في جدول خاص، يُصان تدريجيًا لا يُعاد حسابه. تُكتَب مجاميع كل فترة مرة واحدة عند اكتمال الفترة، وتُشتَق الفترة الحالية من نطاق أصغر كثيرًا، وتقرأ الشاشة صفوفًا بدل أن تمسح التاريخ. فيحدث العمل المكلف على جدول، بعيدًا عن انتباه أحد، وتصير اللوحة عملية بحث.
والتدريجية هي الجزء الذي يحتاج حكمًا، لأن سجلًا يصل متأخرًا يجب أن يستطيع تصحيح مجموع كُتِب بالفعل. ما يعني أن المُجمَّع يعرف أي المُدخَلات حسبها ويمكن إعادة اشتقاقه لفترة لا أن يُضاف إليه فقط - الترتيب نفسه الذي يجعل إعادة التصريح قابلة للتفسير لا غامضة.
ونسخة القراءة المتماثلة تساعد في الحمل المتبقّي وتجلب تحذيرها الخاص الذي يستحق الإفصاح - تأخّر التزامن يعني أن رقمًا يُقرأ من نسخة متماثلة قد يتأخّر ثوانٍ عن الكتابة التي أنتجته. وهذا مقبول عادةً لتقرير. أما للشاشة التي يهبط عليها أحدهم فور الحفظ، فهو يُنتِج تقرير علّة عن بيانات اختفت.
ماذا تتسلّم من عمل أداء Laravel وتوسّعه
وثيقة اكتشافات مرتَّبة بالأولوية، كل مسألة فيها متتبَّعة إلى سببها ومقدَّرة بالجهد، والإصلاحات المنفَّذة كطلبات دمج قابلة للمراجعة، وإعداد ميزانية التكامل المستمر، ومقارنة قبل/بعد مقابل حركة الإنتاج بعد أن يعيش التغيير مدة كافية ليعني شيئًا.
وحيث يتبين أن السقف هو المعمارية نفسها - وهذا يحدث - تقول الاكتشافات ذلك، وتضع رقمًا للبديل بجانبه. تلك محادثة تُجرى في الأسبوع الأول لا في الملخص الختامي.
