+1

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/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ù libraryauthor 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

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí