我有大量的Product对象,它们都有一个关联的“变量”。我不能只是delete_all产品,因为我需要关联也被删除。当我使用Products.all.destroy_all时,它会冻结,并没有完成销毁所有产品,然后它迫使我取消它并将其回滚。有解决办法吗?
delete_all
Products.all.destroy_all
pgvzfuti1#
对于大量的对象和与它们相关联的“变量”,你必须使用destroy_all。
Product.all.destroy_all
由于destroy_all实际上加载了整个关系,然后迭代地逐个销毁记录,因此很容易耗尽内存。因此,让我们在默认情况下做正确的事情,并在默认情况下以100个为一批进行这项工作,并允许您像这样指定批量大小:destroy_all(batch_size: 100).
destroy_all(batch_size: 100).
Product.destroy_all(batch_size: 100)
或您也可以使用in_batches
Product.in_batches(of: 200).destroy_all
ao218c7q2#
如果“变量”记录没有任何必须从数据库中删除的依赖项,则使用dependent: :delete_all而不是dependent: :destroy
dependent: :delete_all
dependent: :destroy
products_count = Product.count # Determine how many batches need to be run number_of_iterations = (products_count.to_f / 1000).ceil (1..number_of_iterations).each do |i| Product.limit(1000).delete_all end
当处理大量的数据时,最好是批量删除。如果在单个事务中删除的行超过5,000行,则数据库将锁定。这意味着在事务期间,任何其他正在运行的进程都无法访问整个表。这可能意味着一些严重的问题,为您的网站的用户,而一个错误正在发生。
nkkqxpd93#
虽然in_batches很方便,但请记住,它掩盖了一些可能影响您的细节。对于10,000条记录,有10,020个查询:
in_batches
所以每个id从db到ruby,再到db到ruby,然后再回到db。一个相对简单的优化是让in_batches为您获取记录,然后手动销毁它们。这可能只少了10个查询,但这些查询要小得多,只向服务器发送10,020个整数(而之前是20,020个):
Product.in_batches(of: 1000, load: true) { |records| records.each(&:destroy) }
现在,10,000条记录需要10,010个查询:
由于delete_all返回删除的记录数,因此您不需要预先计算记录数。你只需要循环,直到它没有更多的记录要删除。您将向服务器发送10个绑定变量,但不获取任何变量。您还将从数据库中获取0条记录和10个整数:
while(Product.limit(1000).delete_all == 1000) ; end # OR while(Product.limit(1000).delete_all > 0) ; end
我在这里举两个例子。第一种方法检测部分批处理并中止。第二种方法需要再运行一次删除查询(这将删除0条记录)。有时候让这段代码不知道批处理大小会更方便,所以我包含了第二个例子。现在,您确实说您有一个关联的第二个表,这将阻碍delete_all解决方案。如果您愿意专门为关联编码,并且关联不是STI,那么您可以手动删除两者。我在这里使用了Product.all,但是如果需要的话,您可以包含一个where()类型的范围。如果它是多态的,则需要添加:product_type => Product.polymorphic_name。
Product.all
where()
:product_type => Product.polymorphic_name
Variant.where(:product_id => Product.all.select(:id)).delete_all Product.all.delete_all
另外,请注意,我使用的是select(:id),而不是上面的pluck(:id)。select将创建一个子查询,pluck将返回并发送所有产品ID。(不是你想要的)
select(:id)
pluck(:id)
select
pluck
3条答案
按热度按时间pgvzfuti1#
对于大量的对象和与它们相关联的“变量”,你必须使用destroy_all。
由于destroy_all实际上加载了整个关系,然后迭代地逐个销毁记录,因此很容易耗尽内存。因此,让我们在默认情况下做正确的事情,并在默认情况下以100个为一批进行这项工作,并允许您像这样指定批量大小:
destroy_all(batch_size: 100).
或
您也可以使用in_batches
ao218c7q2#
如果“变量”记录没有任何必须从数据库中删除的依赖项,则使用
dependent: :delete_all
而不是dependent: :destroy
当处理大量的数据时,最好是批量删除。如果在单个事务中删除的行超过5,000行,则数据库将锁定。这意味着在事务期间,任何其他正在运行的进程都无法访问整个表。这可能意味着一些严重的问题,为您的网站的用户,而一个错误正在发生。
nkkqxpd93#
虽然
in_batches
很方便,但请记住,它掩盖了一些可能影响您的细节。对于10,000条记录,有10,020个查询:
所以每个id从db到ruby,再到db到ruby,然后再回到db。
一个相对简单的优化是让
in_batches
为您获取记录,然后手动销毁它们。这可能只少了10个查询,但这些查询要小得多,只向服务器发送10,020个整数(而之前是20,020个):现在,10,000条记录需要10,010个查询:
由于
delete_all
返回删除的记录数,因此您不需要预先计算记录数。你只需要循环,直到它没有更多的记录要删除。您将向服务器发送10个绑定变量,但不获取任何变量。您还将从数据库中获取0条记录和10个整数:我在这里举两个例子。第一种方法检测部分批处理并中止。第二种方法需要再运行一次删除查询(这将删除0条记录)。有时候让这段代码不知道批处理大小会更方便,所以我包含了第二个例子。
现在,您确实说您有一个关联的第二个表,这将阻碍
delete_all
解决方案。如果您愿意专门为关联编码,并且关联不是STI,那么您可以手动删除两者。我在这里使用了
Product.all
,但是如果需要的话,您可以包含一个where()
类型的范围。如果它是多态的,则需要添加:product_type => Product.polymorphic_name
。另外,请注意,我使用的是
select(:id)
,而不是上面的pluck(:id)
。select
将创建一个子查询,pluck
将返回并发送所有产品ID。(不是你想要的)