سی ام اس نویسسی ام اس نویسیاد بگیر، سیستم خودتو بنویس
مجلهتماس با ما
سی ام اس نویسسی ام اس نویس

CMS نویس مرجعی برای آموزش برنامه نویسی وب و PHP، توسعه افزونه‌های اختصاصی وردپرس، طراحی ورک‌فلو و اتوماسیون با n8n و توسعه CMS های اختصاصی است. هدف ما ارائه آموزش‌های کاربردی و راهکارهای حرفه‌ای برای توسعه‌دهندگان، کسب‌وکارها و علاقه‌مندان به دنیای برنامه‌نویسی است.

پیوندهای سریع
محصولات فیزیکیمحصولات دیجیتالدوره‌های آموزشی
سی ام اس نویس
مجله سی ام اس نویستماس با ماپنل کاربری
سی ام اس نویس — تمامی حقوق برای سی ام اس نویس محفوظ است. © 1406 تماس با ما09307711941
بهینه‌سازی Query در Laravel؛ افزایش سرعت کوئری‌های دیتابیس

بهینه‌سازی Query در Laravel؛ افزایش سرعت کوئری‌های دیتابیس

ممدیر کل سیستم ۲۶ مهر ۱۴۰۵ ۱۷ دقیقه مطالعه ۲ بازدید
بهینه‌سازی Query در Laravel؛ افزایش سرعت کوئری‌های دیتابیس

مقدمه

یکی از عوامل مهم در سرعت اجرای برنامه‌های تحت وب، نحوه تعامل آن‌ها با پایگاه داده است. ممکن است یک پروژه لاراول در ابتدای توسعه بسیار سریع باشد، اما با افزایش تعداد کاربران، محصولات، سفارش‌ها و اطلاعات ذخیره‌شده، زمان پاسخ‌گویی آن افزایش پیدا کند.

در بسیاری از مواقع، مشکل از خود فریم‌ورک نیست؛ بلکه کوئری‌هایی که برای دریافت یا تغییر اطلاعات اجرا می‌شوند، بهینه نیستند.

برای مثال، تصور کنید یک فروشگاه اینترنتی دارید که چندین هزار محصول در پایگاه داده آن ذخیره شده است. اگر برای نمایش صفحه محصولات، تمام رکوردها و تمام ستون‌های جدول را دریافت کنید، ممکن است حجم زیادی از اطلاعات غیرضروری به برنامه منتقل شود. این کار می‌تواند مصرف حافظه و زمان اجرای درخواست را افزایش دهد.

در لاراول ابزارهای مختلفی برای بهینه‌سازی کوئری‌ها وجود دارد؛ از جمله Eloquent، Query Builder، انتخاب ستون‌های ضروری، محدودکردن تعداد نتایج، استفاده صحیح از ایندکس‌ها و بررسی زمان اجرای دستورات SQL.

در این مقاله یاد می‌گیریم چگونه کوئری‌های دیتابیس را در Laravel بهینه کنیم، عملکرد آن‌ها را بررسی کنیم و از اشتباهاتی که باعث کاهش سرعت برنامه می‌شوند جلوگیری کنیم.

۱. بهینه‌سازی Query در Laravel چیست؟

Query یا کوئری، دستوری است که برای دریافت، ثبت، تغییر یا حذف اطلاعات در پایگاه داده اجرا می‌شود.

برای مثال، کوئری زیر اطلاعات کاربران را دریافت می‌کند:


use App\Models\User;



$users = User::all();

این دستور از نظر نحوی صحیح است؛ اما اگر جدول کاربران صدها هزار رکورد داشته باشد، دریافت تمام کاربران فقط برای نمایش چند نتیجه، انتخاب مناسبی نیست.

بهینه‌سازی Query به معنای انتخاب روش مناسب برای اجرای عملیات دیتابیس است؛ به‌گونه‌ای که برنامه با صرف زمان، حافظه و منابع کمتر، نتیجه موردنیاز خود را دریافت کند.

