Quarkus中用事务批量执行insertMany时MongoDB报WriteConflict错误
问题分析与解决方案
核心问题原因
- 超大事务引发锁竞争:你将100万条数据的插入操作放在单个事务中执行,MongoDB的事务机制并不适合承载这种大规模批量操作。长时间运行的事务会持续占用锁资源,随着数据量累积到80万条后,锁竞争加剧,WriteConflict错误的触发概率会急剧上升。
- 批量插入逻辑失效:代码中每次循环都新建
ArrayList并仅添加单个元素,i == sampleDataList.size()这个条件几乎不会生效(仅i=1时触发),实际相当于每次只插入1条数据,完全浪费了insertMany的批量优势,还让事务内的操作数暴增,进一步放大了冲突风险。
修正后的代码实现
int totalCount = 1000000; int batchSize = 100000; // 提前初始化对应容量的列表,避免频繁扩容 ArrayList<SampleData> sampleDataList = new ArrayList<>(batchSize); for (int i = 1; i <= totalCount; i++) { sampleDataList.add(new SampleData(i)); // 达到批量阈值或处理到最后一批时执行插入 if (i % batchSize == 0 || i == totalCount) { // 每批数据使用独立事务,避免单个事务过大 try (ClientSession clientSession = mongoClient.startSession()) { clientSession.startTransaction(); try { sampleDataRepo.mongoCollection().insertMany(clientSession, sampleDataList); clientSession.commitTransaction(); } catch (WriteConflictException e) { // 遇到冲突时重试当前批次 clientSession.abortTransaction(); // 重置循环索引,重新处理当前批次数据 i -= sampleDataList.size(); } catch (Exception e) { clientSession.abortTransaction(); throw new RuntimeException("数据插入失败: " + e.getMessage(), e); } } sampleDataList.clear(); } }
关键优化说明
- 拆分事务:每批数据使用独立事务提交,避免单个事务承载百万级操作,大幅降低锁竞争和WriteConflict的触发概率。
- 修复批量逻辑:预先初始化对应容量的列表,累积到指定批量大小再执行插入,充分发挥
insertMany的性能优势。 - 冲突重试处理:针对
WriteConflictException单独做重试逻辑,保证当前批次数据最终能插入成功。 - 资源自动管理:使用
try-with-resources自动关闭ClientSession,避免会话资源泄漏。
内容的提问来源于stack exchange,提问作者Zhen Wei
相关产品推荐
相关产品推荐

