مقدمه
در قسمت قبل در مورد شاخص های بهروری دیتابیس صحبت کردیم.
در این بخش خواهیم دید که چگونه حذف select های بی استفاده از برنامه های ما می تواند مصرف مموری را کاهش دهد. ابتدا پروژه را دریافت کنید.
در این پروژه ما یک وبلاگ داریم که در صفحه اصلی آن همه پست ها را به ترتیب سال انتشار نشان می دهیم.
بررسی مشکل در بهره وری
Query مورد نیاز برای نمایش داده های صفحه اول بسیار راحت است.
<?php
namespace App\Http\Controllers;
use App\Post;
class PostsController extends Controller
{
public function index()
{
$years = Post::query()
->with('author')
->latest('published_at')
->get()
->groupBy(fn ($post) => $post->published_at->year);
return view('posts', ['years' => $years]);
}
}
پست ها را به ترتیب تاریخ انتشار از دیتابیس می خوانیم و رابطه ی authors را همراه آن ها eager load می کنیم و با کمک متد groupBy
کلاس Collection
، آن ها را گروه بندی می کنیم. سپس نتیجه به دست آمده را به فایل blade
با نام posts
می دهیم تا آن ها را نمایش دهد. اکنون نگاهی به debugbar می اندازیم.
همان طور که مشاهده می کنید توسط یک Query همه پست ها و توسط Query دیگر همه ی نویسنده ها را از دیتابیس خوانده ایم. در قسمت مدل ها هم 20 مدل user (برای نویسنده ها) و 100 مدل پست از دیتابیس خوانده شده است که کاملا منطبق با نیاز صفحه ی ماست. اما اگر به مصرف مموری نگاه کنیم متوجه مصرف بالای آن می شویم. این مقدار در محیط local 31 مگابایت است. اگر از مصرف مموری فریم ورک لاراول که در محیط local حدود 15 مگابایت است صرف نظر کنیم برنامه ی ما 16 مگابایت مموری مصرف می کند که مقدار زیادی است. اما چه چیزی باعث افزایش مصرف مموری ما شده است؟ اگر به قسمت Query ها بر گردیم می بینید که همه ی ستون های جدول posts از دیتابیس select شده اند. با اینکه ما در این صفحه تنها به عنوان، تاریخ و نویسنده ی پست ها نیاز داریم اما همه ستون ها را از دیتابیس فراخوانی کردیم که شامل متن پست ها هم می شود. بنابراین مصرف مموری برنامه ما به دلیل فراخوانی متن پست ها که طولانی هم هستند، افزایش پیدا کرده است. Query خود را به طوری تغییر می دهیم که تنها ستون های مورد نیاز ما از دیتابیس خوانده شوند.
<?php
namespace App\Http\Controllers;
use App\Post;
class PostsController extends Controller
{
public function index()
{
$years = Post::query()
->select(['id', 'title', 'slug', 'published_at', 'author_id'])
->with('author')
->latest('published_at')
->get()
->groupBy(fn($post) => $post->published_at->year);
return view('posts', ['years' => $years]);
}
}
توجه داشته باشید که همیشه primary key
جدول خود را select کنید. همچنین اگر می خواهید رابطه ای را eagerload کنید باید foreign key
آن رابطه را نیز در select خود قرار دهید (در مثال ما ستون های id
و author_id
به این دلیل select شده اند).
همان طور که مشاهده می کنید مصرف مموری به 17 مگابایت رسید.
انجام این کار تنها به جدول اصلی مربوط نمی شود و هنگام eagerload کردن نیز می توان ستون های مورد نیاز را select کرد.
$years = Post::query()
->select(['id', 'title', 'slug', 'published_at', 'author_id'])
->with(['author' => function ($query) {
$query->select(['id', 'name']);
}])
->latest('published_at')
->get()
->groupBy(fn($post) => $post->published_at->year);
برای select کردن ستون های مورد نیاز هنگام eagerload یک میان بر هم وجود دارد.
$years = Post::query()
->select(['id', 'title', 'slug', 'published_at', 'author_id'])
->with('author:id,name')
->latest('published_at')
->get()
->groupBy(fn($post) => $post->published_at->year);
جمع بندی
در این مطلب دیدیم که چگونه انتخاب کردن ستون هایی از دیتابیس که مورد نیاز برنامه هستند، می تواند به کاهش مصرف مموری کمک کند. ممکن است به این فکر کنید که باید تمام Query هایی که در برنامه نوشتم را تغییر دهم و عبارت select را به آن ها اضافه کنم. به نظر من نیازی به انجام این کار نیست و تنها در صفحه هایی از برنامه که داده ی زیادی از دیتابیس فراخوانی می شود (مانند صفحه های index) انجام این کار ضروری است و برای باقی صفحه ها نیازی به تغییر کد قبلی نیست.