قبل از eager loading، اندازهگیری کن
خلاصهٔ کاملتر
نویسنده با یه مثال آشنا شروع میکنه: داشبورد تیم که موقع توسعه سریع بود، شش ماه بعد که یه مشتری هزاران پروژه و سالها تاریخچهٔ تسک داره، بیشتر وقتش رو منتظر دیتابیس میمونه. یکی میگه with() اضافه کن، یکی میگه جواب رو کش کن. به گفتهٔ نویسنده هر دو ممکنه کمک کنن، ولی هیچکدوم نمیگه اپ وقتش رو کجا میگذرونه. مثالها روی Laravel 13 نوشته شدن و پلن کوئریها روی PostgreSQL گرفته شده.
داشبورد ۵۰ پروژهٔ آخر تیم رو با اسم مالک، تعداد تسکهای باز و اینکه تسک عقبافتاده داره یا نه نشون میده. با lazy loading این میشه ۱۰۱ کوئری: یکی برای پروژهها، ۵۰ تا برای مالکها و ۵۰ تا برای کالکشن تسکها. بدتر اینکه همهٔ تسکها میان داخل PHP فقط برای اینکه تسکهای باز شمرده بشن؛ پروژهای با ۸۰ هزار تسک تمومشده همین کارو خیلی گرون میکنه، حتی اگه آخرش سه تسک باز نشون داده بشه.
نویسنده اینجا دو مشکل جدا رو از هم تفکیک میکنه: تعداد زیاد رفتوبرگشت به دیتابیس، و حجم زیاد دادهای که میاد. with(['owner', 'tasks']) فقط اولی رو حل میکنه و معمولاً کوئریها رو به سه تا میرسونه، ولی هنوز کل تاریخچهٔ تسک از روی کانکشن رد میشه و تبدیل به آبجکت PHP میشه. راهحل سوم اینه که فقط چیزی که نمایش داده میشه لود بشه: مدل مالک بهصورت رابطه، ولی شمارش و بولین بهشکل aggregate که محاسبهش سمت دیتابیس بمونه.
قبل از عوضکردن کوئری هم باید اندازهگیری کرد، با یه تیم کوچک و یه تیم بزرگ و با نگهداشتن همهٔ شرایط دیگه ثابت. چیزهایی که نویسنده پیشنهاد میکنه ثبت کنی: کل زمان درخواست و عدد p95 اون، تعداد کوئری و زمان تجمعی دیتابیس، شکل کوئریهای تکراری، اوج مصرف حافظهٔ PHP و حجم پاسخ، و تعداد ردیفها در برابر کاری که دیتابیس برای پیدا کردنشون میکنه.
ابزار شروعش DB::listen() هست که داخل boot() ثبت میشه؛ دیدن یه statement تکراری ۵۰ بار معمولاً مفیدتر از دیدن یه کوئری کنده. نویسنده عمداً bindings رو لاگ نمیکنه چون میتونه دادهی شخصی یا توکن داشته باشه. برای زمان تجمعی هم DB::whenQueryingForLongerThan() هست که آستانهش مثلاً ۳۰۰ میلیثانیهست و روی مجموع زمان کانکشن کار میکنه، نه یه statement. نکتهٔ مهمش هم اینه که اگه زمان SQL کم شد ولی زمان درخواست و حافظه بالا موند، باید سراغ hydration مدل، cast و accessor و رندر رفت.
یه هشدار جانبی هم میده که ربطی به سرعت نداره ولی مهمه: فیلتر team_id فقط دادهٔ کوئری رو محدود میکنه و ثابت نمیکنه کاربر فعلی به اون تیم دسترسی داره. به گفتهٔ نویسنده کوئری سریعی که دادهٔ یه مشتری دیگه رو برمیگردونه، خراب حساب میشه.
نکات کلیدی:
- ۵۰ پروژه با مالک و تسک بهشکل lazy میتونه ۱۰۱ کوئری بشه؛ با eager loading معمولاً ۳ کوئری
- eager loading فقط رفتوبرگشت رو کم میکنه، حجم دادهٔ منتقلشده و hydration سر جاشه
- شمارش و چک وجود داشتن باید سمت دیتابیس بمونه، نه اینکه کل کالکشن تو PHP لود بشه
- DB::listen() برای دیدن کوئریهای تکراری، DB::whenQueryingForLongerThan() برای زمان تجمعی کانکشن
- bindings رو لاگ نکن؛ میتونه دادهی شخصی یا توکن داشته باشه
- فیلتر کردن با team_id جای authorization رو نمیگیره




