隐藏索引是mysql 8.0原生支持的元数据标记功能,无需额外配置;真正关键的是控制optimizer_switch中的use_invisible_indexes开关,并结合真实负载验证效果,否则压测结果无效。

直接说结论:隐藏索引本身不需要“配置”,它是 MySQL 8.0 原生支持的元数据标记功能;真正要做的,是控制 optimizer_switch 中的 use_invisible_indexes 开关,并配合真实负载验证——否则压测结果毫无意义。
怎么创建或改现有索引为隐藏?
操作本身极快(毫秒级),但必须避开主键和隐式主键:
-
CREATE INDEX idx_status ON orders(status) INVISIBLE;—— 新建即隐藏,最稳妥 -
ALTER TABLE orders ALTER INDEX idx_user_id INVISIBLE;—— 修改已有索引,注意检查是否为主键或首个UNIQUE NOT NULL索引,否则报错ERROR 3522 (HY000) - 执行后别只看
SHOW INDEX FROM orders\G输出里的Visible: NO,务必查INFORMATION_SCHEMA.STATISTICS表确认IS_VISIBLE = 'NO'
为什么压测时 EXPLAIN 还是走了隐藏索引?
因为默认情况下优化器根本无视隐藏索引,但如果你或 DBA 曾执行过 SET GLOBAL optimizer_switch = 'use_invisible_indexes=on';,它就全局生效了——这会让所有会话都“看见”隐藏索引,导致压测误判。
- 查当前状态:
SELECT @@optimizer_switch LIKE '%use_invisible_indexes=on%'; - 压测前强制关闭(会话级):
SET SESSION optimizer_switch = 'use_invisible_indexes=off'; - 若需单条 SQL 临时启用对比(比如跑两轮 EXPLAIN),用提示:
SELECT /*+ SET_VAR(optimizer_switch = "use_invisible_indexes=on") */ * FROM orders WHERE status = 'paid';(仅 MySQL 8.0.22+ 支持)
压测中如何判断隐藏索引是否真被跳过?
不能只靠一条 EXPLAIN,得看三类信号是否同步出现:
- 慢查询日志里,原本走该索引的语句
Query_time明显升高,或出现新慢 SQL -
performance_schema.table_io_waits_summary_by_index_usage中对应索引的COUNT_STAR归零(但注意:这个表本身采样不全,仅作参考) - 写入压力不变的情况下,
Handler_read_next或Handler_read_rnd_next指标陡增——说明优化器被迫回表或全表扫描
最容易被忽略的坑:隐藏索引照常消耗写入资源
哪怕索引设为 INVISIBLE,INSERT/UPDATE/DELETE 仍会实时维护 B+ 树和唯一约束校验。压测时如果发现 QPS 下降、CPU 升高、磁盘 IO 暴涨,别急着归因于查询变慢——很可能是隐藏索引在后台默默拖慢写入。尤其当表有多个隐藏索引时,这种开销叠加效应更明显。











