你应该如何在 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 中回填新表?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 PySpark 中的每个分区中回填空值

如何在 Rails 中将当前时间添加 10 天

BigQuery:回填时如何在查询中使用 run_date

如何防止气流回填 dag 运行?

如何在具有某些首字母的新表中插入所有行(pl/sql)

我应该如何在 Rails 6 中重新启动 spring