【问题标题】:Jooq batch record insertJooq 批量记录插入
【发布时间】:2017-12-29 15:57:22
【问题描述】:

我目前正在尝试批量插入许多记录(~2000),而 Jooq 的 batchInsert 没有做我想要的。

我正在将 POJO 转换为 UpdatableRecords,然后我正在执行 batchInsert,它正在为每条记录执行插入操作。因此,Jooq 为每个批量插入执行了大约 2000 次查询,这正在扼杀数据库性能。

它正在执行这段代码(jooq 的批量插入):

for (int i = 0; i < records.length; i++) {
            Configuration previous = ((AttachableInternal) records[i]).configuration();

            try {
                records[i].attach(local);
                executeAction(i);
            }
            catch (QueryCollectorSignal e) {
                Query query = e.getQuery();
                String sql = e.getSQL();

                // Aggregate executable queries by identical SQL
                if (query.isExecutable()) {
                    List<Query> list = queries.get(sql);

                    if (list == null) {
                        list = new ArrayList<Query>();
                        queries.put(sql, list);
                    }

                    list.add(query);
                }
            }
            finally {
                records[i].attach(previous);
            }
        }

我可以这样做(因为 Jooq 在内部做同样的事情):

records.forEach(UpdatableRecord::insert);

代替:

jooq.batchInsert(records).execute();

如何告诉 Jooq 以批处理模式创建新记录?我应该将记录转换为绑定查询然后调用batchInsert 吗?有任何想法吗? ;)

【问题讨论】:

    标签: java database postgresql bulkinsert jooq


    【解决方案1】:

    jOOQ 的 DSLContext.batchInsert() 为每组具有相同生成的 SQL 字符串的连续记录创建一个 JDBC 批处理语句(不幸的是,Javadoc 没有正式定义这一点)。

    当您的记录如下所示时,这可能会变成一个问题:

    +------+--------+--------+
    | COL1 | COL2   | COL3   |
    +------+--------+--------+
    | 1*   | {null} | {null} |
    | 2*   | B*     | {null} |
    | 3*   | {null} | C*     |
    | 4*   | D*     | D*     |
    +------+--------+--------+
    

    .. 因为在这种情况下,生成的 SQL 字符串将如下所示:

    INSERT INTO t (col1) VALUES (?);
    INSERT INTO t (col1, col2) VALUES (?, ?);
    INSERT INTO t (col1, col3) VALUES (?, ?);
    INSERT INTO t (col1, col2, col3) VALUES (?, ?, ?);
    

    这种默认行为的原因是这是保证...DEFAULT行为的唯一方法。就像在 SQL DEFAULT 中一样。 I gave a rationale of this behaviour here.

    考虑到这一点,并且由于每个连续的 SQL 字符串都不同,不幸的是插入并没有像您预期的那样作为单个批次进行批处理。

    解决方案 1:确保所有更改的标志都是 true

    强制所有INSERT 语句相同的一种方法是将每个个体记录的所有更改标志设置为true

    for (Record r : records)
        r.changed(true);
    

    现在,所有 SQL 字符串都将相同。

    解决方案 2:使用Loader API

    您可以导入数据(并在那里指定批量大小),而不是批处理。有关详细信息,请参阅手册中有关导入记录的部分:

    https://www.jooq.org/doc/latest/manual/sql-execution/importing/importing-records

    解决方案 3:改用批处理语句

    您对batchInsert() 的使用很方便,在使用TableRecords 时也很方便。当然,您可以手动生成INSERT 语句并使用 jOOQ 的批处理语句 API 批处理各个绑定变量:

    https://www.jooq.org/doc/latest/manual/sql-execution/batch-execution

    性能说明

    关于 DSLContext.batchInsert() 和类似 API 有几个未解决的问题。为每个单独的记录生成 SQL 字符串的客户端算法效率低下,将来可能会更改,直接依赖 changed() 标志。一些相关问题:

    【讨论】:

    • 解决方案 2 有效。 Jooq 正在创建批量插入,但它不会加快整个过程。无论如何谢谢;)
    • @hya:你尝试过处理批量、批处理和提交大小吗?
    • 仅适用于批量大小。要更改提交大小,我需要禁用自动提交。也许我会稍后再试。
    • @hya:哦,是的,您不应该提交每个单独的插入。通常 1000 行的提交大小可以帮助某些数据库。
    猜你喜欢
    • 2020-03-18
    • 2018-08-13
    • 1970-01-01
    • 2021-03-27
    • 2020-05-22
    • 2015-05-19
    • 1970-01-01
    • 2021-05-23
    • 2020-08-04
    相关资源
    最近更新 更多