不可见索引是元数据标记,需用create index ... invisible或alter table ... alter index ... invisible创建/切换,主键等特定索引不支持;explain默认忽略它,验证须查information_schema.statistics中is_visible='no'并关闭optimizer_switch='use_invisible_indexes=on',否则结果失真。

不可见索引不需要“配置”,它本质是元数据标记,真正要动的是 optimizer_switch 开关和索引状态本身;否则压测或验证结果全是假象。
怎么创建或切换索引为 INVISIBLE
新建索引时直接加 INVISIBLE 最稳妥,已有索引必须用 ALTER TABLE ... ALTER INDEX ... INVISIBLE 切换——其他写法(如 SET INVISIBLE、MODIFY INDEX)全报错 ERROR 1064 或 ERROR 3522。
-
CREATE INDEX idx_status ON orders(status) INVISIBLE;—— 新建即隐藏,毫秒完成,不锁表 -
ALTER TABLE orders ALTER INDEX idx_user_id INVISIBLE;—— 切换已有索引,注意:主键、首个UNIQUE NOT NULL索引会触发ERROR 3522 - 执行后别只看
SHOW INDEX FROM orders\G的Visible: NO,要查INFORMATION_SCHEMA.STATISTICS确认IS_VISIBLE = 'NO'(注意是字符串,不是布尔值)
为什么 EXPLAIN 看不到这个索引
这不是 bug,是设计行为:EXPLAIN 默认根本不会把 INVISIBLE 索引纳入候选集,所以 key 字段为空、type 退化为 ALL 或走别的索引,才是正常表现。
- 想验证物理存在且可用?用
EXPLAIN SELECT * FROM orders USE INDEX (idx_user_id) WHERE user_id = 123;—— 若报ERROR 1176: Key 'idx_user_id' doesn't exist,说明索引名错了或已被删 -
FORCE INDEX同样绕不过可见性逻辑,一旦索引不可见,FORCE会直接失败,不是“强制用了但没效果” - 别信 ORM 或运维脚本里硬编码的
SHOW INDEX列序,它们可能漏掉Visible字段;唯一可靠的是查INFORMATION_SCHEMA.STATISTICS
压测前必须关掉 use_invisible_indexes
如果之前执行过 SET GLOBAL optimizer_switch = 'use_invisible_indexes=on';,那所有会话都会“看见”不可见索引,EXPLAIN 结果完全失真。
- 压测前务必执行:
SET SESSION optimizer_switch = 'use_invisible_indexes=off'; - 临时对比 A/B 效果?用 SQL 级提示更安全:
SELECT /*+ SET_VAR(optimizer_switch = "use_invisible_indexes=on") */ * FROM orders WHERE status = 'paid';(仅 MySQL 8.0.22+ 支持) - 检查当前状态:
SELECT @@optimizer_switch LIKE '%use_invisible_indexes=on%';返回1就得立刻关
最容易被忽略的写入开销
索引设为 INVISIBLE 后,INSERT/UPDATE/DELETE 仍实时维护 B+ 树、校验唯一约束、占用磁盘空间——它只是对优化器“隐身”,不是“卸载”。
- 压测中若发现 QPS 下降、CPU 升高、IO 暴涨,别只盯着查询变慢;多个隐藏索引叠加时,写入拖累会非常明显
- 灰度测试前,必须全局扫描代码、ORM 配置、存储过程,移除所有
FORCE INDEX和USE INDEX,否则一执行就报错 -
mysqldump 还原后索引自动变回
VISIBLE,xtrabackup 虽保留元数据,但跨版本恢复可能出问题











