活動記錄查詢優化技巧:提升Rails on Ruby應用程式效能

活動記錄查詢優化技巧:提升Rails on Ruby應用程式效能

作為 Ruby on Rails 開發人員,瞭解優化資料庫查詢以提高效能和增強使用者體驗非常重要。Active Record 是 Rails ORM(物件關係對映)工具,具有高效查詢資料庫的強大功能。

查詢優化是一個複雜的課題,這方面的書籍很多。在此,我們將探討一些技術和技巧,以優化 Active Record 查詢,提高應用程式的速度和響應能力。

使用選擇性列檢索

優化 Active Record 查詢的最有效方法之一是從資料庫中只檢索必要的列。通過指定所需的確切列,可以最大限度地減少在資料庫和 Rails on Ruby 應用程式之間傳輸的資料。例如,如果我們只想使用資料庫中的名稱,那麼我們可以使用以下列:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# Unoptimized Practice: Retrieving all columns
User.all
# Optimized Practice: Selecting specific columns
User.select(:id, :name)
# Unoptimized Practice: Retrieving all columns User.all # Optimized Practice: Selecting specific columns User.select(:id, :name)
# Unoptimized Practice: Retrieving all columns
User.all
# Optimized Practice: Selecting specific columns
User.select(:id, :name)

採用快速載入

通過提前載入關聯記錄,快速載入有助於減少資料庫查詢次數。通過預先載入關聯記錄,可以避免 N+1 查詢問題,即對每條關聯記錄執行額外查詢。下面是一個 N+1 查詢問題的示例,然後我們將介紹一種名為俄羅斯娃娃快取(Russian Doll Caching)的替代技術。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# N+1 query problem
users = User.all
users.each { |user| puts user.posts.count } # Executes one query for users and N queries for posts (N = number of users)
# N+1 query problem users = User.all users.each { |user| puts user.posts.count } # Executes one query for users and N queries for posts (N = number of users)
# N+1 query problem
users = User.all
users.each { |user| puts user.posts.count }  # Executes one query for users and N queries for posts (N = number of users)

在上述示例中,我們先獲取所有使用者,然後遍歷每個使用者以獲取其相關帖子的計數。這樣會執行 N 次額外查詢,導致效能下降。

為了解決這個問題,我們可以使用 includes 方法進行急迫載入,如下所示:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# Eager loading solution
users = User.includes(:posts).all
users.each { |user| puts user.posts.count } # Executes two queries: one for users and one for posts (regardless of user count)
# Eager loading solution users = User.includes(:posts).all users.each { |user| puts user.posts.count } # Executes two queries: one for users and one for posts (regardless of user count)
# Eager loading solution
users = User.includes(:posts).all
users.each { |user| puts user.posts.count }  # Executes two queries: one for users and one for posts (regardless of user count)

通過使用 includes(:posts) 方法,我們只需兩次查詢即可載入所有使用者的關聯帖子。 includes 方法有效地預載了關聯資料,無需額外查詢,大大提高了效能。

替代技術:俄羅斯娃娃快取

除了急切載入,優化資料庫查詢的另一種技術是俄羅斯娃娃快取。這種技術涉及快取分層資料結構及其關聯,從而無需冗餘查詢即可進行高效檢索。

讓我們舉一個例子,檢索部落格文章及其相關評論的列表:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# Without caching (N+1 query problem)
@posts = Post.all
@posts.each do |post|
@comments = post.comments
# Perform actions with comments
end
# Without caching (N+1 query problem) @posts = Post.all @posts.each do |post| @comments = post.comments # Perform actions with comments end
# Without caching (N+1 query problem)
@posts = Post.all
@posts.each do |post|
@comments = post.comments
# Perform actions with comments
end

在上述程式碼中,迴圈的每次迭代都會觸發一次查詢,以獲取每篇文章的評論,從而導致 N 次額外查詢。

要實現俄羅斯娃娃快取,我們可以使用片段快取等快取方法。通過快取整個檢視或部分檢視,包括相關記錄,我們可以避免多餘的查詢。下面是一個例子:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# With Russian Doll Caching
<% cache @posts do %>
<% @posts.each do |post| %>
<% cache post do %>
<%= post.title %>
<% post.comments.each do |comment| %>
<%= comment.content %>
<% end %>
<% end %>
<% end %>
<% end %>
# With Russian Doll Caching <% cache @posts do %> <% @posts.each do |post| %> <% cache post do %> <%= post.title %> <% post.comments.each do |comment| %> <%= comment.content %> <% end %> <% end %> <% end %> <% end %>
# With Russian Doll Caching
<% cache @posts do %>
<% @posts.each do |post| %>
<% cache post do %>
<%= post.title %>
<% post.comments.each do |comment| %>
<%= comment.content %>
<% end %>
<% end %>
<% end %>
<% end %>

