Jooq 使用 MySql 中的记录批量更新

Posted

技术标签:

【中文标题】Jooq 使用 MySql 中的记录批量更新【英文标题】:Jooq batch update with records in MySql 【发布时间】:2018-09-14 12:51:25 【问题描述】:

使用 JOOQ 进行批量更新的正确方法是什么?

我有以下几点:

public void updateScores(Map<String, Integer> teamScores) 
    writeContext().transaction(config -> 
        DSLContext dslContext = DSL.using(config);
        dslContext.batchUpdate(Maps.transformEntries(teamScores, (id, score) -> 
            TeamScoresRecord record = new TeamScoresRecord();
            record.setTeamId(id);
            record.setScore(score);
            return record;
        ).values()).execute();
    );

public void updateScores(Map<String, Integer> teamScores) 
    writeContext().transaction(config -> 
        DSLContext dslContext = DSL.using(config);
        dslContext.batchUpdate(
                dslContext.selectFrom(TEAM_SCORES)
                          .where(TEAM_SCORES.TEAM_ID.in(teamScores.keySet()))
                          .forUpdate()
                          .fetch()
                          .stream()
                          .peek(record -> record.setScore(teamScores.get(record.getTeamId())))
                          .collect(Collectors.toList())
        ).execute();
    );

建议使用以下哪种方式进行批量更新?

【问题讨论】:

Current;y 我发现第一个有效,第二个无效。我的意思是第一个实际上更新了行,但第二个并没有,因为在此之后进行选择会产生与更新前相同的行 好奇:为什么你在第二个例子中使用peek(),而不是map()?另外,你的地图有多大? @LukasEder TableRecord::set 方法返回 void,并就地修改记录,因此由于 void 使用 map 会出错,但使用 peek 可以工作,因为该项目已就地修改。地图通常是 10k-15k 元素左右 好的,我明白了。还有Record::with,以防万一,或者你可以在代码生成器中生成流畅的setter。我稍后会研究这个问题 恐怕无法用第二种方法重现问题。 【参考方案1】:

这个问题显然很主观,不容易回答。以下是一些观察结果:

即使在 jOOQ 中使用批处理 API,您也会运行大量单独的查询,这会产生相当多的客户端/服务器开销。在您的特定情况下这可能很好,但考虑将整个逻辑移入数据库通常不是一个坏主意,例如通过将地图插入到临时表中,然后使用批量 UPDATE 语句合并两个数据集 如果两个冲突的批次以不同的顺序更新相同的记录,则两个批次都可能导致数据库死锁。我不确定这是否是您的代码中的问题,但您可能希望确保这永远不会发生。 第一种方法将运行一个较少的查询(SELECT 查询,这可能非常昂贵,具体取决于 in-list 的大小)。但是,如果没有 FOR UPDATE 子句,第一种方法可能具有更高的死锁风险。 第一种方法可能会运行比必要更多的更新语句,例如用于同时删除的ID 值。第二种方法可以防止这种情况发生

现在,我不太了解 mysql,无法知道批量更新语句在这里是否会更好,即这样的语句:

UPDATE team_scores
SET score = CASE id
  WHEN :id1 THEN :score1
  WHEN :id2 THEN :score2
  WHEN :id3 THEN :score3
  ...
END
WHERE id IN (:id1, :id2, :id3, ...)

也许您可以对这种方法进行基准测试并将其与批处理进行比较(或将其与批处理相结合,例如批量更新 10 行并批量处理所有这些批量更新)

【讨论】:

我用 H2 数据库测试这个,因为这是我们用来测试的,所以我不确定它是否也适用于 mysql。你知道在任意数据库上测试 jooq 的方法吗? 数据库使用InnoDB引擎实现行级锁定,所以第二个不应该导致整个表被锁定。我再次尝试了第二个版本,它可以工作。我以前可能使用过错误的表。不管怎样,从你说的看,我会恢复到旧的,因为它似乎最不可能拉肚子 @smac89:请提出一个关于测试数据库的新问题。

以上是关于Jooq 使用 MySql 中的记录批量更新的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 JOOQ 批量执行

MySQL群集中的批量插入和更新

mysql进阶 十四 批量更新与批量更新多条记录的不同值实现方法

mysql 批量更新与批量更新多条记录的不同值实现方法

mysql 批量更新与批量更新多条记录的不同值实现方法

在 PostgreSql 中批量更新或删除哪个更高效?