常量
| 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) Link
从数据库中遍历记录集合(例如,使用 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 提供块,它将返回一个 Enumerator,用于与其他方法链接。
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 选项对于您希望多个工作进程处理同一处理队列时尤其有用。您可以设置每个工作进程的 :start 和 :finish 选项,使工作进程 1 处理 id 1 到 9999 之间的所有记录,工作进程 2 处理 10000 及之后的记录。
# 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) Link
将通过 find 选项找到的每批记录作为数组进行迭代。
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 选项对于您希望多个工作进程处理同一处理队列时尤其有用。您可以设置每个工作进程的 :start 和 :finish 选项,使工作进程 1 处理 id 1 到 9999 之间的所有记录,工作进程 2 处理 10000 及之后的记录。
# 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) Link
迭代 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 选项对于您希望多个工作进程处理同一处理队列时尤其有用。您可以设置每个工作进程的 :start 和 :finish 选项,使工作进程 1 处理 id 1 到 9999 之间的所有记录,工作进程 2 处理 10000 及之后的记录。
# 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