不可见索引在EXPLAIN中不出现是设计行为而非bug,因优化器默认过滤IS_VISIBLE='NO'的索引;需SET SESSION optimizer_switch='use_invisible_indexes=on'临时启用,再通过EXPLAIN FORMAT=JSON验证used_indexes。

不可见索引在EXPLAIN里完全不出现,不是bug是设计行为
你执行 EXPLAIN SELECT * FROM orders WHERE status = 'paid',如果 idx_status 是不可见的,key 字段一定为空或显示其他索引名——它压根不会进入优化器候选集。这不是漏了、没生效,而是优化器在生成执行计划前就过滤掉了所有 IS_VISIBLE = 'NO' 的索引。
验证是否真正“不可见”,不能只信 SHOW INDEX FROM orders 里的 Visible: NO,必须看 EXPLAIN 输出的 key 字段和 used_indexes(用 EXPLAIN FORMAT=JSON)。
-
FORCE INDEX (idx_status)会直接报错ERROR 1176 (HY000): Key 'idx_status' doesn't exist,说明优化器连“存在性”都拒绝承认 -
USE INDEX或IGNORE INDEX同样失效,语法合法但被忽略 - ORM 框架如 Django 的
.extra(index='idx_status')、MyBatis 的<hint>USE INDEX(idx_status)</hint>全部炸掉,灰度前必须全局grep扫描
想临时让不可见索引参与优化?只能手动开 session 开关
默认情况下,不可见索引对优化器是“选择性失明”。要让它重新进入候选集,必须显式开启会话级开关:SET SESSION optimizer_switch = 'use_invisible_indexes=on'。
这个开关只影响当前连接,不影响其他会话,也**不改变索引物理状态**,只是让优化器多看一眼。开完后重跑 EXPLAIN FORMAT=JSON,检查 used_indexes 和 key 是否包含该索引名。
- 绝对不要用
SET GLOBAL optimizer_switch,它会污染所有新连接,生产环境极危险 - 压测时若不开这个开关,你看到的
EXPLAIN是“删掉索引后的理想态”,但真实退化场景可能更糟——比如原来走覆盖索引的UPDATE变成回表,锁范围扩大 - 开开关后若发现性能回退,说明这个索引其实被关键路径依赖,不该删
主键和隐式主键强制不可设为不可见
MySQL 会直接拒绝操作并报错:ERROR 3522 (HY000): A primary key index cannot be invisible 或 ER_PRIMARY_CANT_BE_INVISIBLE。
基于三引擎设计,从微信文章、新闻和博客网页提取干净内容,支持标题作者日期元数据,多格式和批量处理。
隐式主键指没有显式定义 PRIMARY KEY 时,第一个 UNIQUE NOT NULL 列自动成为主键索引——它同样禁止设为不可见。误操作前务必确认:
- 查约束:
SELECT CONSTRAINT_NAME, CONSTRAINT_TYPE FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = 'orders' - 查索引可见性:
SELECT INDEX_NAME, IS_VISIBLE FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_NAME = 'orders' - 哪怕
SHOW CREATE TABLE orders看不到PRIMARY KEY,也可能有隐式主键
备份恢复后不可见状态可能丢失,这是最常被忽略的坑
mysqldump 默认保留 INVISIBLE 属性,但物理备份工具(如 xtrabackup)不保存该元数据。恢复后,原本设为不可见的索引可能意外变回 VISIBLE,导致你之前压测结论完全失效。
上线前必须原子验证:SELECT IS_VISIBLE FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = 'your_db' AND TABLE_NAME = 'orders' AND INDEX_NAME = 'idx_status',不能只靠 SHOW INDEX 或备份日志。
另外,ALTER TABLE ... REORGANIZE PARTITION 可能重置整个表所有索引的可见性,分区表尤其要注意事后复查。

















