使用ActiveRecord find_in_batches方法删除大数据

时间:2015-10-08 17:28:09

标签: ruby-on-rails ruby activerecord

好的,所以我知道在处理非常大的数据时,我们可以使用find_in_batches,正如我所理解的那样,Model.all.each能够以非常快的方式完成find_in_batches的工作,效率更高

现在,我正在尝试删除一个非常大的数据,我正在考虑使用相同的database.rake批量删除它们。

以下是我所拥有的(来自佣金任务old_messages = TextMessage.where("created_at < ?", number.days.ago ) old_messages.find_in_batches do |batch| batch.delete_all end ):

ArgumentError: wrong number of arguments (0 for 1..3)
/Users/Sunday/.rvm/gems/ruby-2.2.0/gems/httparty-0.13.5/lib/httparty.rb:66:in `logger'
/Users/Sunday/.rvm/gems/ruby-2.2.0/gems/activerecord-4.2.2/lib/active_record/relation/delegation.rb:94:in `public_send'
/Users/Sunday/.rvm/gems/ruby-2.2.0/gems/activerecord-4.2.2/lib/active_record/relation/delegation.rb:94:in `block in method_missing'
/Users/Sunday/.rvm/gems/ruby-2.2.0/gems/activerecord-4.2.2/lib/active_record/relation.rb:302:in `scoping'
/Users/Sunday/.rvm/gems/ruby-2.2.0/gems/activerecord-4.2.2/lib/active_record/relation/delegation.rb:94:in `method_missing'
/Users/Sunday/.rvm/gems/ruby-2.2.0/gems/activerecord-4.2.2/lib/active_record/relation/batches.rb:112:in `find_in_batches'
/Users/Sunday/workspace/resilience/lib/tasks/database.rake:18:in `block (2 levels) in <top (required)>'

但是当我运行它时,我收到以下错误:

database.rake

第18行的old_messages.find_in_batches do |batch|为:find_in_batches

那么,是否可以像我想要的那样使用<input ng-model="query" type="text" placeholder="Filter by" autofocus> <ul class="gNow"> <li ng-repeat="item in selRole.selectRoles | multifilter:query"> <form name="{{item.formName}}" ng-submit="selRole.selectRole(item.formName)" novalidate> ... 进行删除?如果答案是肯定的,那么我做错了什么。感谢所有回复。

5 个答案:

答案 0 :(得分:12)

我认为没有人回答你的问题。

要回答“你做错了什么”,你能否以这种方式使用'find_in_batches':

'delete_all'不起作用的原因是因为'delete_all'仅适用于activerecord关系。当你使用'find_in_batches'时,变量'batch'现在只是一个普通的数组,可能有它自己的'delete_all'方法不同,

如果要删除数千条记录,则可能需要“find_in_batches”。所以以前的答案是不正确的。 (这可能会导致内存超出异常和超时)

请注意,这与您显示的原始错误无关,但您不能将'batch'与'delete_all'一起使用,因为'batch'是一个数组,'delete_all'用于activerecords

如何使用find_in_batches删除

我遇到了类似的问题

user.posts.destroy_all

由于成千上万的帖子而超载服务器(这是一个例子,我的实际模型不是'帖子')

您可以使用

user.posts.select(:id).find_in_batches(batch_size: 100) do |ids|
  Post.where(id: ids).delete_all
end

如果是一次sql调用,它会尝试将所有删除项一次存储在内存中,这可能会破坏服务器, 这将具有可管理的sql调用大小。

答案 1 :(得分:2)

新功能:#in_batches(https://api.rubyonrails.org/classes/ActiveRecord/Batches.html#method-i-in_batches

Person.where("age > 21").in_batches do |relation|
  relation.delete_all
  sleep(10) # Throttle the delete queries
end

答案 2 :(得分:1)

使用Model.in_batches.destroy_all

请注意dependent个关联,这些关联可能仍会在不进行批处理的情况下加载。我创建了一个宝石来解决此问题:batch_dependent_associations

另请参见Rails中的dhh问题:Relation#destroy_all should perform its work in batches

答案 3 :(得分:0)

正在运行

TextMessage.where("created_at < ?", number.days.ago ).delete_all 

将执行单个查询,并且足够有效。你这里不需要批次。

答案 4 :(得分:0)

像其他答案建议的那样,将in_batchesfind_in_batchesdelete_all一起使用,将导致每批两次查询。一个SELECT查询以获取记录,一个DELETE查询以删除记录。

在不获取记录的情况下进行批量删除的方法可能是:

def delete_batched(records, batch_size: 1000)
  loop do
    amount_deleted = records.reorder(:id).limit(batch_size).delete_all
    break if amount_deleted < batch_size
  end
end

delete_batched(TextMessage.where("created_at < ?", number.days.ago))

(据我了解,a deterministic processing order is desirable会这样删除,因此按:id进行排序。使用reorder将替换所有已经设置的order