开启事务,使用唯一索引前两列查询不到刚插入的数据

tidb v4.0.2版本

开启事务,使用唯一索引前两列查询不到刚插入的数据
没有唯一键uk_tId_aa_bb时,查询也是没问题的

CREATE TABLE `test1` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `t_id` bigint(20) NOT NULL COMMENT 'tid',
  `aa` varchar(20) NOT NULL COMMENT 'aa',
  `bb` varchar(20) NOT NULL DEFAULT '' COMMENT 'bb',
  `cc` varchar(128) NOT NULL DEFAULT '' COMMENT 'cc',
  PRIMARY KEY (`id`),
  KEY `idx_aa_bb_cc_id` (`aa`,`bb`,`cc`,`id`),
  UNIQUE KEY `uk_tId_aa_bb` (`t_id`,`aa`,`bb`),
  KEY `idx_tenantid` (`t_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT=1020548 COMMENT='test';

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into test1(t_id, aa, bb, cc) values(10086,'test1', 'test', '0-59/5 * * * *');
Query OK, 1 row affected (0.02 sec)

mysql> select * from test1 where t_id = 10086 and aa ='test1';
Empty set (0.00 sec)

mysql> select * from test1 where t_id = 10086;
+--------+-------+-------+------+----------------+
| id     | t_id  | aa    | bb   | cc             |
+--------+-------+-------+------+----------------+
| 990548 | 10086 | test1 | test | 0-59/5 * * * * |
+--------+-------+-------+------+----------------+
1 row in set (0.01 sec)

mysql> select * from test1 where t_id = 10086 and aa ='test1';
Empty set (0.00 sec)

mysql> select * from test1 where t_id = 10086 and aa ='test1' and bb= 'test';
+--------+-------+-------+------+----------------+
| id     | t_id  | aa    | bb   | cc             |
+--------+-------+-------+------+----------------+
| 990548 | 10086 | test1 | test | 0-59/5 * * * * |
+--------+-------+-------+------+----------------+
1 row in set (0.00 sec)

你的操作流程:

  1. begin; :事务开始,此时建立了一个快照。因为是刚开启的事务,这个快照里还没有 你即将插入的数据。
  2. insert ... :插入数据。虽然数据写入了,但在“可重复读”级别下,当前事务自己 暂时还不能通过普通的快照读(Snapshot Read)看到这条刚插入的“新版本”数据(除非使用特定的读方式)。
  3. select ... (使用二级索引):当你使用非主键索引(如 uk_tId_aa_bb )查询时,TiDB 需要先通过二级索引找到主键,再通过主键去主表(Clustered Index 或 Row ID)回表查询完整数据。这个“回表”的过程触发了快照读,读取的是事务开始时的旧快照,因此读不到你刚插入的数据,结果为空。
  4. select ... (使用主键/聚簇索引):当你使用 t_id 查询或者全表扫描时,TiDB 可能使用了主键索引或者执行了不同的读取逻辑(在某些版本中,主键查询可能会绕过普通的快照读限制,或者优化器选择了不同的执行路径),导致你看到了数据

tidb 7.1是没问题的

嗯,应该升级一下了, tidb 4 发布于 2020 年, 新版本做了很多优化

而且没有uk_tId_aa_bb时,查询也是没问题的

没有uk_tId_aa_b, 主键就是默认的

目前是可重复读隔离级别,按理是可以读取到本事务做的修改,看不到其他事务做的修改

事务没有提交吧

可重复读隔离级别 (Repeatable Read)

当事务隔离级别为可重复读时,只能读到该事务启动时已经提交的其他事务修改的数据,未提交的数据或在事务启动后其他事务提交的数据是不可见的。对于本事务而言,事务语句可以看到之前的语句做出的修改。

官网文档的描述

  • 修复当开启 new collation 时,若在事务内的更新涉及了 new collation 列,并在该事务内通过唯一索引读取更新数据时,被更新的数据无法被读取到的问题 #18703

应该就是这个问题了

  • 事务内索引更新延迟 在事务中插入数据后,TiDB 会先将数据写入内存,但唯一索引 uk_tid_aa_bb 的更新存在延迟。你用 t_idaa 这两列(索引的前两列)查询时,索引还未同步,因此无法找到刚插入的数据。

  • 主键 / 普通索引与唯一索引的可见性差异

  • 主键 id 和普通索引 idx_tenantid 是实时可见的,所以仅用 t_id 查询能返回结果。

  • 唯一索引 uk_tid_aa_bb 涉及多列约束,在事务提交前不会完全生效,导致查询不到。

  • 事务隔离级别的影响 TiDB 默认的 RC(读已提交) 隔离级别下,事务内的索引可见性并非完全实时,尤其是唯一索引这类带约束的索引,需要等待事务提交后才能完全同步。

有道理

认证都6的

事务内强制走表读(不太优雅)
SELECT /*+ IGNORE_INDEX(t uk_tId_aa_bb) */ …

或:

SET tidb_enable_index_merge = OFF;