مهم‌ترین اهداف بهینه‌سازی کوئری عبارت‌اند از:

  • کاهش زمان اجرای درخواست‌های دیتابیس
  • کاهش حجم اطلاعات دریافت‌شده
  • کاهش مصرف حافظه برنامه
  • جلوگیری از اجرای کوئری‌های غیرضروری
  • بهبود سرعت صفحات و APIها
  • افزایش توانایی برنامه در پاسخ‌گویی به کاربران بیشتر

البته هر کوئری طولانی الزاماً به معنی وجود مشکل نیست. گاهی حجم داده‌ها یا ماهیت عملیات باعث می‌شود اجرای یک کوئری زمان بیشتری نیاز داشته باشد. به همین دلیل، بهتر است ابتدا عملکرد واقعی برنامه را اندازه‌گیری کنیم و سپس تغییرات لازم را انجام دهیم.

۲. چرا کوئری‌های غیربهینه باعث کندی برنامه می‌شوند؟

فرض کنید یک صفحه مدیریت دارید که باید اطلاعات محصولات را نمایش دهد. اگر هر محصول شامل عنوان، توضیحات طولانی، تصویر، مشخصات فنی و اطلاعات دیگری باشد، دریافت تمام ستون‌ها درحالی‌که فقط عنوان و قیمت موردنیاز هستند، منابع اضافی مصرف می‌کند.

از طرف دیگر، اجرای مکرر کوئری‌های مشابه، استفاده نکردن از ایندکس مناسب یا دریافت تعداد بسیار زیادی رکورد می‌تواند عملکرد سیستم را کاهش دهد.

برای مثال، این کد تمام محصولات را دریافت می‌کند:


$products = Product::all();

اما اگر فقط به محصولات فعال نیاز دارید، بهتر است شرط را در سطح دیتابیس اعمال کنید:


$products = Product::where('is_active', true)->get();

در این مثال فرض شده است مدل Product و ستون is_active در پروژه شما وجود دارند.

مزیت این روش آن است که فیلترکردن اطلاعات پیش از انتقال نتایج به برنامه انجام می‌شود؛ بنابراین دیگر لازم نیست ابتدا همه محصولات را دریافت کنید و سپس در PHP موارد غیرضروری را حذف کنید.

۳. فقط ستون‌های موردنیاز را دریافت کنید

یکی از ساده‌ترین روش‌های بهینه‌سازی Query در لاراول، استفاده از select() برای انتخاب ستون‌های موردنیاز است.

فرض کنید می‌خواهید فهرستی از کاربران را نمایش دهید و فقط به شناسه، نام و ایمیل آن‌ها نیاز دارید.

روش ساده اما پرهزینه‌تر از نظر حجم داده:


use App\Models\User;



$users = User::all();

روش هدفمندتر:


$users = User::query()

    ->select(['id', 'name', 'email'])

    ->get();

در روش دوم، فقط ستون‌های مشخص‌شده در نتیجه کوئری دریافت می‌شوند.

این کار به‌ویژه زمانی مفید است که جدول شامل ستون‌های بزرگ، مانند متن‌های طولانی یا داده‌های حجیم باشد.

نکته مهم درباره ستون‌های کلیدی

اگر از Eloquent و روابط مدل‌ها استفاده می‌کنید، هنگام محدودکردن ستون‌ها باید شناسه‌های موردنیاز برای ارتباط میان مدل‌ها را نیز انتخاب کنید.

برای مثال، اگر می‌خواهید محصولات را همراه با دسته‌بندی آن‌ها دریافت کنید، معمولاً باید کلید خارجی مربوط به دسته‌بندی را نیز در نتیجه محصولات داشته باشید.

بنابراین، انتخاب ستون‌ها باید با توجه به ساختار مدل‌ها و روابط پروژه انجام شود.

۴. به‌جای دریافت همه رکوردها، از Pagination استفاده کنید

در پروژه‌های واقعی، معمولاً لازم نیست تمام رکوردهای دیتابیس در یک درخواست دریافت شوند.

