隐式主键和隐藏主键索引永远不可设为invisible,mysql会报错er_primary_cant_be_invisible;需先查information_schema确认约束类型,再用explain验证优化器是否真正忽略该索引。

怎么确认一个索引能不能被隐藏
主键和隐式主键索引永远不能设为 INVISIBLE,MySQL 会直接报错 ER_PRIMARY_CANT_BE_INVISIBLE。执行前务必查清目标索引是否属于主键约束:
SELECT CONSTRAINT_NAME, CONSTRAINT_TYPE FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = 'your_table' AND CONSTRAINT_SCHEMA = DATABASE();- 如果结果里有非
PRIMARY的唯一约束(比如UNIQUE KEY),也要小心——它可能是隐式主键(当表没显式主键、且第一个NOT NULL列带索引时) - 辅助索引(
KEY或INDEX)基本都可隐藏,但唯一索引隐藏后仍强制校验唯一性,INSERT冲突照报错
隐藏后怎么验证优化器真没用它
别只看 SHOW INDEX FROM tbl 里 Visible 是 NO 就放心——这只能说明元数据改了,不等于优化器真的绕开了它。
- 必须用
EXPLAIN FORMAT=TRADITIONAL SELECT ... WHERE ...,检查key字段是否出现该索引名;key为空才表示生效 - 如果用了
EXPLAIN ANALYZE,还要盯住used_key和实际执行路径,避免误判 - 注意 session 级配置干扰:比如
optimizer_switch='use_index_extensions=off'可能让其他索引也“不可见”,建议用干净连接测试:mysql -u root -p --defaults-file=/dev/null
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 -r "FORCE INDEX\|USE INDEX\|IGNORE INDEX"扫描代码库,否则监控再久也发现不了漏网查询
写入性能不会因隐藏而改善
隐藏索引 ≠ 卸载索引。它对写入开销毫无减免,这点必须提前认清。
-
INSERT/UPDATE/DELETE依然要维护 B+ 树结构,innodb_rows_inserted统计值不变 - 如果你观察到某索引写入压力大、查询几乎不用,隐藏它只是“假装看不见”,不是解决方案;真要降负载,得
DROP INDEX - 对比测试时别只压测查询 QPS,一定要同步跑
sysbench oltp_write_only场景,否则你会误判收益
真实场景里,最容易栽在“以为隐藏就万事大吉”上——它不减少磁盘占用、不降低写入成本、不阻止 FORCE 使用,唯一作用就是让优化器在计划生成阶段自动过滤掉这个候选索引。想靠它长期占着不删?不如早点 DROP 干净。











