ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

TiDB 不可见索引(Invisible Index)设计与实现解析:基于 2020-03-12-invisible-index 设计文档的深度指南

2026/9/10 2:06:23 拓冰建站 浏览量
TiDB 不可见索引(Invisible Index)设计与实现解析:基于 2020-03-12-invisible-index 设计文档的深度指南 TiDB 不可见索引Invisible Index设计与实现解析基于 2020-03-12-invisible-index 设计文档的深度指南【免费下载链接】tidbTiDB is built for agentic workloads that grow unpredictably, with ACID guarantees and native support for transactions, analytics, and vector search. No data silos. No noisy neighbors. No infrastructure ceiling.项目地址: https://gitcode.com/GitHub_Trending/ti/tidbTiDB 的不可见索引Invisible Index允许你创建或调整一个对优化器隐藏但仍持续维护的索引从而在不执行破坏性 DROP 操作的前提下安全评估删除索引对查询性能的影响。本指南以仓库中的设计文档为主体骨架结合当前 TiDB 源码实现完整讲解不可见索引的语法、行为约束、底层执行链路从ALTER INDEX到 schema 变更 Job以及优化器侧的过滤逻辑帮助你掌握这一零风险索引下线演练能力的原理与正确用法。一、不可见索引要解决什么问题索引对数据库读写性能的影响举足轻重是否存在索引、优化器是否选对了索引很大程度上决定了数据库读写的性能表现。在某些场景下我们希望评估删除某个索引对数据库读写性能的影响。过去 TiDB 的做法是借助 Index Hint 让优化器忽略指定索引虽然能达到目的但要求逐条修改所有 SQL 语句在大规模业务中并不可行见设计文档的 Background 一节。不可见索引正是为此而生它为索引增加一个可见/不可见的选项不可见的索引不会被优化器使用但依然会在 DML 操作中持续维护。对查询而言不可见索引的效果等价于通过 Index Hint 忽略该索引却无需改动任何 SQL。更关键的是删除再重建一个大表的索引代价高昂而将索引在 VISIBLE 与 INVISIBLE 之间切换是快速的原位in-place操作——这让它成为以安全方式下线索引的理想工具。二、核心语法与行为契约设计文档给出了完整的语法支持当前 TiDB 已全部实现。2.1 建索引时指定可见性INVISIBLE/VISIBLE作为索引选项index_option的一部分可在创建索引时设置也可在建表时直接设置CREATE [...] INDEX index_name [index_type] ON tbl_name (key_part,...) [index_option] index_option: {VISIBLE | INVISIBLE}具体到当前仓库建索引路径位于 pkg/ddl/index.go其中对indexOption.Visibility ast.IndexVisibilityInvisible的分支处理见pkg/ddl/index.go#L440附近将不可见标记写入索引元数据。2.2 通过 ALTER INDEX 切换可见性通过如下 DDL 可以随时将索引切换为不可见或恢复可见ALTER TABLE table_name ALTER INDEX index_name { INVISIBLE | VISIBLE };这是不可见索引最有价值的操作——它让下线演练可以随时开始、随时回滚而无需重建索引。2.3 三个必须遵守的行为约束设计文档明确了三条配套行为均已落地元数据可查不可见信息必须体现在INFORMATION_SCHEMA.STATISTICS表和SHOW INDEX输出中新增IS_VISIBLE/VISIBLE列Hint 冲突报错当不可见索引被用于索引 Hint 时需要报错主键不可隐藏主键索引不能被设置为不可见。此外还有一条容易被忽略的隐含规则来自设计文档没有显式主键的表如果存在建于 NOT NULL 列上的 UNIQUE 索引则第一个这样的唯一索引在行约束上等价于隐式主键同样不能被设置为不可见。三、从设计到实现源码级解读设计文档的 Implementation 一节列出了六项实现任务下面逐项对照当前仓库源码验证其落地情况。3.1 元数据存储IS_VISIBLE / VISIBLE 列INFORMATION_SCHEMA.STATISTICS表新增了IS_VISIBLE列其列定义TypeVarchar长度 3位于 pkg/infoschema/tables.goSHOW INDEX输出同样提供可见性信息pkg/infoschema/tables.go。同时SHOW CREATE TABLE也会展示不可见索引信息以便完整还原表结构。3.2 ALTER INDEX 的完整执行链路ALTER TABLE ... ALTER INDEX ... INVISIBLE/VISIBLE的实际执行路径如下入口pkg/ddl/executor.go 的AlterIndexVisibility将ast.IndexVisibilityInvisible映射为invisibletrue先做前置校验再构造一个类型为model.ActionAlterIndexVisibility的 DDL Job 提交给 DDL 框架校验pkg/ddl/index.go 的validateAlterIndexVisibility检查索引是否存在且处于StatePublic状态若目标状态与当前状态一致则直接跳过幂等并禁止对列式索引Columnar Index设置 INVISIBLEJob 处理onAlterIndexVisibilitypkg/ddl/index.go与setIndexVisibilitypkg/ddl/index.go在 schema 变更过程中改写索引的可见性标记该 Job 类型在 pkg/ddl/job_worker.go 中被分发处理。从源码结构看这个操作走的是标准的在线 DDLonline DDL异步 Job 机制因此不会阻塞读写这与设计文档快速的原位操作的定位一致。3.3 主键与隐式主键保护当尝试把聚簇索引clustered index主键设置为不可见时TiDB 直接拒绝pkg/ddl/create_table.go在建表/建主键路径上若constr.Option.Visibility ast.IndexVisibilityInvisible且表具有聚簇索引则返回dbterror.ErrPKIndexCantBeInvisible该错误对应 MySQL 兼容错误码ERROR 3522 (HY000): A primary key index cannot be invisible错误文案定义在 pkg/errno/errname.go。ALTER INDEX路径则通过validateAlterIndexVisibility等逻辑配合元数据校验从两个入口共同保障主键/隐式主键索引不会被隐藏。3.4 优化器如何看不见不可见索引设计文档提出不可见索引不能供优化器使用除非开关打开但对查询而言其效果等价于 Index Hint 忽略。当前实现中优化器在选择访问路径时对不可见索引做了显式过滤pkg/planner/core/planbuilder.go读取会话变量OptimizerUseInvisibleIndexes遍历表索引时若!optimizerUseInvisibleIndexes index.Invisible则直接continueFilter out invisible index, because they are not visible for optimizerpkg/planner/core/point_get_plan.go 与#L617Point Get / Batch Point Get 计划同样要求索引可见且StatePublic不可见索引不会被用于点查加速。值得注意的是与 MySQL 不同TiDB 在 SQL Hint 中显式使用不可见索引时会抛出错误设计文档 Compatibility 一节描述为 Unresolved name 错误而不是静默允许。这一点在使用 Hint 时需要格外留意。四、系统开关tidb_opt_use_invisible_indexes设计文档提出在optimizer_switch中新增use_invisible_indexes开关文档中写作use_invite_indexes为笔误用于控制 INVISIBLE 选项是否生效。TiDB 实际实现为独立系统变量变量定义于 pkg/sessionctx/vardef/tidb_vars.goTiDBOptUseInvisibleIndexes tidb_opt_use_invisible_indexes注册于 pkg/sessionctx/variable/setvar_affect.go。开关默认关闭即优化器不使用不可见索引。开启后优化器会重新考虑这些索引-- 关闭默认不可见索引不被优化器使用 set session tidb_opt_use_invisible_indexesoff; -- 开启优化器可以继续使用不可见索引 set session tidb_opt_use_invisible_indexeson;这个开关给了运维一个额外的逃生舱即使索引已设置为 INVISIBLE仍可在会话或全局层面临时恢复其参与优化用于应急或对比验证。五、为什么不用 WriteOnly 状态实现设计取舍设计文档 Rationale 一节讨论了另一种备选方案用 DDL 状态机中的WriteOnly状态来表示不可见。该方案被否决原因有三需要改动 schema 变更的核心逻辑实现复杂度高无法区分正处于 WriteOnly 迁移中的索引与被标记为不可见的索引语义互相污染处理开关是否使用不可见索引非常麻烦。当前采用索引选项 优化器过滤的方案将可见性作为独立的元数据维度与 schema 状态机解耦既保持了 DDL 框架的简单性也让可见性切换成为可随时执行、可随时回滚的轻量操作。六、兼容性与迁移这是一个全新特性与旧版本 TiDB 完全兼容不影响任何数据迁移。语法与功能基本兼容 MySQL唯一明确的差异点是当在 SQL Hint 中使用不可见索引且use_invisible_indexes false时MySQL 允许使用该不可见索引而 TiDB不允许会抛出Unresolved name错误。也就是说在把 MySQL 业务迁移到 TiDB 时如果业务 Hint 中引用了被标记为不可见的索引需要提前清理或调整相关 Hint。七、测试与验证设计文档的 Testing Plan 包括单元测试与借鉴 MySQL 不可见索引相关测试用例。当前仓库中的测试可以帮你快速验证上述所有行为pkg/planner/core/casetest/index/index_test.goTestInvisibleIndex用CREATE TABLE t1 ( a INT, KEY( a ) INVISIBLE )建表插入 10 行后先验证EXPLAIN走TableFullScan索引不可见、不被使用再set session tidb_opt_use_invisible_indexeson验证执行计划切换为IndexFullScan——这一组用例直观演示了开关对执行计划的影响pkg/ddl/db_change_test.goTestAlterIndexVisibility覆盖ALTER INDEX ... VISIBLE/INVISIBLE的 DDL 行为tests/integrationtest/r/ddl/db_integration.result 与 tests/integrationtest/r/ddl/primary_key_handle.result记录了集成测试中主键不可见报错等场景的预期输出。八、实战建议零风险的索引下线流程结合设计文档的初衷与当前实现推荐如下不可见索引演练流程切换为不可见ALTER TABLE t ALTER INDEX idx_a INVISIBLE;——索引停止被优化器使用但 DML 仍持续维护索引数据保持新鲜观察与评估在业务低峰期观察读写性能、慢查询与执行计划变化此阶段随时可以回滚回滚或确认若索引仍然必要执行ALTER TABLE t ALTER INDEX idx_a VISIBLE;立即恢复若确认无用再执行DROP INDEX idx_a ON t;——此时才执行真正破坏性的操作且由于索引始终被维护回滚重建索引仍需成本因此先 INVISIBLE 观察是唯一的低风险路径。需要注意的边界条件主键索引含隐式主键等价约束的 NOT NULL 唯一索引不能被设置为不可见列式索引同样不支持 INVISIBLESQL Hint 中显式引用不可见索引会报错而非静默忽略。总结不可见索引是 TiDB 提供的一种可逆的索引下线能力以索引元数据上的一个可见性标记为核心结合优化器访问路径过滤、独立的tidb_opt_use_invisible_indexes系统开关以及一套与 MySQL 高度兼容的语法与行为约束实现了在不动 SQL、不重建索引的前提下安全评估索引价值的目标。其底层实现ActionAlterIndexVisibilityDDL Job 优化器过滤与设计文档中的方案完全对应相关源码与测试均可在本仓库的 pkg/ddl、pkg/planner/core 与 pkg/infoschema 中查阅。【免费下载链接】tidbTiDB is built for agentic workloads that grow unpredictably, with ACID guarantees and native support for transactions, analytics, and vector search. No data silos. No noisy neighbors. No infrastructure ceiling.项目地址: https://gitcode.com/GitHub_Trending/ti/tidb创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考