فرض کنید یک فروشگاه ۲۰ هزار محصول دارد. اگر بخواهید تمام محصولات را در یک صفحه نمایش دهید، ممکن است مصرف حافظه و زمان پردازش افزایش پیدا کند.

به‌جای این کار، می‌توانید از Pagination استفاده کنید:


use App\Models\Product;



$products = Product::query()

    ->where('is_active', true)

    ->select(['id', 'name', 'price'])

    ->orderBy('id')

    ->paginate(20);

در این مثال، در هر صفحه حداکثر ۲۰ محصول نمایش داده می‌شود و کاربر می‌تواند میان صفحات جابه‌جا شود.

در Blade نیز می‌توانید لینک‌های صفحات را نمایش دهید:


@foreach ($products as $product)

    <div>

        <h3>{{ $product->name }}</h3>

        <p>{{ number_format($product->price) }}</p>

    </div>

@endforeach



{{ $products->links() }}

این روش برای فهرست محصولات، کاربران، سفارش‌ها و سایر داده‌هایی که به‌صورت صفحه‌بندی‌شده نمایش داده می‌شوند، کاربرد دارد.

تفاوت paginate و simplePaginate

لاراول چند روش برای صفحه‌بندی ارائه می‌کند.

  • paginate() تعداد کل نتایج را نیز محاسبه می‌کند تا اطلاعات صفحات در دسترس باشند.
  • simplePaginate() برای پیمایش صفحات قبلی و بعدی طراحی شده و معمولاً به محاسبه تعداد کل نتایج نیاز ندارد.
  • cursorPaginate() برای پیمایش مبتنی بر یک نشانگر مرتب‌سازی استفاده می‌شود و در بسیاری از فهرست‌های بزرگ می‌تواند انتخاب مناسبی باشد.

برای مثال:


$products = Product::query()

    ->orderBy('id')

    ->simplePaginate(20);

اگر تعداد کل صفحات برای رابط کاربری ضروری نیست، simplePaginate() می‌تواند گزینه مناسبی باشد.

با این حال، انتخاب روش مناسب به نیاز پروژه، نوع مرتب‌سازی و ساختار دیتابیس بستگی دارد.

۵. از فیلترکردن اطلاعات در دیتابیس استفاده کنید

یکی از اشتباهات رایج، دریافت تعداد زیادی رکورد و سپس فیلترکردن آن‌ها در PHP است.

برای مثال، کد زیر تمام محصولات را دریافت می‌کند و سپس محصولات فعال را در حافظه برنامه فیلتر می‌کند:


$products = Product::all();



$activeProducts = $products->where('is_active', true);

اگر تعداد محصولات زیاد باشد، این روش می‌تواند منابع غیرضروری مصرف کند.

روش بهتر:


$activeProducts = Product::query()

    ->where('is_active', true)

    ->get();

در روش دوم، شرط فیلتر در کوئری دیتابیس اعمال می‌شود و فقط نتایج موردنیاز به برنامه برمی‌گردند.

این اصل درباره مرتب‌سازی و محدودکردن نتایج نیز کاربرد دارد. تا جای ممکن، عملیات قابل‌اجرا در دیتابیس را به همان لایه واگذار کنید؛ البته زمانی که نتیجه از نظر منطق برنامه با روش مورد انتظار یکسان باشد.

۶. استفاده از ایندکس برای افزایش سرعت Query

ایندکس یا Index یکی از ابزارهای مهم بهینه‌سازی پایگاه داده است.

ایندکس را می‌توان مانند فهرست یک کتاب در نظر گرفت. بدون فهرست، برای پیدا کردن یک موضوع ممکن است مجبور شوید صفحات زیادی را بررسی کنید؛ اما با فهرست مناسب، مسیر دسترسی کوتاه‌تر می‌شود.

در دیتابیس نیز ایندکس می‌تواند به موتور پایگاه داده کمک کند رکوردهای موردنیاز را سریع‌تر پیدا کند.

فرض کنید در فروشگاه خود مرتب محصولات را براساس شناسه دسته‌بندی فیلتر می‌کنید.

