不可见索引使优化器彻底忽略它,除非显式启用use_invisible_indexes;创建时加invisible,已有索引需alter table ... alter index ... invisible;主键等不支持;explain不显示,验证需查information_schema并对比执行计划。

不可见索引不是“隐藏界面”,而是让优化器彻底无视它——除非你显式开启开关或强制提示;它不参与 EXPLAIN,不进执行计划,但磁盘占用、写入开销、唯一性校验全在。
怎么创建或切换已有索引为 INVISIBLE
新建索引时直接加 INVISIBLE 最省事;已有索引必须用 ALTER TABLE ... ALTER INDEX ... INVISIBLE,不能写成 SET INVISIBLE 或 MODIFY INDEX,否则报错 ERROR 1064 (42000):
CREATE INDEX idx_status ON orders(status) INVISIBLE;ALTER TABLE orders ALTER INDEX idx_status INVISIBLE;- 主键(含隐式主键)、全文索引、空间索引不支持设为不可见,会直接报
ERROR 3522 (HY000) - 操作是元数据级变更,毫秒级完成,但需等待长事务释放元数据锁(MDL)
为什么 EXPLAIN 看不到 INVISIBLE 索引,以及怎么验证它真被忽略了
EXPLAIN 里压根不会列出不可见索引,这是设计行为,不是 bug。验证是否生效,不能只看命令返回 Query OK,得查两处:
- 查
INFORMATION_SCHEMA.STATISTICS:SELECT INDEX_NAME, IS_VISIBLE FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_NAME = 'orders' AND INDEX_NAME = 'idx_status';→ 返回NO才算成功 - 对比执行计划:
EXPLAIN SELECT * FROM orders WHERE status = 'shipped';应该不再走idx_status(key字段为空或换用其他索引) - 若写了
USE INDEX (idx_status),会直接报ERROR 1176 (42000): Key 'idx_status' doesn't exist,说明优化器已将其“注销”
怎么临时让不可见索引参与优化,做 A/B 对比
默认情况下,FORCE INDEX 和 USE INDEX 都无效;真正能绕过可见性过滤的,只有 optimizer_switch = 'use_invisible_indexes=on':
- 会话级启用(推荐):
SET SESSION optimizer_switch = 'use_invisible_indexes=on';,然后跑EXPLAIN - SQL 级启用(更安全):
SELECT /*+ SET_VAR(optimizer_switch = 'use_invisible_indexes=on') */ * FROM orders WHERE status = 'shipped'; - 注意:MySQL 默认关闭该开关,如果发现隐藏索引“意外生效”,大概率是有人全局或会话级打开了它
- 两次
EXPLAIN必须在相同会话下做,避免被其他optimizer_switch设置干扰
线上灰度测试时最容易漏掉的三个信号
单条 SQL 的 EXPLAIN 对比远远不够。真实影响藏在线上负载里:
- 盯
performance_schema.table_io_waits_summary_by_index_usage中对应索引的COUNT_STAR是否归零(但低频关键查询可能漏统计) - 观察慢查询日志里
Query_time是否突增,尤其是原来走该索引的语句 - 检查
Handler_read_*指标(如Handler_read_next、Handler_read_rnd_next)是否明显上升,反映扫描量变大 - 切记:别跳过 24 小时观察期——要覆盖完整业务波峰波谷;回滚只需一条
ALTER TABLE ... VISIBLE,但前提是没人误删了索引定义或改了备份脚本











