在Rails 4 / ActiveRecord / MySQL中优化删除操作

jvlzgdj9  于 2023-11-16  发布在  Mysql
关注(0)|答案(3)|浏览(163)

本质上,我们需要删除@user中没有t3记录的T1记录。虽然不是 * 必需的 *,但也可以删除没有T3连接的T2记录。
这就是被推送到生产环境的代码。很明显,它很棒,因为它通过了单元测试(哈!).

T1.where(user_id: @user.id, enabled: true)
    .joins('LEFT JOIN t2 ON t2.t1_id = t1.id')
    .joins('LEFT JOIN t3 ON t3.id = t2.t3_id')
    .where('t3.id IS NULL').delete_all

字符串
生成的SQL:

DELETE FROM `t1`
WHERE `t1`.`id` IN
(SELECT id FROM
     (SELECT `t1`.`id` FROM `t1`
      LEFT JOIN t2 ON t2.t1_id = t1.id
      LEFT JOIN t3 ON t3.id = t2.t3_id
      WHERE `t1`.`user_id` = 65987
      AND `t1`.`enabled` = 1
      AND (t2.id IS NULL)
     ) __active_record_temp
    );


我知道这是这里产生的SQL的唯一原因,是因为它包含在Server 500死锁错误中。* 在测试时,我似乎无法在控制台中显示delete_all查询。* 我能够将查询输出转换为SELECT,并解释,其中显示了最外面的select扫描了数百万行(我相信对于DELETE操作来说,这意味着相同数量的行锁)。

问题:

1.在Rails with ActiveRecord中,基于连接值从一个或多个表中删除记录的最佳方法是什么?
1.我们有什么选择来检查/测试Rails中的SQL输出,以确保我没有性能差和死锁的风险?

更新:图变厚...添加当前关联

class User < ActiveRecord::Base
has_many :T1s
has_many :T2s

class T1 < ActiveRecord::Base
belongs_to :user

class T2Custom < ActiveRecord::Base
self.table_name = "t2"
has_many :T3s, :foreign_key => :t2_id

class T3 < ActiveRecord::Base
belongs_to :T2, foreign_key: "t2_id"
belongs_to :T1

g2ieeal7

g2ieeal71#

使用PRIMARY KEY遍历主表。对于每个块(比如1000行),在单独的事务中执行UPDATE

  • 详细信息 *(这是为DELETE编写的,但可以适用于UPDATE

如果你不需要,不要使用LEFT
确保您有合适的索引,以避免在JOINs上进行表扫描。
如果这些表中的任何一个是多:多,请按照效率提示 * 这里 *。

a1o7rhls

a1o7rhls2#

1.您可以使用Active Record(https://apidock.com/rails/ActiveRecord/Batches/find_in_batchesfind_in_batches方法来删除记录,并且它是可配置的。
1.你可以在测试中使用子弹宝石来验证你没有n+1个查询(https://github.com/flyerhzm/bullet),但我不确定这是你的问题。
您可以使用Active Record(https://apidock.com/rails/ActiveRecord/Relation/to_sql.to_sql函数来解释任何查询。

9q78igpj

9q78igpj3#

作为一个快速(可能是永久的)修复,我决定用.destroy_all替换.delete_all,它基本上只运行内部查询,扫描27行,然后逐个示例化和删除记录,如果需要的话,还可以运行destroy回调。
LEFT JOIN的作用是查找此时由于其他表中的子记录从未被创建而无效的记录。作用域中的记录数不应超过30。代码试图查找并删除不应该存在的记录。这意味着,95%的情况下,代码段将在(快速)初始查询返回空值后退出。
按照ruby_newbie的建议运行.to_sql对解决这个问题非常有帮助。

相关问题