برای نمونه:


$products = Product::query()

    ->where('category_id', $categoryId)

    ->get();

اگر جدول محصولات بسیار بزرگ باشد، ایجاد ایندکس مناسب روی ستون category_id ممکن است عملکرد این کوئری را بهبود دهد.

چگونه ایندکس ایجاد کنیم؟

در لاراول می‌توانید از Migration استفاده کنید.

برای مثال، اگر می‌خواهید روی ستون category_id ایندکس ایجاد کنید، می‌توانید در Migration جدول محصولات بنویسید:


Schema::table('products', function (Blueprint $table) {

    $table->index('category_id');

});

این کد باید در یک Migration مناسب قرار بگیرد. اگر ستون از قبل ایندکس مناسب دارد، ایجاد ایندکس تکراری ضرورتی ندارد.

همچنین اگر جدول از ابتدا ساخته می‌شود، می‌توانید ایندکس را در همان Migration اصلی تعریف کنید.

آیا ایجاد ایندکس برای تمام ستون‌ها مفید است؟

خیر. ایندکس‌ها برای همه ستون‌ها و همه کوئری‌ها مزیت یکسانی ندارند.

ایندکس می‌تواند عملیات جست‌وجو را سریع‌تر کند، اما فضای ذخیره‌سازی مصرف می‌کند و هنگام درج، تغییر یا حذف اطلاعات ممکن است هزینه بیشتری ایجاد کند.

بنابراین:

  • ابتدا کوئری‌های پرتکرار و کند را شناسایی کنید.
  • شرط‌های WHERE، مرتب‌سازی‌ها و ارتباط‌های مهم را بررسی کنید.
  • از ابزارهای تحلیل اجرای کوئری دیتابیس استفاده کنید.
  • پس از ایجاد ایندکس، عملکرد را دوباره اندازه‌گیری کنید.

نکته درباره ایندکس‌های ترکیبی

گاهی یک کوئری هم‌زمان براساس چند ستون فیلتر می‌شود:


$orders = Order::query()

    ->where('user_id', $userId)

    ->where('status', 'paid')

    ->get();

در چنین شرایطی ممکن است یک ایندکس ترکیبی روی ستون‌های user_id و status مناسب باشد.

اما ترتیب ستون‌ها در ایندکس ترکیبی اهمیت دارد و باید با الگوی واقعی کوئری‌ها و امکانات موتور دیتابیس هماهنگ باشد. بهتر است بدون بررسی Query Plan، صرفاً براساس حدس ایندکس ایجاد نکنید.

۷. استفاده صحیح از Eloquent Relationships

Eloquent امکان تعریف رابطه میان مدل‌ها را فراهم می‌کند. استفاده صحیح از این روابط می‌تواند کد را خواناتر و نگهداری آن را آسان‌تر کند؛ اما باید توجه داشت که نحوه دریافت اطلاعات مرتبط بر عملکرد کوئری‌ها تأثیر می‌گذارد.

فرض کنید هر محصول به یک دسته‌بندی تعلق دارد و در مدل Product رابطه زیر تعریف شده است:


public function category()

{

    return $this->belongsTo(Category::class);

}

اگر فقط اطلاعات محصول را نیاز داشته باشید، کوئری ساده زیر کافی است:


$products = Product::query()->get();

اما اگر در صفحه، دسته‌بندی هر محصول نیز نمایش داده شود، باید نحوه دریافت رابطه را در نظر بگیرید.

برای دریافت محصولات همراه با دسته‌بندی می‌توانید از with() استفاده کنید:


$products = Product::with('category')->get();

این روش از پیش رابطه موردنیاز را بارگذاری می‌کند و در بسیاری از سناریوها به کاهش تعداد درخواست‌های دیتابیس کمک می‌کند.

با این حال، اگر تعداد محصولات بسیار زیاد باشد، همچنان باید تعداد نتایج را محدود کنید و ستون‌های موردنیاز را انتخاب کنید.

