隐藏索引不会被优化器选中,但仍参与写入维护、可被force index强制使用,主键及隐式主键不可隐藏,其本质是优化器的选择性失明而非物理删除。

隐藏索引不会被优化器选中,但也不是“彻底消失”——它仍参与写入维护、仍能被 FORCE INDEX 强制调用,关键看优化器是否在计划生成阶段把它纳入候选集。
EXPLAIN 里看不到索引名,不代表隐藏成功
很多人只查 SHOW INDEX FROM tbl 看 Visible 列是 NO 就以为完事了。其实真正要验证的是优化器行为:
-
EXPLAIN FORMAT=TRADITIONAL SELECT * FROM tbl WHERE col = ?中的key字段不能出现该索引名 - 如果用了
EXPLAIN ANALYZE,还要确认used_key或执行路径里没走这个索引 - 注意 session 级配置干扰:比如
optimizer_switch='use_index_extensions=off'可能让某些索引“意外不可见”,建议用干净连接测试(mysql -u root -p --defaults-file=/dev/null)
FORCE INDEX 能绕过隐藏限制,这是双刃剑
隐藏索引对 FORCE INDEX 完全无效,只要语法合法,它就会被强制使用:
-
SELECT * FROM tbl FORCE INDEX(idx_name) WHERE col = 123会照常走idx_name,哪怕它是INVISIBLE - ORM 框架可能隐式插入这类 hint:Django 的
.extra(index='idx_name')、MyBatis 的<hint>USE INDEX(idx_name)</hint>都可能绕过隐藏逻辑 - 灰度验证前必须全局 grep 代码库里的
FORCE、USE INDEX、IGNORE INDEX等关键词
主键和隐式主键索引永远不能隐藏
MySQL 会直接拒绝这类操作,并报错 ER_PRIMARY_CANT_BE_INVISIBLE:
- 显式定义的
PRIMARY KEY索引无法设为INVISIBLE - 没有显式主键时,第一个
UNIQUE NOT NULL索引会被当作隐式主键,同样禁止隐藏 - 尝试
ALTER TABLE t ALTER INDEX uk_col INVISIBLE前,先确认它是不是隐式主键:SELECT CONSTRAINT_NAME, CONSTRAINT_TYPE FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = 't' AND CONSTRAINT_SCHEMA = DATABASE()
隐藏不等于卸载,写入开销一分不少
很多人误以为“看不见=不维护”,其实 B+ 树结构照常更新:
-
INSERT/UPDATE/DELETE依然要写入该索引页,innodb_rows_inserted统计值不会变 - 如果你发现某索引写入压力大、查询几乎不用,隐藏它只是掩盖问题;真要降负载,得
DROP INDEX - 对比测试时别只比查询 QPS,一定要同步压测写入吞吐(比如用 Sysbench 的
oltp_write_only场景)
最常被忽略的一点:隐藏索引不是开关,而是优化器的“选择性失明”。它不改变物理结构,也不减少磁盘占用或写入成本,唯一作用是切断优化器到该索引的决策路径——而这条路径,随时可能被 FORCE 或 ORM hint 重新打通。











