不可见索引不是关掉索引,而是让优化器默认彻底跳过它,但写入开销、磁盘占用和唯一性校验仍存在;用ALTER TABLE ... ALTER INDEX ... INVISIBLE可毫秒级设为不可见,不锁表不重建,主键及隐式主键不支持,EXPLAIN默认不显示,需通过系统表或强制开关验证。

不可见索引不是“关掉索引”,而是让优化器默认彻底跳过它——但写入开销、磁盘占用、唯一性校验全在。它解决的是那个经典困境:你不敢删,又怕它拖慢写入。
怎么把已有索引设为不可见?只改元数据,不锁表
用 ALTER TABLE ... ALTER INDEX ... INVISIBLE,毫秒级完成,无需重建索引:
ALTER TABLE orders ALTER INDEX idx_status INVISIBLE;- 必须确保该索引不是主键(含显式或隐式),否则报错
ERROR 3522 (HY000): Primary key cannot be invisible - 不能写成
MODIFY INDEX或SET INVISIBLE,那些语法直接报ERROR 1064 - 唯一索引(
UNIQUE KEY)可以设为不可见,但若被外键或约束依赖,需提前确认逻辑一致性
为什么 EXPLAIN 看不到它?以及怎么让它“临时现身”
EXPLAIN 默认不显示不可见索引,这不是 bug,是设计行为:优化器压根没把它放进候选集。验证是否生效,得靠对比:
- 普通查询:
EXPLAIN SELECT * FROM orders WHERE status = 'shipped';→ 若之前走idx_status,现在key字段为空或换索引,基本确认生效 - 强制启用(仅测试用):
SELECT /*+ SET_VAR(optimizer_switch = "use_invisible_indexes=on") */ * FROM orders WHERE status = 'shipped';再EXPLAIN;注意该提示仅在 MySQL 8.0.22+ 支持 - 会话级开关更常用:
SET SESSION optimizer_switch = 'use_invisible_indexes=on';,但务必记得之后关掉,避免干扰其他语句 -
FORCE INDEX (idx_status)会直接报错ERROR 1176 (42000): Key 'idx_status' doesn't exist,别试
怎么判断它到底能不能删?别只看单条 EXPLAIN
真实影响藏在负载里,不是执行计划里。重点盯三类信号:
- 查
performance_schema.table_io_waits_summary_by_index_usage,观察COUNT_STAR是否归零 —— 但注意:低频关键查询可能漏统计,不能作为唯一依据 - 开慢查询日志,过滤出
Query_time明显增长的语句,尤其关注原来走该索引的查询路径是否退化为type: ALL - 监控
Handler_read_*指标突增(如Handler_read_next、Handler_read_rnd_next),这是全表扫描/回表加重的典型信号 - 观察周期至少 24 小时,覆盖业务波峰波谷;期间若异常,
ALTER TABLE orders ALTER INDEX idx_status VISIBLE;秒级恢复
最容易被忽略的四个实际陷阱
很多人设完 INVISIBLE 就以为安全了,结果线上出问题:
- 误信
SHOW INDEX FROM orders\G的Comment字段 —— 它恒为NULL,唯一可靠的是查INFORMATION_SCHEMA.STATISTICS.IS_VISIBLE或Visible: NO - 忘了检查
optimizer_switch全局状态 —— 如果有人之前开了use_invisible_indexes=on,你的“不可见”就失效了,查SELECT @@optimizer_switch LIKE '%use_invisible_indexes=on%' - 以为不可见索引不占空间、不影响写入 —— 它照常消耗磁盘、拖慢
INSERT/UPDATE/DELETE,这点和删除索引有本质区别 - 跨版本迁移含不可见索引的表结构 —— MySQL 5.7 或更低版本解析
mysqldump输出会失败,导出时加--no-create-info或手动清理INVISIBLE关键字



