نکته: مشکل N+1 Query موضوعی مستقل و مهم است که در مقاله بعدی این مجموعه به‌صورت کامل بررسی خواهد شد.

۸. برای شمارش و بررسی وجود رکوردها از متد مناسب استفاده کنید

گاهی فقط می‌خواهید بدانید چند رکورد با یک شرط مشخص وجود دارند یا اصلاً رکوردی وجود دارد یا خیر.

در چنین شرایطی، لازم نیست همه رکوردها را دریافت کنید.

برای شمارش سفارش‌های پرداخت‌شده یک کاربر:


$paidOrdersCount = Order::query()

    ->where('user_id', $userId)

    ->where('status', 'paid')

    ->count();

برای بررسی وجود حداقل یک سفارش پرداخت‌شده:


$hasPaidOrder = Order::query()

    ->where('user_id', $userId)

    ->where('status', 'paid')

    ->exists();

متد count() تعداد رکوردهای منطبق با شرط را برمی‌گرداند و exists() مشخص می‌کند آیا رکوردی مطابق شرط وجود دارد یا خیر.

این دو متد برای هدف‌های متفاوتی طراحی شده‌اند؛ اگر فقط وجود یک رکورد مهم است، معمولاً exists() انتخاب مناسب‌تری از دریافت تمام رکوردها خواهد بود.

۹. دریافت تعداد محدودی از نتایج با limit و take

اگر فقط به چند نتیجه نیاز دارید، بهتر است تعداد نتایج را در سطح کوئری محدود کنید.

برای مثال، اگر می‌خواهید جدیدترین پنج محصول را نمایش دهید:


$products = Product::query()

    ->orderByDesc('created_at')

    ->limit(5)

    ->get();

می‌توانید از take() نیز استفاده کنید:


$products = Product::query()

    ->orderByDesc('created_at')

    ->take(5)

    ->get();

در این مثال، مرتب‌سازی براساس زمان ایجاد انجام می‌شود و فقط پنج نتیجه برگردانده می‌شوند.

این روش برای نمایش محصولات جدید، آخرین سفارش‌ها، مطالب مرتبط و سایر بخش‌هایی که به تعداد محدودی از رکوردها نیاز دارند، مفید است.

۱۰. جلوگیری از بارگذاری تمام داده‌ها در حافظه

گاهی لازم است روی تعداد زیادی رکورد عملیات انجام دهید؛ برای مثال، قیمت محصولات را به‌روزرسانی کنید یا برای سفارش‌های قدیمی یک عملیات اجرایی انجام دهید.

در چنین شرایطی، استفاده از get() برای دریافت همه رکوردها ممکن است مصرف حافظه را افزایش دهد.

روش ساده:


$products = Product::all();



foreach ($products as $product) {

    // انجام عملیات روی محصول

}

اگر تعداد محصولات زیاد باشد، بهتر است داده‌ها را در بخش‌های کوچک پردازش کنید.

استفاده از chunk


Product::query()

    ->orderBy('id')

    ->chunk(500, function ($products) {

        foreach ($products as $product) {

            // انجام عملیات روی هر محصول

        }

    });

در این مثال، محصولات در دسته‌های ۵۰۰تایی پردازش می‌شوند.

استفاده از lazy

اگر بخواهید نتایج را به‌صورت تدریجی پردازش کنید، می‌توانید از lazy() استفاده کنید:


Product::query()

    ->orderBy('id')

    ->lazy(500)

    ->each(function ($product) {

        // انجام عملیات روی محصول

    });

این روش به شما امکان می‌دهد داده‌ها را بدون بارگذاری تمام نتایج در یک مجموعه بزرگ پردازش کنید.

اگر در حین پردازش، رکوردها را تغییر می‌دهید و این تغییرات روی شرط یا ترتیب کوئری تأثیر می‌گذارند، روش‌هایی مانند chunkById() یا lazyById() ممکن است مناسب‌تر باشند. انتخاب روش به نوع عملیات و ساختار داده‌ها بستگی دارد.

۱۱. چگونه کوئری‌های کند را شناسایی کنیم؟

