隐藏索引是优化器的选择性失明,非物理删除:仍参与写入维护、唯一性校验和磁盘占用,仅不被优化器自动选用;确认方式为查show index的visible列是否为no,或用explain验证执行路径。

隐藏索引不是“禁用开关”,而是让优化器彻底忽略它——但写入开销和唯一性检查照常发生。
怎么确认某个索引是否已被设为 INVISIBLE
别信 SHOW INDEX 的 Comment 字段,它在 MySQL 8.0.12 之前永远是 NULL。真正可靠的判断方式只有两个:
- 查
SHOW INDEX FROM t输出里的Visible列:值为NO才是隐藏状态 - 用
SELECT * FROM information_schema.STATISTICS WHERE TABLE_NAME = 't' AND INDEX_NAME = 'idx_name',看INDEX_COMMENT是否含invisible(仅部分版本支持) - 执行
EXPLAIN SELECT ... WHERE indexed_col = ?,如果没走该索引,且已排除其他干扰(如类型隐式转换、函数包裹),基本可反向验证
为什么 ALTER INDEX ... INVISIBLE 很快,但仍有风险
这个操作本身是 Instant DDL,不重建索引树,所以毫秒级完成。但它仍需获取元数据锁(MDL),在高并发写入场景下可能短暂阻塞后续 DML。更关键的是:
-
INVISIBLE索引仍参与唯一性校验:哪怕设为不可见,INSERT或UPDATE触发重复键冲突时,依然会报ERROR 1062 (23000): Duplicate entry ... for key 'idx_name' - 它持续消耗磁盘空间和写入性能:每次
INSERT/UPDATE/DELETE都要更新 B+ 树节点,这点和删除索引完全不同 - 主键和主键约束上的索引无法设为
INVISIBLE,尝试会直接报错ERROR 3522 (HY000): Primary key cannot be invisible
如何临时“启用”一个 INVISIBLE 索引做 A/B 测试
不能靠改全局配置,也不能靠 SET SESSION optimizer_switch = "use_invisible_indexes=on" —— 这个开关在 MySQL 8.0.23+ 已被移除,且即使旧版本开启,也只对当前会话生效,线上环境极难管控。安全做法是显式提示:
- 用
USE INDEX (idx_name):告诉优化器“只考虑这个索引”,但不强制使用;如果该索引真不匹配查询条件,优化器仍可能选全表扫描 - 用
FORCE INDEX (idx_name):强制走该索引,哪怕代价更高;适合压测时对比执行计划差异 - 注意:这两种提示只对单条语句生效,无法跨事务或连接复用;不要在 ORM 自动生成的 SQL 中硬编码,容易被覆盖
什么时候该删,而不是一直留着 INVISIBLE 索引
INVISIBLE 不是归档方案,长期挂着反而埋雷:
- 备份恢复时若用
mysqldump --no-create-info+ 手动建表,INVISIBLE属性会丢失,从库或新实例上变成可见索引,行为突变 - 跨版本升级(比如迁移到 MySQL 5.7)会因语法不识别而失败,报错
ERROR 1064 (42000): You have an error in your SQL syntax - 某些监控工具(如 pt-index-usage、部分云厂商 RDS 控制台)不解析
Visible列,把 INVISIBLE 索引当活跃索引统计,误导容量评估 - 真正该删的信号是:
performance_schema.table_io_waits_summary_by_index_usage中该索引的COUNT_STAR持续为 0,且慢日志里对应查询未出现新增延迟
最易被忽略的一点:INVISIBLE 索引在 CREATE TABLE ... LIKE 或 CREATE TABLE ... SELECT 时不会被复制,它的“隐形”会直接变成“消失”。如果依赖这类操作做灰度发布或影子表验证,必须手动补建并设为 INVISIBLE,否则测试就失真了。











