Tidb insert on duplicate key 或 replace into 报主键冲突异常

【 TiDB 使用环境】生产环境
【 TiDB 版本】
7.1.1
【复现路径】
程序多线程写入的时候报Duplicate entry ‘81432804’ for key ‘change_records.PRIMARY’, 但程序控制的是几秒内相同主键只会更新一次
【遇到的问题:问题现象及影响】
报主键冲突的这个表没有设置主键,是唯一键。把sql复制出来手工执行可以正常插入执行。

表结构:
CREATE TABLE change_records( idbigint(20) NOT NULL COMMENT '序号', u_idchar(32) NOT NULL DEFAULT '' COMMENT '唯一键', eidchar(36) NOT NULL COMMENT 'eid', seq_noint(11) NOT NULL COMMENT '序号', after_contentmediumtext DEFAULT NULL COMMENT '变更后内容', before_contentmediumtext DEFAULT NULL COMMENT '变更前内容', change_datevarchar(255) NOT NULL DEFAULT '' COMMENT '变更日期', change_itemvarchar(2000) NOT NULL DEFAULT '' COMMENT '变更名称', update_timedatetime(6) DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6) COMMENT '更新时间', UNIQUE KEYeid_u_id (eid,u_id), KEY key_row_update_time (row_update_time), KEY eid_u_tags (eid,u_tags), KEY key_type (type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin /*T! SHARD_ROW_ID_BITS=8 */ ;

插入语句:
INSERT

INTO

change_records (seq_no,

special_type,

eid,

update_time,

change_date,

source,

type,

u_id,

id,

before_content,

after_content,

change_item)

VALUES (-1,

0,

‘0c6c80b6-dce3-422b-9c1c’,

‘2024-04-16 01:54:51’,

‘2015-05-15’,

‘1’,

‘人员变更’,

‘d1d75004c7261a85c09c66375082d990’,

7085492,

‘张三’,

‘李四’,

‘人员’) ON

DUPLICATE KEY

UPDATE

seq_no =

VALUES(seq_no),

special_type =

VALUES(special_type),

eid =

VALUES(eid),

update_time =

VALUES(update_time),

change_date =

VALUES(change_date),

source =

VALUES(source),

type =

VALUES(type),

u_id =

VALUES(u_id),

id =

VALUES(id),

before_content =

VALUES(before_content),

after_content =

VALUES(after_content),

change_item =

VALUES(change_item);

之前用的replace into 也是报同样报主键冲突

此表是有做过 br 或 lightning 恢复操作吗,执行下 show table change_records next_row_id; 看下结果呢

只有这一条记录冲突?还是还有很多其他的?

UNIQUE 的值是怎么获取到的,随机产生还是?

得到的结果

uuid + 按规则生成的数值

很多冲突,查了库里是没这条数据的

是的,以前好像做过lightning恢复操作的

而且一些别的表也是如此,都是没有主键的

我们初期的全量数据是通过 lightning导入的,

非聚簇表的主键是按 _tidb_rowid
可以按照这条 sql 查下哪条数据冲突:

select _tidb_rowid , change_records.* from change_records where _tidb_rowid= 81432804;

可能是由于 lightning 导入异常导致 rowid 被异常占用了

这种有没解决办法呢,现在问题是大多数表都这样的操作, 也出现了好几个表一样的主键冲突镶

你先按上面的这个sql查下这个 rowid 是否已经被占用了

最简单的解决办法是先滚动重启一下 tidb-server ,让缓存的 _tidb_rowid 往前刷一下,大概率能解决问题

如果还是解决不了,那就只能每个表手动设置一下了。

怀疑之前恢复的有些问题,不知道啥问题,目前看就是隐藏的 _tidb_rowid 冲突了,现在解决方法就是就是 rebase 一下,但是 rebase 成多少你得算一下

是的,刚另外条语句,我查了下,和_tidb_rowid冲突的数据是不一致的, 但奇怪的是报错的这条语句手动执行又可以成功的,是不是可以这样理解,tidb压力大了, _tidb_rowid 没及时往前推?手工执行的时候已经往后移了

不是那个原因,每个 tidb server 有自己缓存的一段,而且这个 id 用了一次就会往下走,所以你再插可能不冲突

原理是这样的: 你程序执行的时候已经尝试过这个冲突的 _tidb_rowid 了,所以下一次执行就会采用下一个 _tidb_rowid

举个例子来说:
假如 _tidb_rowid 为10 的 这条数据已经存在, 那么你程序第一次执行的时候会尝试插入 _tidb_rowid 10,然后失败了,这个时候你再手动执行,就会尝试插入_tidb_rowid 11 的数据,然后就成功了。

你也可以通过插入成功的那条数据倒查一下 _tidb_rowid 验证一下,是不是和前面冲突的 _tidb_rowid 不一致

1 个赞

没理解你要问什么。。。 就是 _tidb_rowid 重复了呀

除了重启kv, 手工更新缓存可以用什么命令

alter table xxx AUTO_INCREMENT xxx;

不太推荐这么搞,这个是要每个表去设置的,而且你要基于每个表当前的 _tidb_rowid 最大值去设一个更大的值。

另外, 是重启 tidb-server ,不是重启kv

1 个赞