# [Critical bug] TiDB 使用 Multi-schema change 添加唯一索引导致数据索引不一致

## Key Words

multi-schema,unique index,inconsistent data index,INSERT IGNORE

## Issue

使用 multi-schema change 添加唯一索引后，唯一索引的状态未能正确设置，导致后续执行 INSERT IGNORE 语句时会不正确地插入了重复的行，破坏了索引的唯一性约束。例如：

```sql
-- 创建测试表 T
create table t (a int, b int);
-- 插入测试数据
insert into t values (1, 1);
insert into t values (2, 2);
-- 通过 multi-schema change 新增 unique index
alter table t add unique index idx(b), ...[any other schema changes];
-- 通过 ‘insert ignore' 成功插入重复值
insert ignore into t values (2, 2);
-- 进行数据一致性检查报错；
admin check table t;

ERROR 8223 (HY000): data inconsistency in table: t, index: idx, handle: 2, index-values:"handle: 3, values: [KindInt64 2]" != record-values:"handle: 2, values: [KindInt64 2]"
```

目前仅在 **`INSERT IGNORE`** 语句上发现此类违反唯一性约束的 bug。使用 `INSERT`、`UPDATE` 和 `REPLACE` 语句来插入唯一索引重复值，都会按照预期报 "duplicate entry" 的错误。

## Root Cause

GitHub issue: <https://github.com/pingcap/tidb/issues/40217>

TiDB 从 v6.3.0 起，支持使用 ingest 模式 [@@tidb_ddl_enable_fast_reorg](https://docs.pingcap.com/zh/tidb/stable/system-variables#tidb_ddl_enable_fast_reorg-%E4%BB%8E-v630-%E7%89%88%E6%9C%AC%E5%BC%80%E5%A7%8B%E5%BC%95%E5%85%A5) 添加索引，默认值为 `ON` 已开启。在增量数据合并回原索引的步骤完成后，multi-schema change 对目标索引的状态的更改未持久化到 TiKV，而执行下一个 schema 变更时丢弃了这个更改。在整个 multi-schema change 的 DDL 完成后，目标索引始终处于不正确的状态。

假如应用后续执行 `INSERT IGNORE` 语句，TiDB 在判断索引值是否重复时，该索引的状态影响了判断逻辑，导致检查被忽略，插入了重复的值（正常情况是不插入重复值并报 `warning`）。

## Diagnostic Steps

出现数据索引不一致后，如果同时符合以下几种条件就可以确认是同一问题：

- TiDB 版本为 6.3.0、6.4.0 或 6.5.0，默认已经开启 `tidb_ddl_enable_fast_reorge`。
- 检查该表的 DDL 历史记录，使用过 multi-schema change 添加唯一索引。
- 数据索引不一致涉及到的索引和 multi-schema change 添加的索引是同一个索引。
- 搜索 TiDB 添加唯一索引时间段的日志，包含 `"[ddl-ingest]"` 关键字。

## Resolution

预计修复版本 `v6.5.1 LTS` 

## Workaround

建议不要使用 multi-schema change 添加唯一索引。如果已经添加，最好重建索引以避免日后出现数据索引不一致。

## Affect Version

v6.3

v6.4

v6.5