TiDB v7.5.1 疑似存在 BIT 类型在覆盖索引下的查询一致性问题

一个好的问题描述有利于社区小伙伴更快帮你定位到问题,高效解决你的问题

【TiDB 使用环境】生产环境
【TiDB 版本】:7.5.1
【部署方式】裸金属机器部署 (x86_64)
【操作系统/CPU 架构/芯片详情】Rocky Linux release 9.1 (Blue Onyx)
【机器部署详情】 *CPU: Intel(R) Xeon(R) Gold 5317 CPU @ 3.00GHz (单路 12核24线程)
内存:250GB
磁盘: NVMe SSD
【集群数据量】50TB
【集群节点数】14
【问题复现路径】做过哪些操作出现的问题
【遇到的问题:问题现象及影响】
在处理 user_pipeline_wait_entity 表时发现严重的索引数据不一致现象。表中只有约 3 条数据,其中包含 bit(1) 类型的字段 v3。
具体现象描述:

  1. 执行 SELECT * FROM user_pipeline_wait_entity WHERE v3 = 0; 能正常返回 1 条数据。

  2. 执行 SELECT original_user_id FROM user_pipeline_wait_entity WHERE v3 = 0; 返回结果为空(Empty set)。

  3. 已知索引结构为 idx_v3_original_user (v3, original_user_id),此时 TiDB 选择了 IndexOnlyScan

  4. 使用 Hint /*+ IGNORE_INDEX(user_pipeline_wait_entity, idx_v3_original_user) */ 强制走全表扫描后,结果恢复正常(1条数据)。

  5. 通过 DROP INDEX 重新 ADD INDEX 后,问题消失。

【问题复现路径】

  1. 创建包含 bit(1) 字段及相关复合索引的表。

  2. 插入少量测试数据(包含 v3=0 的记录)。

  3. 通过覆盖索引字段进行查询(不回表)。
    CREATE TABLE user_pipeline_wait_entity (
    id bigint(20) NOT NULL,
    create_time datetime DEFAULT NULL,
    job_status int(11) NOT NULL,
    original_user_id int(11) NOT NULL,
    s3path varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
    source_id varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
    status int(11) NOT NULL,
    suffix varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
    update_time datetime DEFAULT NULL,
    version int(11) NOT NULL,
    book_upload bit(1) DEFAULT b’0’,
    v3 bit(1) DEFAULT b’0’,
    progress varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
    async_finish bit(1) DEFAULT NULL,
    PRIMARY KEY (id) /*T![clustered_index] CLUSTERED */,
    KEY ms_user_task (original_user_id),
    KEY ms_task3 (original_user_id,status,job_status,create_time),
    KEY ms_task_1 (job_status,update_time),
    KEY ms_task_user (original_user_id,v3),
    KEY ms_task_2 (create_time,original_user_id,v3),
    KEY idx_v3_original_user (v3,original_user_id)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

  • 异常查询: EXPLAIN SELECT original_user_id FROM user_pipeline_wait_entity WHERE v3=0;

    • 使用了 IndexReader 访问 idx_v3_original_user 索引,未回表,结果为空。
  • 正常查询: EXPLAIN SELECT * FROM user_pipeline_wait_entity WHERE v3=0;

    • 使用了 TableReader 进行全表扫描,结果正确。

【资源配置】进入到 TiDB Dashboard -集群信息 (Cluster Info) -主机(Hosts) 截图此页面

【复制黏贴 ERROR 报错的日志】
【其他附件:截图/日志/监控】

BIT 类型字段在索引存储与回表读取时的行为差异 ,或者是 索引数据本身发生了损坏/不一致 。 推荐替换为 TINYINT

– 检查特定表的索引一致性
ADMIN CHECK TABLE user_pipeline_wait_entity;

索引get值不一致了吗

BIT(1) 类型的字段修改为 TINYINT(1) 。试一下

  1. BIT类型编码问题
    TiDB的BIT类型在存储和索引编码时存在特殊处理。当使用覆盖索引时,优化器直接从索引读取数据而不回表,此时BIT类型的解码可能出现错误​。
  2. 索引构建时的数据不一致
    根据TiDB 7.5.1 Release Notes,虽然修复了"BIT type columns might cause query errors due to decode failures"(#49566, #50850, #50855)​,但你的案例表明在复合索引+覆盖扫描场景下仍有问题。
1 个赞

可能的触发条件
• 表使用聚簇索引(Clustered Index) - 你的表有/*T![clustered_index] CLUSTERED */​
• BIT字段作为复合索引的前缀列(你的idx_v3_original_user是(v3, original_user_id))
• 查询使用IndexOnlyScan(不回表)

1 个赞

强制回表(临时规避)
对于涉及BIT类型字段的查询,强制使用TableReader而非IndexOnlyScan:

1 个赞

重建索引-- 检查所有包含BIT类型的索引
SELECT
TABLE_NAME,
INDEX_NAME,
COLUMN_NAME,
DATA_TYPE
FROM information_schema.STATISTICS s
JOIN information_schema.COLUMNS c
ON s.TABLE_SCHEMA = c.TABLE_SCHEMA
AND s.TABLE_NAME = c.TABLE_NAME
AND s.COLUMN_NAME = c.COLUMN_NAME
WHERE s.TABLE_SCHEMA = ‘your_db’
AND c.DATA_TYPE = ‘bit’;

– 重建这些索引
ALTER TABLE user_pipeline_wait_entity DROP INDEX idx_v3_original_user;
ALTER TABLE user_pipeline_wait_entity ADD INDEX idx_v3_original_user (v3, original_user_id);

2 个赞

嗯,要 先删后加

重建能否解决呢

重建索引 就能解决这个问题

感觉还是要少用 bit 这个类型

是索引数据有问题了吗

使用int这种场景可以替代吗

:upside_down_face: 貌似tiflash也不支持bit,最后让开发改用TINYINT(1)替代