10 Tips for Eager Loading to Avoid n+1 Queries in Rails
Bài đăng này đã không được cập nhật trong 6 năm
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
All rights reserved