last_insert_id()返回错误

【 TiDB 使用环境】生产环境
【 TiDB 版本】v4.0.8
【遇到的问题】
问题背景
1.显示开启2个事务
2.insert语句使用on duplicate key update
3.事务1与事务2未提交的情况下写入uk相同的一行数据
4.事务1提交后事务2提交
5.预期:事务2的last_insert_id()位上一次写入的主键id值
【问题现象及影响】

事务1 事务2
start transaction;
start transaction;
select last_insert_id(); //27
select last_insert_id(); //28
insert into test1_byxx(name,uid,mtime) values (‘aa’,11,now()) on duplicate key update name=‘aa’,uid=11,mtime=now(); //Query OK, 1 row affected
select last_insert_id(); //31
insert into test1_byxx(name,uid,mtime) values (‘aa’,11,now()) on duplicate key update name=‘aa’,uid=11,mtime=now(); //阻塞
commit; //Query OK, 2 rows affected
select last_insert_id(); //32
commit;

解读:
1.事务1上次写入的主键id为27,事务2上次写入的主键id为28
2.uid为唯一索引,事务1和事务2写入uid相同的一行数据
3.事务1先提交,事务2后提交,事务1的last_insert_id()为31(本次写入的数据行主键id值)符合预期,事务2不符合预期,返回的last_insert_id()为32,应当返回28

影响:
业务依赖last_insert_id()往关联表写入数据,事务2返回的id主表中并不存在,导致关联表中写入了一条脏数据

测试表结构如下:
CREATE TABLE test1_byxx (
id bigint(20) NOT NULL AUTO_INCREMENT,
name varchar(20) NOT NULL DEFAULT ‘’,
uid int(11) NOT NULL DEFAULT 0,
ctime datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT ‘创建时间’,
mtime datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT ‘更新时间’,
PRIMARY KEY (id),
UNIQUE KEY uk_uid (uid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT=1
【附件】

请提供各个组件的 version 信息,如 cdc/tikv,可通过执行 cdc version/tikv-server --version 获取。

这应该是正常的吧,事务2备阻塞,事务1执行完后自增列到31了,等事务2执行是只能从32开始

事务2是更新,并不是插入,last_insert_id()值应该不变的
同样的执行流程,在mysql中就是符合预期的,事务2的last_insert_id()在insert on duplicate update前一致
大佬说的事务2执行只能从32开始,这个是没问题的,但是这个值是自增值,并不是last_insert_id()

1 个赞

明白你的意思了,可以看下这个insert on duplicate key update does not return Last Insert ID in the case of an update · Issue #23914 · pingcap/tidb · GitHub
executor: set last insert id in the duplicated update statement by jackysp · Pull Request #6345 · pingcap/tidb · GitHub
insert on duplicate key update 在更新的情况下没有返回 Last Insert ID

属于一个bug

last_insert_id() 是从一个会话变量里获得的,当插入数据时会更新这个变量。5.2.3上测试了下,tidb能保证session级的on duplicate kye update时 last_insert_id保持不变。第一次执行此SQL时会获取一个最新的inc_id,后续重复执行时虽然不更新系统级变量,保持last_insert_id一致,但实际还是推进自增列的值。
这块确实和mysql不太一致

2 个赞

此话题已在最后回复的 1 分钟后被自动关闭。不再允许新回复。