我正试图将5,000,000行从一个Postgre DB移到另一数据库。两个连接都在Hikari CP连接池中。

我浏览了许多文档和帖子。它给我留下了下面的代码。但这并不是真的可用:

(jdbc/with-db-connection [tx {:datasource source-db}]
  (jdbc/query tx
      [(jdbc/prepare-statement (jdbc/get-connection tx)
                                answer-sql
                                {:fetch-size 100000})]
                  {:result-set-fn (fn [result-set]
                                    (jdbc/insert-multi!
                                     {:datasource target-db}
                                     :migrated_answers
                                     result-set))}))

我已经尝试了很多不同的形式。 jdbc/with-db-transaction或我能想到的其他任何方法都没有太大帮助。
  • 许多教程和帖子仅提及如何整体处理结果。用小表进入RAM绝对可以,但是看起来很快。但这种情况并非如此。
  • 因此,当我正确使用:fetch-size时,我的RAM不会爆炸(hocus pocus),因此传输很慢,DB连接的两个连接都在“ Activity ”和“事务中的空闲”状态之间切换。我从来没有等那么久才找到实际传输的任何数据!

    当我在Talend Open Studio(生成Java代码的ETL工具)中创建此简单批处理时,它将在5分钟内传输所有数据。并且在那里的“游标大小”也设置为100000。我认为Clojure的干净代码应该更快。
  • 我得到的最快结果是下面的代码。我认为这是因为:as-array参数。如果我不使用:max-rows参数内存会爆炸,因为它没有被延迟处理,那么我就不能在整个Transfet中使用它。为什么?我不明白这里的规则。
    (jdbc/with-db-transaction [tx {:datasource source-db}]
      (jdbc/query tx
                  [(jdbc/prepare-statement (:connection tx)
                                            answer-sql
                                           {:result-type :forward-only
                                            :concurrency :read-only
                                            :fetch-size 2000
                                            :max-size 250000})]
                  {:as-arrays? true
                   :result-set-fn (fn [result-set]
                                    (let [keys (first result-set)
                                          values (rest result-set)]
                                      (jdbc/insert-multi!
                                         {:datasource dct-db}
                                          :dim_answers
                                           keys values)))}))
    

  • 我将不胜感激任何帮助或信息,我将不胜感激。

    最佳答案

    我认为这里的主要观察结果是,虽然您的查询是从一个数据库懒惰地流式传输结果,但是您的插入只是向另一个数据库的一个巨大写入。关于内存使用情况,如果最后要为单个写入操作收集所有这些结果(内存中),则我认为是否以延迟方式流式传输查询结果没有太大区别。

    平衡内存使用量和吞吐量的一种方法是分批写入:

    (db/with-db-transaction [tx {:datasource source-db}]
      (db/query tx
        [(db/prepare-statement (:connection tx)
                               answer-sql
                               {:result-type :forward-only
                                :concurrency :read-only
                                :fetch-size 2000})]
        {:as-arrays? true
         :result-set-fn (fn [result-set]
                          (let [keys (first result-set)
                                values (rest result-set)]
                            (doseq [batch (partition-all 2000 values)]
                              (db/insert-multi! {:datasource dct-db}
                                                :dim_answers
                                                keys
                                                batch))))}))
    

    不同之处在于,这是使用partition-all批量插入values(与:fetch-size大小相同,但我确定可以对其进行调整)。通过将JVM最大堆大小设置为-Xmx1g之类,将这种方法的性能/内存使用与其他方法进行比较。我无法使用此堆大小来完成非批处理版本。

    我能够在大约1分钟内在笔记本电脑上的本地PostgreSQL数据库之间迁移600万个小块行,并使用java进行迁移。我也使用了HikariCP。

    如果确实要分批插入,则如果适合您的用例,则可能需要考虑将所有插入物包装在单个事务中。为了简洁起见,我在此处省略了其他交易。

    如果我不使用:max-size参数,内存会爆炸

    我在最新的clojure.java.jdbc中找不到对该选项的任何引用(除规范外),并且没有影响我的测试。我确实看到了:max-rows,但您当然不想要。

    我认为这是因为:as-array参数。

    我希望这对内存使用很有帮助。行向量应该比行映射更节省空间。

    10-02 06:44
    查看更多