不可见索引不优化查询,仅用于安全验证删索引影响:先停用(alter index invisible)、再观察(查慢日志、handler指标、p99响应)、最后决策;需确认is_visible='no'、禁用use_invisible_indexes、排除force index干扰,且写入仍维护。

不可见索引本身不优化查询,它只帮你安全验证“删掉这个索引会不会出事”——验证必须分三步走:先停用、再观察、最后决策,缺一不可。
ALTER TABLE ALTER INDEX INVISIBLE 为什么不能直接信
这条语句确实毫秒完成,但它只是把 information_schema.STATISTICS.IS_VISIBLE 改成 'NO',不保证优化器立刻“失明”。常见干扰包括:
- 当前会话开启了
use_invisible_indexes=on,查SELECT @@optimizer_switch能看到; - 刚执行完就跑
EXPLAIN,元数据同步有毫秒级延迟,建议等 1–2 秒再验证; - 索引名拼错,或目标索引其实在另一张表上,
SHOW INDEX FROM t的Visible列才是唯一可信信号。
EXPLAIN 不走隐藏索引,但也不能只靠它判断
EXPLAIN 默认彻底忽略 INVISIBLE 索引,这是对的。但仅看它是否“没走该索引”,容易误判:
- 如果原来就没走这个索引(比如字段类型隐式转换、函数包裹),隐藏前后
EXPLAIN结果一样,不代表它没被依赖; - 真正要盯的是慢查询日志里有没有相关 SQL 突然变慢,以及
Handler_read_next、Handler_read_rnd_next是否明显上涨; - 应用层 P99 响应时间跳变、超时告警增多,比数据库指标更早暴露问题。
FORCE INDEX 或 USE INDEX 对不可见索引完全无效
哪怕你写 SELECT * FROM t FORCE INDEX (idx_name) WHERE ...,只要 idx_name 是 INVISIBLE,MySQL 在解析阶段就报错:ERROR 1176 (42000): Key 'idx_name' doesn't exist in table 't'。这不是语法错误,是设计行为。
- 想临时强制用它做 A/B 对比?只能打开会话级开关:
SET SESSION optimizer_switch = 'use_invisible_indexes=on';,然后跑EXPLAIN; - 8.0.23+ 版本已移除该开关,压测必须用
/*+ SET_VAR(optimizer_switch = 'use_invisible_indexes=on') */提示; - ORM 或中间件里硬编码的
FORCE INDEX必须提前清理,否则切为不可见后上线即故障。
长期挂着 INVISIBLE 索引反而埋雷
很多人以为“看不见=不干活”,其实相反:
- 每次
INSERT/UPDATE/DELETE都照常更新 B+ 树、写 redo、占 buffer pool; - 唯一约束类的
INVISIBLE索引仍会校验重复键,ERROR 1062照样报; - 备份恢复时,
xtrabackup不保证保留可见性,mysqldump --no-create-info手动建表会丢失INVISIBLE属性,从库或新实例上可能突然“复活”成可见索引。
验证周期建议至少覆盖一个完整业务周期(比如 24 小时),尤其要包含低频但关键的场景——月底报表、风控拦截、定时补数任务,这些最容易漏监控、最怕出事。











