Tìm hiểu về N+1 query

 Nov-27-2020 02:15 PM
#news #CNTT

1. N+1 query là gì?

N+1 query xảy ra khi ta truy vấn 1 câu query thì mỗi kết quả của câu query trước kéo theo N câu query khác. Như vậy, 1 là câu query ban đầu N chính là số lượng câu query được gọi cho mỗi kết quả của câu truy vấn ban đầu.

2. N+1 query ảnh hưởng thế nào tới performance?

Hệ thống phải truy vấn nhiều câu query hơn dẫn tới việc làm giảm tốc độ phản hồi của server.

Chính tính năng mặc định lazy loading khiến các ngôn ngữ như ruby gặp phải vấn đề N+1 query thường xuyên hơn hẳn các ngôn ngữ khác.

Điều này hoàn toàn có thể tránh được khi bạn truy vấn cơ sở dữ liệu bằng các câu truy vấn SQL thuần. Nhưng thực tế hầu hết các hệ thống hiện tại đều không sử dụng các câu truy vấn thuần do các vần đề về bảo mật như SQL injection và cũng do cú pháp của các hệ quản trị cơ sở dữ liệu có sự khác biệt. Vậy các hệ thống hiện tại hầu hết đều sử dụng ORM để tương tác với cơ sở dữ liệu. Bạn có thể tìm hiểu thêm về ORM <dẫn link về ORM>.

Vậy tại sao ORM lại sử dụng truy vấn N+1 query? Để trả lời câu hỏi này ta xét tới cách phương thức mà ORM đang sử dụng để load dữ liệu.

Lazy loading: Chỉ load data từ database khi cần. Eager loading: Load tất cả dữ liệu ra.

Tùy vào mỗi loại ngôn ngữ sẽ sử dụng lazy loading hay eager loading mặc định. Ví dụ trong Ruby ActiveRecord mặc định sử dụng lazy loading, hay trong nodejs TypeORM mặc định không sử dụng lazy loading. Trong một số ngôn ngữ như java hibernate sẽ yêu cầu phải định nghĩa trước trong mỗi quan hệ sẽ sử dụng kiểu loading nào. Nhìn qua sẽ thấy lazy loading có vẻ là một sự lựa chọn sáng suốt vì nó chỉ load khi cần nên sẽ giảm số lượng bản ghi dư thửa bị load lên. Như khi ta có user và user details nó sẽ chỉ load lên user chứ không load user_details tương ứng trừ khi người dùng yêu cầu xem user details. Tuy nhiên chính việc chỉ load data từ database khi cần. Mỗi bản ghi con sẽ được load riêng biệt do ORM chỉ lấy nó khi cần nên mỗi lần truy vấn sẽ chỉ lấy ra đúng 1 bản ghi. Số lượng câu truy vấn nhiều sẽ khiến database phải quét lại bảng nhiều lần ( trong khi hoàn toàn có thể lấy tất cả các bản ghi này trong 1 lần quét hay sử dụng duy nhất 1 câu truy vấn ).

3. Làm cách nào để phòng chống N+1 query?

Sau khi đă hiểu ngọn nguồn của N+1 query ta sẽ từ đó rút ra các phương pháp phòng chống N+1 query. Như phần trên ta biết N+1 query xảy ra khi dữ liệu được load ra bằng lazy loading như vậy ta chỉ cần thay đổi phương thức load sang eager loading là có thể giải quyết được vấn đề này. Eager load sẽ load tất cả bản ghi con lên và lưu trữ chúng trong bộ nhớ đệm để sử dụng cho sau này. Như vậy database sẽ chỉ bị quét 1 lần. Tuy nhiên eager load có nhược điểm là nó sẽ load tất cả các bản ghi con lên trong khi số lượng bản ghi con mà ta cần dùng tới lại không nhiều như vậy. select in ý tưởng là tạo ra 2 câu truy vấn 1 câu truy vấn gốc và 1 câu truy vấn kèm theo để lấy bản ghi con của câu truy vấn gốc. Joins kết nối 2 bản ghi với nhau để cho ra kết quả do đó chỉ cần quét cơ sở dữ liệu duy nhất 1 lần. Tuy nhiên cách này sẽ làm giảm performance khi ta join 2 bảng có số lượng bản ghi quá lớn.

Lưu Xuân Đại

Hỗ trợ trực tuyến
Online Offline
Hỗ trợ trực tuyến