Active Record 批次
常數
DEFAULT_ORDER | = | :asc |
ORDER_IGNORE_MESSAGE | = | "忽略作用域排序,使用 :cursor 和 :order 設定自訂排序。" |
執行個體公開方法
find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, cursor: primary_key, order: DEFAULT_ORDER, &block) 連結
迴圈處理資料庫記錄的集合(例如使用 Scoping::Named::ClassMethods.all
方法)效率不佳,因為會嘗試一次實體化所有物件。
針對這類案例,批次處理方法允許您以批次處理記錄,大幅減少記憶體使用量。
find_each
方法使用 find_in_batches
,批次大小為 1000(或依照 :batch_size
選項指定)。
Person.find_each do |person|
person.do_awesome_stuff
end
Person.where("age > 21").find_each do |person|
person.party_all_night!
end
如果您未為 find_each
提供區塊,它將傳回一個列舉子以串連其他方法
Person.find_each.with_index do |person, index|
person.award_trophy(index + 1)
end
選項
-
:batch_size
- 指定批次大小。預設為 1000。 -
:start
- 指定游標欄位值開始範圍,包含該值。 -
:finish
- 指定游標欄位值結束範圍,包含該值。 -
:error_on_ignore
- 覆寫應用程式設定,指定當關聯中存在順序時是否應引發錯誤。 -
:cursor
- 指定用於批次的欄位(可以是欄位名稱或欄位名稱陣列)。預設為主要金鑰。 -
:order
- 指定游標欄位順序(可以是:asc
或:desc
或包含 :asc 或 :desc 的陣列)。預設為:asc
。class Order < ActiveRecord::Base self.primary_key = [:id_1, :id_2] end Order.find_each(order: [:asc, :desc])
在上方的程式碼中,
id_1
以升冪排序,而id_2
以降冪排序。
尊重限制條件,若有指定限制條件,則不需設定批次大小:可以小於、等於或大於限制條件。
如果有多個工作者處理相同的處理佇列,start
和 finish
會特別有用。您可以讓工作者 1 處理 id 1 到 9999 之間的所有記錄,讓工作者 2 處理 10000 之後的所有記錄,方式是在每個工作者設定 :start
和 :finish
選項。
# In worker 1, let's process until 9999 records.
Person.find_each(finish: 9_999) do |person|
person.party_all_night!
end
# In worker 2, let's process from record 10_000 and onwards.
Person.find_each(start: 10_000) do |person|
person.party_all_night!
end
注意:順序可以是升冪 (:asc) 或降冪 (:desc)。在主要金鑰上會自動設定為升冪(“id ASC”)。這也表示此方法僅在游標欄位可排序(例如整數或字串)時才會作用。
注意:在使用自訂欄位批次處理時,它們應包含至少一個唯一欄位(例如主要金鑰)作為決勝條件。此外,為了降低競爭條件發生的可能性,所有欄位都應為靜態(設定後不可變更)。
注意:批次處理本質上會因其他處理程序修改資料庫而產生競爭條件。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/batches.rb, line 85 def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, cursor: primary_key, order: DEFAULT_ORDER, &block) if block_given? find_in_batches(start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore, cursor: cursor, order: order) do |records| records.each(&block) end else enum_for(:find_each, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore, cursor: cursor, order: order) do relation = self cursor = Array(cursor) apply_limits(relation, cursor, start, finish, build_batch_orders(cursor, order)).size end end end
find_in_batches(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, cursor: primary_key, order: DEFAULT_ORDER) 連結
將透過尋找選項找到的每批記錄當成陣列產生。
Person.where("age > 21").find_in_batches do |group|
sleep(50) # Make sure it doesn't get too crowded in there!
group.each { |person| person.party_all_night! }
end
如果你未對 find_in_batches
提供區塊,它會傳回一個 Enumerator,以與其他方法串連
Person.find_in_batches.with_index do |group, batch|
puts "Processing group ##{batch}"
group.each(&:recover_from_last_night!)
end
要逐一產生每筆記錄,請改用 find_each
。
選項
-
:batch_size
- 指定批次大小。預設為 1000。 -
:start
- 指定游標欄位值開始範圍,包含該值。 -
:finish
- 指定游標欄位值結束範圍,包含該值。 -
:error_on_ignore
- 覆寫應用程式設定,指定當關聯中存在順序時是否應引發錯誤。 -
:cursor
- 指定用於批次的欄位(可以是欄位名稱或欄位名稱陣列)。預設為主要金鑰。 -
:order
- 指定游標欄位順序(可以是:asc
或:desc
或包含 :asc 或 :desc 的陣列)。預設為:asc
。class Order < ActiveRecord::Base self.primary_key = [:id_1, :id_2] end Order.find_in_batches(order: [:asc, :desc])
在上方的程式碼中,
id_1
以升冪排序,而id_2
以降冪排序。
尊重限制條件,若有指定限制條件,則不需設定批次大小:可以小於、等於或大於限制條件。
如果有多個工作者處理相同的處理佇列,start
和 finish
會特別有用。您可以讓工作者 1 處理 id 1 到 9999 之間的所有記錄,讓工作者 2 處理 10000 之後的所有記錄,方式是在每個工作者設定 :start
和 :finish
選項。
# Let's process from record 10_000 on.
Person.find_in_batches(start: 10_000) do |group|
group.each { |person| person.party_all_night! }
end
注意:順序可以是升冪 (:asc) 或降冪 (:desc)。在主要金鑰上會自動設定為升冪(“id ASC”)。這也表示此方法僅在游標欄位可排序(例如整數或字串)時才會作用。
注意:在使用自訂欄位批次處理時,它們應包含至少一個唯一欄位(例如主要金鑰)作為決勝條件。此外,為了降低競爭條件發生的可能性,所有欄位都應為靜態(設定後不可變更)。
注意:批次處理本質上會因其他處理程序修改資料庫而產生競爭條件。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/batches.rb, line 161 def find_in_batches(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, cursor: primary_key, order: DEFAULT_ORDER) relation = self unless block_given? return to_enum(:find_in_batches, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore, cursor: cursor, order: order) do cursor = Array(cursor) total = apply_limits(relation, cursor, start, finish, build_batch_orders(cursor, order)).size (total - 1).div(batch_size) + 1 end end in_batches(of: batch_size, start: start, finish: finish, load: true, error_on_ignore: error_on_ignore, cursor: cursor, order: order) do |batch| yield batch.to_a end end
in_batches(of: 1000, start: nil, finish: nil, load: false, error_on_ignore: nil, cursor: primary_key, order: DEFAULT_ORDER, use_ranges: nil, &block) 連結
產生 ActiveRecord::Relation
物件,以使用一批記錄。
Person.where("age > 21").in_batches do |relation|
relation.delete_all
sleep(10) # Throttle the delete queries
end
如果你未對 in_batches
提供區塊,它會傳回一個 BatchEnumerator
,而且是可列舉的。
Person.in_batches.each_with_index do |relation, batch_index|
puts "Processing relation ##{batch_index}"
relation.delete_all
end
在傳回的 BatchEnumerator
物件上呼叫方法的範例
Person.in_batches.delete_all
Person.in_batches.update_all(awesome: true)
Person.in_batches.each_record(&:party_all_night!)
選項
-
:of
- 指定批次大小。預設值為 1000。 -
:load
- 指定是否應載入關聯。預設值為 false。 -
:start
- 指定游標欄位值開始範圍,包含該值。 -
:finish
- 指定游標欄位值結束範圍,包含該值。 -
:error_on_ignore
- 覆寫應用程式設定,指定當關聯中存在順序時是否應引發錯誤。 -
:cursor
- 指定用於批次的欄位(可以是欄位名稱或欄位名稱陣列)。預設為主要金鑰。 -
:order
- 指定游標欄位順序(可以是:asc
或:desc
或包含 :asc 或 :desc 的陣列)。預設為:asc
。class Order < ActiveRecord::Base self.primary_key = [:id_1, :id_2] end Order.in_batches(order: [:asc, :desc])
在上方的程式碼中,
id_1
以升冪排序,而id_2
以降冪排序。 -
:use_ranges
- 指定是否使用範圍遞迴 (id >= x AND id <= y)。這會使在整個或幾乎整個資料表上遞迴執行速度快數倍。預設只有整個資料表遞迴會使用這種遞迴方式。你可以透過傳入false
來停用此行為。如果你在資料表上執行遞迴,而唯一條件是例如archived_at: nil
(僅記錄的一小部分已封存),選擇這種方法是有意義的。
會遵循限制,而且如果沒有限制,批次大小無需設定,它可以小於、等於或大於限制。
如果有多個工作者處理相同的處理佇列,start
和 finish
會特別有用。您可以讓工作者 1 處理 id 1 到 9999 之間的所有記錄,讓工作者 2 處理 10000 之後的所有記錄,方式是在每個工作者設定 :start
和 :finish
選項。
# Let's process from record 10_000 on.
Person.in_batches(start: 10_000).update_all(awesome: true)
在關聯上呼叫 where 查詢方法的範例
Person.in_batches.each do |relation|
relation.update_all('age = age + 1')
relation.where('age > 21').update_all(should_party: true)
relation.where('age <= 21').delete_all
end
注意:如果你要逐一遞迴記錄,你應該對產生的 BatchEnumerator 呼叫 each_record
Person.in_batches.each_record(&:party_all_night!)
注意:順序可以是升冪 (:asc) 或降冪 (:desc)。在主要金鑰上會自動設定為升冪(“id ASC”)。這也表示此方法僅在游標欄位可排序(例如整數或字串)時才會作用。
注意:在使用自訂欄位批次處理時,它們應包含至少一個唯一欄位(例如主要金鑰)作為決勝條件。此外,為了降低競爭條件發生的可能性,所有欄位都應為靜態(設定後不可變更)。
注意:批次處理本質上會因其他處理程序修改資料庫而產生競爭條件。
來源:顯示 | 在 GitHub 上
# File activerecord/lib/active_record/relation/batches.rb, line 259 def in_batches(of: 1000, start: nil, finish: nil, load: false, error_on_ignore: nil, cursor: primary_key, order: DEFAULT_ORDER, use_ranges: nil, &block) cursor = Array(cursor).map(&:to_s) ensure_valid_options_for_batching!(cursor, start, finish, order) if arel.orders.present? act_on_ignored_order(error_on_ignore) end unless block return BatchEnumerator.new(of: of, start: start, finish: finish, relation: self, cursor: cursor, order: order, use_ranges: use_ranges) end batch_limit = of if limit_value remaining = limit_value batch_limit = remaining if remaining < batch_limit end if self.loaded? batch_on_loaded_relation( relation: self, start: start, finish: finish, cursor: cursor, order: order, batch_limit: batch_limit, &block ) else batch_on_unloaded_relation( relation: self, start: start, finish: finish, load: load, cursor: cursor, order: order, use_ranges: use_ranges, remaining: remaining, batch_limit: batch_limit, &block ) end end