Cách giải quyết vấn đề N+1 query trong Laravel (kỹ thuật Eager Loading)

0

Xin chào mọi người,Larvel đã sử dụng kỹ thuật ORM giúp lập trình viên thao tác dễ dàng hơn với DB.Tuy nhiên Nếu không sử dụng đúng cách thư viện ORM của Laravel thì ta rất dễ bị rơi vào vấn đề N+1

Hãy giả sử bạn gặp một vấn đề như thế này:

  • Bạn có một danh sách các bài viết trong bảng posts, với Model Eloquent tên là Post
  • Mỗi Post thuộc về một User. Post có quan hệ belongsTo với User, và User có quan hệ hasMany với Post.
  • Bạn có một trang Post list, cần hiển thị ra một danh sách các Post, dĩ nhiên với tên tác giả của post đó. Đây là một tình huống rất hay thường gặp trong các project, với nhiều biến thể khác nhau. Và dưới đây là một cách giải quyết thường gặp.
// Post Model
public function user()
{
    return $this->belongsTo(User::class);
}
// In Controller or Somewhere else
$posts = Post::all();
// View
foreach ($posts as $post) {
    {{ $post->title }}
    {{ $post->user->name }}
}
  • Như vậy để in ra tên của chủ nhân của bài viết, ta đã sử dụng đến quan hệ user mà mình đã định nghĩa, tức nó sẽ lấy ra User chủ nhân của bài Post. Nếu bạn cài Laravel Debugbar thì bạn sẽ nhận ra những vấn đề của phương pháp này:
    • Với mỗi một bài Post ta phải thực hiện một câu truy vấn SQL để lấy ra User. Nếu số lượng bài post của bạn là lớn thì số lượng câu truy vấn SQL phải thực cũng sẽ là lớn.
    • Có khả năng bị thực hiện những câu truy vấn trùng lặp nhau. Chẳng hạn bài post số 1, 2, 3 đều thuộc user có id là 1. Trong trường hợp đó ta sẽ phải thực hiện 3 câu truy vấn SQL cùng với mục đích là lấy ra user có id bằng 1 (để gán vào cho từng bài post).

Nên biết truy vấn SQL thường là một trong những tác vụ tốn nhiều thời gian nhất trong một xử lý request. Rõ ràng việc phải sử dụng nhiều câu truy vấn như vậy là một vấn đề ta cần khắc phục. Ta gọi vấn đề đó là N + 1 problem, bởi ngoài việc dùng 1 câu truy vấn để select ra N bài post, bạn sẽ phải sử dụng tiếp N câu truy vấn để lấy ra N user cho N bài post đó.

Laravel cung cấp một công cụ để giải quyết vấn đề này, đó là Eager Loading.

// Eager Loading
$posts = Post::with('user')->all();

// Lazy Eager Loading
$posts = Post::all();
$posts->load('user');

Với việc sử dụng hàm with() hay load(), ta có thể load một lúc ra tất cả các User thuộc về tất cả các Post chỉ bằng một câu truy vấn SQL. Điều này sẽ giúp ta giảm số lượng câu truy vấn đi một cách đáng kể.

Hãy luôn cố gắng dùng Eager Loading những lúc có thể nhé. Laravel cung cấp cho chúng ta một hệ thống Eager Loading rất hoàn hảo và mạnh mẽ. Bên cạnh việc load một quan hệ như trên, ta còn có thể load một lúc nhiều quan hệ (Multiple Relationships Eager Loading)load quan hệ chồng nhau (Nested Eager Loading), hay thực hiện Eager Loading có kèm điều kiện …

Leave A Reply

Your email address will not be published.