پیش از بهینه‌سازی، باید بدانید کدام کوئری‌ها مشکل دارند. تغییر دادن کد بدون اندازه‌گیری ممکن است هیچ بهبودی ایجاد نکند یا حتی عملکرد را بدتر کند.

لاراول امکان مشاهده کوئری‌های اجراشده را فراهم می‌کند.

برای مثال، می‌توانید در محیط توسعه از DB::listen() استفاده کنید:


use Illuminate\Database\Events\QueryExecuted;

use Illuminate\Support\Facades\DB;

use Illuminate\Support\Facades\Log;



DB::listen(function (QueryExecuted $query) {

    Log::debug('Database query executed.', [

        'sql' => $query->sql,

        'time_ms' => $query->time,

    ]);

});

این نمونه، متن کوئری و زمان اجرای آن را در لاگ ثبت می‌کند.

کد را می‌توانید در محل مناسب برای بررسی کوئری‌ها قرار دهید؛ برای نمونه، در یک Service Provider مخصوص محیط توسعه. از ثبت بی‌هدف همه کوئری‌ها در محیط تولید خودداری کنید، زیرا این کار ممکن است حجم لاگ‌ها و هزینه پردازش را افزایش دهد.

همچنین توجه داشته باشید که مقدار time زمان اجرای کوئری گزارش‌شده توسط Laravel است؛ این مقدار لزوماً تمام زمان پردازش درخواست، زمان انتقال شبکه یا زمان رندر صفحه را نشان نمی‌دهد.

بررسی زمان تجمعی کوئری‌ها

گاهی یک کوئری به‌تنهایی کند نیست، اما تعداد زیادی کوئری در یک درخواست اجرا می‌شوند و زمان تجمعی آن‌ها زیاد می‌شود.

لاراول امکان نظارت بر زمان تجمعی اجرای کوئری‌ها را نیز فراهم می‌کند.

برای نمونه، در یک Service Provider می‌توانید از DB::whenQueryingForLongerThan() استفاده کنید تا وقتی زمان تجمعی کوئری‌های یک درخواست از حد مشخصی عبور کرد، یک هشدار ثبت شود.

این روش به شما کمک می‌کند درخواست‌هایی را شناسایی کنید که مجموع عملیات دیتابیس آن‌ها بیش از حد طولانی شده است.

۱۲. استفاده از EXPLAIN برای بررسی کوئری‌های دیتابیس

گاهی مشاهده متن کوئری و زمان اجرای آن برای پیدا کردن علت کندی کافی نیست. در چنین شرایطی، می‌توانید از ابزارهای تحلیل اجرای کوئری در خود دیتابیس استفاده کنید.

در MySQL و MariaDB، دستور EXPLAIN اطلاعاتی درباره روش اجرای یک کوئری ارائه می‌کند.

برای مثال:


EXPLAIN

SELECT id, name, price

FROM products

WHERE category_id = 10;

با توجه به نسخه دیتابیس و نوع خروجی، می‌توانید بررسی کنید که آیا ایندکس مناسبی استفاده شده است، چه تعداد رکورد احتمالی بررسی می‌شود و موتور دیتابیس چه مسیری را برای اجرای کوئری انتخاب کرده است.

اگر از PostgreSQL استفاده می‌کنید، ابزارهایی مانند EXPLAIN و EXPLAIN ANALYZE نیز در دسترس‌اند؛ اما باید تفاوت‌های موتور دیتابیس را در نظر بگیرید.

نکته مهم: اجرای EXPLAIN ANALYZE می‌تواند کوئری را واقعاً اجرا کند. برای کوئری‌های سنگین یا عملیاتی که داده‌ها را تغییر می‌دهند، پیش از اجرا باید اثر آن را بررسی کنید.

بهینه‌سازی اصولی به معنای تغییر کورکورانه کوئری نیست؛ بلکه ابتدا باید رفتار واقعی دیتابیس را بررسی کنید و سپس براساس شواهد تصمیم بگیرید.

