不可见索引不能替代删除索引的决策,它只安全验证“删了是否出事”:创建后不改执行计划、不降写入开销,但立刻停用查询路径;CREATE INDEX时加INVISIBLE最稳妥,秒级完成且避开元数据锁风险。

不可见索引不能替代删除索引的决策,它只帮你安全验证“删了这个索引,线上会不会出事”——创建后不改执行计划、不降写入开销,但能立刻停用查询路径。
CREATE INDEX 时直接加 INVISIBLE 关键字
新建即隐藏最稳妥,避免 ALTER 带来的元数据锁风险,也省去状态切换验证步骤:
-
CREATE INDEX idx_order_status ON orders(status) INVISIBLE;—— 秒级完成,仅修改元数据 -
CREATE UNIQUE INDEX idx_email ON users(email) INVISIBLE;—— 唯一索引支持,但若该列是隐式主键(如表无显式PRIMARY KEY且 email 是首个UNIQUE NOT NULL字段),会报ERROR 3522 (HY000) - 不支持
FULLTEXT、SPATIAL索引,仅限 InnoDB 的 B-tree 类型
ALTER TABLE ALTER INDEX INVISIBLE 切换已有索引
已有索引必须用标准语法切换,任何变体都会失败,且需提前排除主键/外键依赖:
-
ALTER TABLE orders ALTER INDEX idx_created_at INVISIBLE;—— 正确,毫秒级 -
ALTER TABLE orders ALTER INDEX idx_created_at VISIBLE;—— 恢复也一样快,无需重建 - 执行前查
INFORMATION_SCHEMA.KEY_COLUMN_USAGE:若CONSTRAINT_TYPE = 'PRIMARY KEY',直接失败 - 长事务未提交时,DDL 会被阻塞在元数据锁(MDL)等待阶段
验证 INVISIBLE 是否真被优化器忽略
别信 SHOW INDEX 的 Visible: NO 就算完事,得双重确认:
- 查系统表:
SELECT IS_VISIBLE FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_NAME = 'orders' AND INDEX_NAME = 'idx_created_at';返回'NO'(字符串)才算成功 - 跑真实查询:
EXPLAIN SELECT * FROM orders WHERE created_at > '2026-01-01';—— 若之前走该索引,现在key为空或换成别的索引,才说明生效 - 警惕
optimizer_switch干扰:SELECT @@optimizer_switch LIKE '%use_invisible_indexes=on%';如果返回1,说明会话或全局开了开关,得先关掉:SET SESSION optimizer_switch = 'use_invisible_indexes=off'; -
FORCE INDEX会绕过可见性检查,如果代码里硬写了它,测试就失真了——灰度前必须全量扫描应用层 SQL 和 ORM 配置
下线前观察什么指标才靠谱
不能只看一条 EXPLAIN,得在真实负载下盯三类信号是否同步出现:
- 慢查询日志里,原本走该索引的语句
Query_time明显升高,或Rows_examined暴涨 -
performance_schema.events_statements_summary_by_digest中该 SQL 的平均执行时间、扫描行数是否突增 -
Handler_read_next或Handler_read_rnd_next指标陡增 —— 说明退化为全表扫描或文件排序 - 最关键一点:写入压力不变,但
innodb_rows_inserted没下降、innodb_buffer_pool_reads也没减少 —— 因为隐藏索引照常维护 B+ 树,磁盘和 CPU 开销一分没少
最容易被忽略的是:隐藏索引不是“暂停服务”,而是“前台停业、后台照常开工”。压测中如果发现写入性能劣化,别只盯着查询计划看——B+ 树还在实时更新,唯一性校验照常执行。











