Trung bình Laravel

Tối Ưu Truy Vấn Laravel Với Eager Loading

Tìm hiểu cách tối ưu truy vấn database trong Laravel bằng eager loading để tránh vấn đề N+1 query.

19 Th06, 2026 3 phút 1,735 Lượt xem 6 Khối code

Vấn đề N+1

Khi làm việc với Eloquent ORM của Laravel, một trong những "kẻ giết hiệu năng" phổ biến nhất là vấn đề N+1 query. Nó xuất hiện ngay khi bạn load một collection model rồi truy cập một relationship bên trong vòng lặp.

$posts = Post::all(); // 1 query

foreach ($posts as $post) {
    echo $post->author->name; // +1 query CHO MỖI post
}

Load 50 posts thì bắn ra 51 query: một cho posts, cộng thêm một cho mỗi author. Lỗi này gần như vô hình khi review code nhưng cực kỳ nguy hiểm khi chịu tải cao.

graph TD A[Post::all - 1 query] --> B[Lặp qua từng post] B --> C[post.author - query 2] B --> D[post.author - query 3] B --> E[post.author - query 51]

Cách khắc phục: eager loading với with()

$posts = Post::with('author')->get(); // tổng cộng 2 query

foreach ($posts as $post) {
    echo $post->author->name; // không phát sinh query thêm
}

Laravel chạy một query cho posts và một query duy nhất WHERE author_id IN (...) cho tất cả author — luôn là 2 query dù có bao nhiêu post.

Relationship lồng nhau và nhiều relationship

// Nhiều relation
$posts = Post::with(['author', 'comments'])->get();

// Relation lồng nhau (dot notation)
$posts = Post::with('comments.user')->get();

// Ràng buộc relation bằng closure
$posts = Post::with(['comments' => fn ($q) => $q->latest()->limit(5)])->get();

Chỉ load những cột cần thiết

$posts = Post::with('author:id,name')->get();

Chỉ chọn id,name giúp payload nhỏ gọn — nhưng luôn phải bao gồm khóa ngoại (id), nếu không relation sẽ không khớp được.

Eager load sau khi đã có collection

Đã có sẵn collection? Load relationship sau đó bằng load():

$posts = Post::all();
$posts->load('author');

Tự động phát hiện N+1

Bật strict mode trong service provider để lazy loading ném lỗi khi đang phát triển:

use Illuminate\Database\Eloquent\Model;

Model::preventLazyLoading(! app()->isProduction());

Điểm cần nhớ

  • with() eager-load ngay từ đầu (2 query); load() eager-load một collection đã có sẵn.
  • Luôn eager-load bất kỳ relationship nào bạn truy cập bên trong vòng lặp.
  • Dùng column selection và closure để chỉ load đúng những gì cần.
  • Bật preventLazyLoading khi dev để bắt lỗi N+1 trước khi lên production.

Cùng chủ đề Laravel