10 Tips for Eager Loading to Avoid n+1 Queries in Rails
Sử dụng Eager Loading là một cách rất tuyệt vời để ngăn chặn n+1 query . Tuy nhiên cú pháp và các cách vận dụng cần phải rất linh hoạt. Sau đây là 10 tip để hướng dẫn cách dùng sao cho hợp lý. 1. Sử dụng gem Bullet để xác định n+1 query Cài đặt và config gem theo hướng dẫn (https://github.com/f ...
Sử dụng Eager Loading là một cách rất tuyệt vời để ngăn chặn n+1 query . Tuy nhiên cú pháp và các cách vận dụng cần phải rất linh hoạt. Sau đây là 10 tip để hướng dẫn cách dùng sao cho hợp lý.
1. Sử dụng gem Bullet để xác định n+1 query
Cài đặt và config gem theo hướng dẫn (https://github.com/flyerhzm/bullet) . Sau đó khi bạn chạy chương trình của bạn ở môi trường development trên trình duyệt, sẽ có popup thông báo mỗi khi phát sinh n+1 query. Trong thông báo sẽ chỉ ra những câu query thừa và có thể có gợi ý cách sửa. Sử dụng bullet thực sự rất hữu ích, vì đôi khi chúng ta bỏ qua vấn đề n+1 query khi code. Khi đó bullet sẽ nhắc nhở chúng ta cần sửa.
2. Sử dụng includes cùng tên class dạng số nhiều với quan hệ has_many
Ví dụ:
class Library has_many :books end
Ta có thể includes book khi query lấy library
@libraries = Library.where(size: 'large').includes(:books)
3. Sử dụng includes cùng tên class dạng số ít với quan hệ has_one/belongs_to
Ví dụ:
class Book belongs_to :author end
Ta có thể includes author khi query lấy book
@books = Book.all.includes(:author)
4. Includes nhiều quan hệ cùng lúc
class Library has_many :books has_many :magazines has_many :scrolls end
@libraries = Library.all.includes(:books, :magazines, :scrolls)
5. Includes lồng 1 cấp
class Library has_many :books end
class Book belongs_to :author end
Chỉ với 1 query ta có thể lấy được cả library, books, author, dù library và author không có quan hệ trực tiếp với nhau.
@libraries = Library.all.includes( books: :author )
6. Includes lồng 2 cấp trở lên
class Library has_many :books end
class Book belongs_to :author end
class Author has_one :bio end
@libraries = Library.all.includes( books: [ author: :bio ] )
7. Includes lồng nhau với nhiều quan hệ
@libraries = Library.all.includes(:scrolls, :magazines, books: [:author] )
@libraries = Library.all.includes(books: [:author], scrolls: [:scribe] )
8. Có thể đặt includes sau câu điều kiện của query (where) nhưng phải đặt trước các câu lệnh tính toán và lấy limit
@libraries = Library.where(size: "large").includes(:books).limit(5) @authors = Author.where(genre: "History").includes(:books).limit(3)
9. Includes là cách viết tắt của 2 kiểu eager loading
- preload: Có 2 truy vấn, truy vấn đầu sẽ lấy model chính, truy vấn 2 lấy model đi kèm.
Blog.includes(:posts) Blog Load (2.8ms) SELECT "blogs".* FROM "blogs" Post Load (0.7ms) SELECT "posts".* FROM "posts" WHERE "posts"."blog_id" IN (1, 2, 3)
- eager_load: 2 model quan hệ sẽ được join lại, và chỉ cần sử dụng 1 truy vấn để lấy ra cả 2
Blog.includes(:posts).where(name: 'Blog 1').where(posts: {title: 'Post 1-1'}) SQL (0.2ms) SELECT "blogs"."id" AS t0_r0, "blogs"."name" AS t0_r1 FROM "blogs" LEFT OUTER JOIN "posts" ON "posts"."blog_id" = "blogs"."id" WHERE "blogs"."name" = ? AND "posts"."title" = ? [["name", "Blog 1"], ["title", "Post 1-1"]]
Tùy từng trường hợp mà ta sẽ sử dụng includes theo 1 trong 2 loại trên
10. Gem SearchKick cũng hỗ trợ sử dụng eager loading
Ta có thể includes các model liên quan ngay trong câu search của searhkick
Book.search "moby dick", include: [:author, :isbn, :publisher]
Nguồn
https://medium.com/@codenode/10-tips-for-eager-loading-to-avoid-n-1-queries-in-rails-2bad54456a3f