10 Tips to Avoid n+1 Queries in Rails

Việc sử dụng eager loading để tránh N + 1 query trong rails rất là hiệu quả nếu như bạn biết cách sử dụng chúng. Tuy nhiên cú pháp và việc tùy biến sử dụng trong từng trường hợp thì có một chút rắc rối. Dưới đây là 10 thủ thật để sử dụng eager loading đúng cách.

Sử dụng gem Bullet để tìm n+1 query

Cài đặt gem, config và chạy bundle exec rails g bullet:install. Nếu bullet phát hiện có n + 1 query nó sẽ hiển thị ra 1 popup thông báo cùng với các thông tin chi tiết để ta biết ta bị n + 1 query ở đâu và cách giải quyết với các association đó. Bullet cũng giúp phát hiện các eager loading không dùng đến.

Sử dụng eager load để tránh n + 1 query

Quan hệ has_many cần sử dụng includes với tên class số nhiều

@libraries = Library.where(size: 'large').includes(:books)

Quan hệ belongs_to / has_one cần sử dụng includes với tên class số ít

@books = Book.all.includes(:author)

Load nhiều associations bằng cách sử dụng dấu phẩy

@library = Library.all.includes(:books, :magazines, :scrolls)

Load assocation lồng nhau 1 cấp sử dụng hash

@library = Library.all.includes( books: :author )

Load assocation lồng nhau 2 cấp hoặc nhiều hơn sử dụng hash lồng nhau

@library = Library.all.includes( books: [ author: :bio ] )

Load assocation phức tạp sử dụng dấu phẩy và hash lồng nhau

@library = Library.all.includes( { :books => :author }, :scrolls, :magazines )

Đặt include ngày sau truy vấn điều kiện nhưng đặt trước limit hoặc calculation

@libraries = Library.where(size: "large").includes(:books).limit(5)

Hàm includes thực chất là đường tắt của hai loại eager loading

Hai phương thức đó là preload và eager_load, chúng khác nhau một chút về cách lấy dữ liệu.

  • Preload: thực hiện 2 câu query, câu thứ nhất lấy ra dữ liệu của model chính, câu thứ hai lấy dữ liệu từ các model liên kết
  • Eager load: thực hiện 1 câu query sử dụng left outer join, join model chính và các model liên kết. Sử dụng includes cho phép rails lựa chọn giúp bạn eager load hoặc preload. Trong trường hợp rails lựa chọn không hiệu quả thì bạn có thể tự lựa chọn sử dụng preload hay eager_load.

Sử dụng gem Searchkick để hỗ trợ eager load

Kết quả trả về từ truy vấn elastic search của searchkick có thể được tìm nạp trước model liên kết với nó. Cú pháp như sau:

Book.search "moby dick", include: [:author, :isbn, :publisher]

Nguồn tham khảo

  1. https://medium.com/@codenode/10-tips-for-eager-loading-to-avoid-n-1-queries-in-rails-2bad54456a3f
  2. https://github.com/flyerhzm/bullet
  3. http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations

All Rights Reserved