Trang Chủ

N + 1 query – Tính năng hay là bug

Người viết: Hoàn Kỳ

1. N + 1 query là gì ?

Câu chuyện xảy ra khi chúng ta, những coder viết code chưa khéo, sinh ra nhiều truy vấn vào cơ sở dữ liệu làm giảm performance của hệ thống. Khi đồng nghiệp đọc code thường thì sẽ bình luận ngay: fix N + 1

Nếu để trót lọt, sau một thời gian vận hành mà chương trình chậm, điều tra ra nguyên nhân rồi lại câu nói kinh điển:

✌️ Đứa nào code ra cái đống shit này đây ✌️

2. Ví dụ về N + 1

Mình viết ví dụ tựa như mã giả thui nhé, không đặt nặng vấn đề cú pháp:

Giả sử ta có một cơ sở dữ liệu, trong đó table post có khóa ngoại user_id, nói theo kiểu mã giả là một post thuộc về một user

Thực hiện truy vấn vào cơ sở dữ liệu và lấy tất cả User kèm theo các Post của User đó:

Các câu lệnh SQL sinh ra như sau

Lấy máy tính CASIO FX-500 MS ra để đếm thì thấy mình cần dùng 4 câu truy vấn:

Đối với những hệ thống có số lượng bản ghi lớn (cỡ như phải trả về 1000 user thì chúng ta phải thực hiện 1001 truy vấn) hoặc có database với độ trễ cao (thời gian thực thi truy vấn cao) thì ắt hẳn sẽ làm giảm performance của hệ thống.

Vậy làm sao để có thể lấy ra dữ liệu tương đương như vậy nhưng với số lượng truy vấn  hơn ?

2. Cách khắc phục

2.1 Sử dụng select in ()

Tối ưu câu lệnh SQL ngay và luôn.

Chúng ta cần 2 truy vấn:

2.2 Sử dụng joins

Luyên thuyên về joins một lát để hiểu tại sao dùng joins lại giảm được số query.

2.2.1 Joins là gì ?

Ta có một ví dụ như này:

id name address email
1 Hoa Vinh Ha Noi hoavinh@gmaill.com
2 Mão Mèo Ha Noi maomeokta@gmail.com
id user_id title content
1 1 Cô gái M52 Nhà em có bán rượu không
2 2 Kém Duyên Mà sao anh say vì em mất rồi

Trong ví dụ trên bạn có thể dùng joins để tạo ra bảng mới chứa tên, email của user và những tên bài đăng mà user đó tạo ra.

Và kết quả là:

name email titlle
Hoa Vinh hoavinh@gmaill.com Cô gái M52
Mão Mèo maomeokta@gmail.com Kém Duyên

Như vậy, joins sẽ tem tém hai bảng lại rồi truy vấn trên kết quả đó nên joins chỉ tốn một query duy nhất đã cho ra kết quả.

2.2.2 Các loại joins

Khi sử dụng joins thì chắc chắn bạn đang dùng một trong các loại sau nên ta sẽ tìm hiểu luôn:

2.2 Lựa chọn cái nào

Vậy thì hãy xem 3 ví dụ sau:

Lý do lỗi ở đây là gì ? Tại sao where với bảng bên trái thì không gặp lỗi mà bảng bên phải lại gặp lỗi no such column

-> Bởi vì select in() sẽ không load trước được dữ liệu từ bảng post, nên nó không hiểu trường title là gì.

-> Còn trường name nằm ngay trong table user rồi nên không gặp lỗi.

Vậy trong trường hợp có điều kiện where ở bảng bên phải thì phải sài joins nhé, còn ngược lại thì cứ sài select in() cho ngon, bổ, rẻ.

Có thể bạn quan tâm

Hướng dẫn cách fix và restore WordPress bị shell hack hoặc chiếm quyền điều khiển

Mẹo quy ước tên cho CSS giúp bạn rút ngắn 2/3 thời gian khi debug!

3. N +1 trong Rails

preload:

Eagerload

Inludes

Cú pháp cũ hơn:

4. Tính năng hay bug

Nhưng chưa hẳn, hãy xem xét ví dụ sau:

Hoặc một trường hợp đơn giản hơn, khi sử dụng html cache thì sao ?

Như vậy, không phải lúc nào gặp N + 1 cũng sẽ khử, nó còn phụ thuộc vào từng trường hợp cụ thể, khử được theo select in() thì thì khử nó không thương tiếc, còn phải khử theo joins thì nên chú ý tới độ lớn của bảng.

✌️ Happy coding ✌️

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Chia sẻ bài viết ngay