۱۳. استفاده از Cache برای داده‌های پرتکرار

گاهی یک کوئری بهینه است، اما اجرای مکرر آن برای داده‌هایی که به‌ندرت تغییر می‌کنند، منابع اضافی مصرف می‌کند.

برای مثال، ممکن است تعداد محصولات فعال یا فهرست دسته‌بندی‌های سایت در درخواست‌های زیادی موردنیاز باشد.

در چنین شرایطی می‌توانید از Cache لاراول استفاده کنید:


use App\Models\Category;

use Illuminate\Support\Facades\Cache;



$categories = Cache::remember(

    'active_categories',

    now()->addMinutes(30),

    function () {

        return Category::query()

            ->where('is_active', true)

            ->orderBy('name')

            ->get(['id', 'name']);

    }

);

در این مثال، نتیجه کوئری برای مدت ۳۰ دقیقه در Cache ذخیره می‌شود. اگر مقدار موجود و معتبر باشد، در درخواست‌های بعدی همان نتیجه از Cache دریافت خواهد شد.

این روش برای داده‌های نسبتاً ثابت می‌تواند مفید باشد؛ اما باید سیاست به‌روزرسانی یا حذف Cache را نیز مشخص کنید.

برای مثال، اگر دسته‌بندی جدیدی ایجاد شود یا دسته‌بندی‌ای غیرفعال شود، باید تصمیم بگیرید که داده ذخیره‌شده چگونه به‌روز شود.

Cache جایگزین بهینه‌سازی کوئری نیست. اگر کوئری اصلی بسیار کند باشد یا داده‌ها دائماً تغییر کنند، ممکن است Cache راه‌حل مناسبی نباشد.

۱۴. استفاده از Query Builder برای کوئری‌های ساده

Eloquent ابزار مناسبی برای کار با مدل‌ها و روابط است؛ اما در بعضی عملیات‌های ساده که به امکانات مدل نیازی ندارند، Query Builder می‌تواند انتخاب مناسبی باشد.

برای مثال:


use Illuminate\Support\Facades\DB;



$products = DB::table('products')

    ->where('is_active', true)

    ->select('id', 'name', 'price')

    ->orderBy('name')

    ->limit(20)

    ->get();

این کوئری اطلاعات موردنیاز را از جدول محصولات دریافت می‌کند.

Query Builder برای بسیاری از عملیات‌های دیتابیس خوانا و کاربردی است؛ اما نباید تصور کرد که همیشه از Eloquent سریع‌تر است. عملکرد واقعی به نوع کوئری، مدل‌ها، روابط، تبدیل داده‌ها و ساختار برنامه بستگی دارد.

همچنین برخلاف Eloquent، نتایج Query Builder به‌صورت پیش‌فرض نمونه‌های مدل Eloquent نیستند. اگر به قابلیت‌هایی مانند Accessorها، Castها یا رویدادهای مدل نیاز دارید، این تفاوت را در تصمیم‌گیری لحاظ کنید.

۱۵. اشتباهات رایج در بهینه‌سازی Query در Laravel

هنگام بهینه‌سازی کوئری‌ها، بهتر است از چند اشتباه رایج دوری کنید.

اشتباه اول: دریافت همه رکوردها بدون نیاز واقعی

استفاده از all() یا get() برای دریافت حجم بسیار زیادی از داده‌ها، درحالی‌که فقط چند رکورد موردنیاز است، می‌تواند منابع برنامه را هدر دهد.

اشتباه دوم: دریافت تمام ستون‌ها

اگر فقط به چند ستون نیاز دارید، استفاده از select() می‌تواند حجم داده منتقل‌شده را کاهش دهد.

اشتباه سوم: ایجاد ایندکس بدون بررسی

ایندکس بیشتر همیشه به معنای سرعت بیشتر نیست. ایندکس‌های غیرضروری فضای ذخیره‌سازی مصرف می‌کنند و هزینه تغییر داده‌ها را افزایش می‌دهند.

اشتباه چهارم: بهینه‌سازی براساس حدس

