你应该如何在 Rails 中回填新表?
Posted
技术标签:
【中文标题】你应该如何在 Rails 中回填新表?【英文标题】:How should you backfill a new table in Rails? 【发布时间】:2016-02-26 20:24:10 【问题描述】:我正在创建一个新表,需要使用以下一次性 rake 任务回填基于用户帐户(超过几十万个)的数据。
我决定为每 2000 个用户创建一个大的 INSERT 字符串并执行该查询。
代码大致如下:
task :backfill_my_new_table => :environment do
inserts = []
User.find_each do |user|
tuple = # form the tuple based on user and user associations like (1, 'foo', 'bar', NULL)
inserts << tuple
end
# At this point, the inserts array is of size at least 20,000
conn = ActiveRecord::Base.connection
inserts.each_slice(2000) do |slice|
sql = "INSERT INTO my_new_table (ref_id, column_a, column_b, column_c) VALUES #inserts.join(", ")"
conn.execute(sql)
end
end
所以我想知道,有没有更好的方法来做到这一点?我采用的方法有哪些缺点?我应该如何改进它?如果我没有对 inserts
数组进行切片,而是简单地执行了一个带有几十万个 VALUES 元组的 INSERT
怎么办?这种方法有什么缺点?
谢谢!
【问题讨论】:
你为什么不使用封装在事务中的 MyNewTable 方法来加速插入?此外,当前的实现使您可以使用 SQL 注入。 哦,我错过了你一次做多个插入。那确实会更快(但不确定如果您将普通插入包装在每个交易中,例如 1000 个。 【参考方案1】:取决于您使用的 PG 版本,但在大多数情况下将数据批量加载到表中,这已经足够了:
尽可能使用COPY 而不是INSERT; 如果使用多个 INSERT,禁用自动提交并将所有 INSERT 包装在一个事务中,即BEGIN; INSERT ...; INSERT ...; COMMIT;
禁用目标表上的索引和检查/约束;
禁用表触发器;
alter table所以变成unlogged(PG 9.5以后,数据导入后别忘了开启登录),或者增加max_wal_size这样WAL就不会被淹没了
20k 行对于 PG 来说并不是什么大问题,因此一个事务中的 2k 切片插入就可以了,除非涉及到一些非常复杂的触发器/检查。也值得一读PG manual section on bulk loading。
UPD:和a little bit old, yet wonderful piece from depesz,摘录:
所以,如果你想尽可能快地插入数据——使用复制(或者更好——pgbulkload)。如果由于某种原因您不能使用复制,则使用多行插入(8.2 中的新功能!)。然后如果可以的话,将它们捆绑在事务中,并使用准备好的事务,但通常——它们不会给你太多。
【讨论】:
以上是关于你应该如何在 Rails 中回填新表?的主要内容,如果未能解决你的问题,请参考以下文章