在此實現中,我們使用 cache 輔助器快取 @posts 物件和每個單獨的帖子。在渲染檢視或部分檢視時,Rails 會在執行任何程式碼前檢查快取,從而消除了額外查詢的需要。

通過實施俄羅斯娃娃快取,你可以最大限度地減少資料庫查詢,並高效地檢索分層資料結構及其關聯,從而優化效能。

快速載入是一種強大的技術,可通過預載入關聯來避免 N+1 查詢問題。此外,俄羅斯娃娃快取(Russian Doll Caching)通過快取分層資料結構及其關聯,為優化資料庫查詢提供了另一種方法。

通過使用這些技術,您可以提高 Ruby on Rails 應用程式的效能和響應速度。選擇最適合你的應用程式需求和複雜性的方法。

在開發應用程式時,有一些工具可以幫助你識別 N+1 查詢。例如,BulletRack Mini ProfilerProsopite 等工具就值得您在專案中嘗試。

利用索引

索引可以讓資料庫更快地定位記錄,從而提高查詢效能。在 Active Record 中,您可以在資料庫模式中新增索引,尤其是在查詢中經常使用的列上。例如:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# Add index to improve performance
add_index :users, :email
# Add index to improve performance add_index :users, :email
# Add index to improve performance
add_index :users, :email

此外,還有一些 gem 可以幫助您確定應在何處新增索引,如 lol_dbadatabase_consistency gem。

利用條件優化資料庫查詢

在構建查詢時,請考慮使用資料庫特定的條件功能來避免不必要的資料檢索。Active Record 提供了各種優化查詢條件的方法,如 wherelimitoffsetorder 。下面是一個例子:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# Unoptimized query
users = User.all
users.select { |user| user.age > 18 && user.age < 25 }
# Optimized query
users = User.where(age: 19..24).all
# Unoptimized query users = User.all users.select { |user| user.age > 18 && user.age < 25 } # Optimized query users = User.where(age: 19..24).all
# Unoptimized query
users = User.all
users.select { |user| user.age > 18 && user.age < 25 }
# Optimized query
users = User.where(age: 19..24).all

批量處理大型資料集

由於記憶體限制,處理大型資料集可能會影響效能。可以考慮使用批處理技術將查詢分解成較小的塊,從而減少記憶體使用。這種方法在執行更新或刪除記錄等操作時尤其有用。

不過,要實現最佳效能,正確使用批處理非常重要。讓我們來看一個批處理不佳的示例,看看它如何對應用程式產生負面影響:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# Unoptimized Practice: Naive batch processing
users = User.all
users.each do |user|
# Perform operations on user record
end
# Unoptimized Practice: Naive batch processing users = User.all users.each do |user| # Perform operations on user record end
# Unoptimized Practice: Naive batch processing
users = User.all
users.each do |user|
# Perform operations on user record
end

在上述程式碼片段中,我們使用 User.all 從資料庫中獲取所有使用者記錄。在處理大型資料集時,這可能會帶來嚴重的效能問題,因為它會一次性將所有記錄載入到記憶體中。因此,應用程式可能會消耗過多的記憶體資源,執行速度也會減慢。

為了解決這個問題,讓我們使用更優化的批處理方法來重構程式碼:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# Optimized Practice: Batch processing with `find_in_batches`
User.find_in_batches(batch_size: 1000) do |users_batch|
users_batch.each do |user|
# Perform operations on user record
end
end
# Optimized Practice: Batch processing with `find_in_batches` User.find_in_batches(batch_size: 1000) do |users_batch| users_batch.each do |user| # Perform operations on user record end end
# Optimized Practice: Batch processing with `find_in_batches`
User.find_in_batches(batch_size: 1000) do |users_batch|
users_batch.each do |user|
# Perform operations on user record
end
end

在這個更新的實現中,我們使用了 Active Record 提供的 find_in_batches 方法。該方法以 batch_size 指定的較小批次獲取記錄,從而減少了記憶體佔用。它在自己的記憶體上下文中處理每批記錄,大大提高了應用程式處理大型資料集時的效能。

通過使用 find_in_batches ,可以有效地以節省記憶體的方式處理大型資料集。請記住,要根據應用程式的需要和可用的系統資源來調整 batch_size

小結

優化 Active Record 查詢對於提高 Rails on Ruby 應用程式的效能至關重要。通過遵循本文概述的技巧(包括選擇性列檢索、急切載入、索引、優化條件和批處理),您可以顯著提高資料庫查詢的速度和效率。

請記住,對查詢進行微調不僅能改善使用者體驗,還能減輕資料庫伺服器的負載。牢記這些優化技巧,你的 Ruby on Rails 應用程式就能流暢執行,即使資料量很大。Happy coding!

評論留言