你应该如何在Rails中回填新表?

use*_*421 5 postgresql ruby-on-rails query-optimization rails-activerecord

我正在创建一个新表,需要使用基于用户帐户(超过几十万)的数据进行回填,并执行以下一次性佣金任务.

我决定要为每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
Run Code Online (Sandbox Code Playgroud)

所以我想知道,有更好的方法吗?我采取的方法有哪些缺点?我该怎样改进它?如果我没有对inserts数组进行切片并且只执行一个INSERT超过几十万个VALUES元组的单个怎么办?这种方法的缺点是什么?

谢谢!

НЛО*_*НЛО 0

取决于您使用的 PG 版本,但在大多数情况下将数据批量加载到表中,这已经足够了:

\n\n
    \n
  • 尽可能尝试使用COPY而不是 INSERT;
  • \n
  • 如果使用多个 INSERT,请禁用自动提交并将所有 INSERT 包装在单个事务中,即BEGIN; INSERT ...; INSERT ...; COMMIT;
  • \n
  • 禁用目标表上的索引和检查/约束;
  • \n
  • 禁用表触发器;
  • \n
  • 更改表,使其不再记录(从 PG 9.5 开始,不要忘记在数据导入后打开日志记录),或增加max_wal_size这样WAL就不会被淹没
  • \n
\n\n

20k 行对于 PG 来说并不是什么大问题,因此一个事务中的 2k 切片插入就可以了,除非涉及一些非常复杂的触发器/检查。PG 手册中有关批量加载的部分也值得阅读。

\n\n

UPD:来自depesz 的一篇有点旧但精彩的文章,摘录:

\n\n
\n

因此,如果您想尽快插入数据 \xe2\x80\x93 使用 copy (或者更好的是 \xe2\x80\x93 pgbulkload)。如果出于某种原因你不能使用复制,那么使用多行插入(8.2 中的新功能!)。然后,如果可以的话,将它们捆绑在事务中,并使用准备好的事务,但通常 \xe2\x80\x93 它们不会给你太多。

\n
\n