insert into on duplicate key update - 返回主键问题
问题 - InsertOrUpdate 会修改内存主键
在项目中,需要批量插入销售记录数据进入 record 表。这张表有自己的唯一键 sale_no 作为唯一标识号。我们需要同步 Mysql 表与 ES 索引,设计成同时双写 mysql 和 es。代码如下
双写逻辑
SQL
insert into record
(column1, column2, ...)
values
(value1, value2, ...)
...
on duplicate key update
column1 = values(values1)
...
private void saveRecords(List<RecordPO> RecordPOList) {
// 1. 这里使用 insert into on duplicate key update SQL 语句批量插入
markableRecordPOUdfMapper.batchInsertReplace(markableRecordPOList);
// 2. 批量更新 ES,转成 ES 类型 PO 再 Bulk
RecordSearch.bulkIndex(RecordIndexConverter.toIndexObjects(markableRecordPOByUniqueSerialNo));
// ......
}
可是出现一个奇怪的现象,每次更新数据表后,es 都会比 mysql 多几个数据。
测试验证
最后排查结果是:batchInsertReplace 方法返回的主键是有问题的,不是真实数据库存储的主键,而是 Mybatis 自己生成的。下面是实验和问题详解。
测试验证
自己造出实验数据,一遍insert一边update,我们预期的是 insert 的PO 赋值出插入数据库的自增主键,update 的 PO 赋值更新的主键。
结果出人意料,插入数据的PO获得主键 115066,UPDATE 的 PO 获得主键居然是 115067(数据库里真实的主键应该是113242,所以只能更新那条记录,却根本没获取到要更新记录的主键)。
所以这个 PO 回填的主键是假的,只是 Mybatis 自增主键 + 1
解决方案
强制走从库反查数据库再读取一遍主键
- 为什么要反查,因为批量插入的健只有在mysql中才能获取到
- 为什么强制走从库?因为刚插入新数据,会有主从延迟
markableRecordPOUdfMapper.batchInsertReplace(markableRecordPOList);
// 用唯一索引强制走从库读取
List<MarkableRecordPO> markableRecordPOByUniqueSerialNo = getRecordPOBySaleSerialNoFromMaster(uniqueSerialNoList);
RecordSearch.bulkIndex(RecordIndexConverter.