گاهی توسعه‌دهنده بخشی از کد را کند تصور می‌کند، درحالی‌که زمان اصلی در کوئری دیگری صرف می‌شود. ابتدا اندازه‌گیری کنید.

اشتباه پنجم: استفاده از Cache بدون سیاست انقضا

اگر داده‌ها تغییر کنند اما Cache به‌روزرسانی نشود، ممکن است اطلاعات قدیمی به کاربر نمایش داده شوند.

اشتباه ششم: نادیده گرفتن روابط Eloquent

بارگذاری رابطه‌ها در حلقه‌ها می‌تواند باعث اجرای تعداد زیادی کوئری شود. این موضوع به مشکل N+1 مرتبط است و در مقاله بعدی به‌طور مستقل بررسی خواهد شد.

جمع‌بندی

بهینه‌سازی Query در Laravel یکی از گام‌های مهم برای افزایش سرعت و مقیاس‌پذیری پروژه‌های PHP است. برای بهبود عملکرد، ابتدا باید کوئری‌های واقعی برنامه را بررسی کنید و سپس براساس نیاز، تغییرات مناسبی در نحوه دریافت و پردازش داده‌ها انجام دهید.

در این مقاله یاد گرفتیم:

  • فقط ستون‌ها و رکوردهای موردنیاز را دریافت کنیم.
  • از Pagination برای نمایش مجموعه‌های بزرگ استفاده کنیم.
  • ایندکس‌ها را براساس الگوی واقعی کوئری‌ها طراحی کنیم.
  • برای شمارش و بررسی وجود داده‌ها از متدهای مناسب استفاده کنیم.
  • پردازش مجموعه‌های بزرگ را با chunk() و lazy() مدیریت کنیم.
  • زمان اجرای کوئری‌ها را با ابزارهای لاراول بررسی کنیم.
  • با استفاده از EXPLAIN رفتار دیتابیس را تحلیل کنیم.
  • برای داده‌های پرتکرار و نسبتاً ثابت از Cache استفاده کنیم.

به خاطر داشته باشید که هدف بهینه‌سازی، صرفاً نوشتن کد کوتاه‌تر نیست؛ بلکه باید برنامه با حفظ صحت نتایج، منابع کمتری مصرف کند و سریع‌تر پاسخ دهد.

در مقاله بعدی این مجموعه، سراغ یکی از مشکلات رایج در پروژه‌های لاراول می‌رویم: N+1 Query؛ مشکلی که ممکن است باعث اجرای تعداد زیادی کوئری غیرضروری شود، حتی زمانی که کد ظاهراً ساده و صحیح به نظر می‌رسد.

منابع رسمی

  • مستندات دیتابیس در Laravel 12
  • مستندات Eloquent ORM در Laravel 12
  • مستندات Query Builder در Laravel 12
  • مستندات Cache در Laravel 12

نظرات (0)

نظرات پس از تأیید مدیر نمایش داده می‌شوند

هنوز نظری ثبت نشده است؛ اولین نفر باشید!

مقالات مرتبط

ساخت CMS با Laravel 12؛ آموزش ساخت سیستم مدیریت محتوا از صفر

ساخت CMS با Laravel 12؛ آموزش ساخت سیستم مدیریت محتوا از صفر

۲۴ دقیقه
ساخت فروشگاه اینترنتی با Laravel 12؛ آموزش پروژه‌محور از صفر تا طراحی هسته فروشگاه

ساخت فروشگاه اینترنتی با Laravel 12؛ آموزش پروژه‌محور از صفر تا طراحی هسته فروشگاه

۲۲ دقیقه
ساخت Marketplace با Laravel؛ آموزش طراحی فروشگاه چندفروشندگی

ساخت Marketplace با Laravel؛ آموزش طراحی فروشگاه چندفروشندگی

۲۲ دقیقه
مدیریت Queueهای سنگین در Laravel؛ آموزش پردازش کارهای طولانی

مدیریت Queueهای سنگین در Laravel؛ آموزش پردازش کارهای طولانی

۲۲ دقیقه