【问题标题】:Avoid N+1 query in large data set避免在大数据集中进行 N+1 查询
【发布时间】:2018-11-14 22:27:11
【问题描述】:

我有一个迁移,它使用新的属性值更新现有记录。该模型称为“MyRecord”。它在数据库中有数百万条记录,新的 unit_id 列为空。我想用特定值更新该 unit_id 列:

MyRecord.find_each do |record|
  unit_id = Unit.calculate_unit_from_old_columns(record.legacy_column_1, record.legacy_column_2).first.id
  record.update unit_id: unit_id
end

这会创建很多 N+1 查询:

SELECT units.* FROM units WHERE units.item_1 = 'Electronics' AND units.item_2 = 'Auto' 
UPDATE my_records SET unit_id='43' WHERE legacy_column_1 = 'Legacy Electronics' AND legacy_column_2 = 'Legacy Auto';

其中一些 N+1 查询是重复的。我在日志中看到了很多这样的内容:

SELECT units.* FROM units WHERE units.item_1 = 'Electronics' AND units.item_2 = 'Auto' 
SELECT units.* FROM units WHERE units.item_1 = 'Electronics' AND units.item_2 = 'Auto' 

我熟悉通过包含进行急切加载。但是,当运行此迁移以更新现有数据时,还没有关联。所以我不能这样做:

record.includes(:unit)

如何消除 N+1 个查询并缓存查询,以便在重复查询时不会再次访问数据库?

【问题讨论】:

  • calculate_unit_from_old_columns 有多复杂?最好将所有内容移动到更新查询(SET unit_id = DO_STUFF(legacy_column_1, legacy_column_2)。如果太复杂,您应该准备批量更新(将 1000 条记录映射到 (id, new_unit_id) 对,并将其用于更新查询。
  • @MarcinKołodziej 你能给我一个批量更新的例子或指向一个链接吗?
  • calculate_unit_from_old_columns 是一个命名范围:scope : calculate_unit_from_old_columns, ->(item1, item2) { where(item_1: item1, item2: item_2 ) }
  • 哦,对了,这行很长。好吧,你可以用 join 写一个简单的更新。你用 MySQL 和 PostgreSQL 标记了你的问题,它们有不同的语法,应该很容易搜索。
  • @MarcinKołodziej 我正在使用 MySQL

标签: mysql sql ruby-on-rails


【解决方案1】:

使用简单的查询,如果运行时间过长可以考虑批处理:

MyRecord.connection.execute(
  "UPDATE my_records, units 
   SET unit_id = units.id 
   WHERE units.item_1 = legacy_column_1 AND units.item_2 = legacy_column_2"
)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-30
    • 1970-01-01
    • 2015-09-21
    • 1970-01-01
    • 2017-11-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多