mysql 不显示隐藏索引不是 bug,而是设计使然:优化器在解析阶段就过滤掉 is_visible = 'no' 的索引,explain 不会将其纳入候选集,force index 会直接报错“key doesn't exist”。

EXPLAIN 为什么完全不显示隐藏索引
这不是 bug,是 MySQL 的明确设计:优化器在生成执行计划前就过滤掉了 IS_VISIBLE = 'NO' 的索引,EXPLAIN 根本不会把它们放进候选集。哪怕你写 FORCE INDEX (idx_name),也会直接报错 ERROR 1176 (HY000): Key 'idx_name' doesn't exist in table 't1'——它不是“没选中”,而是解析阶段就被判定为“不存在”。
-
EXPLAIN SELECT * FROM t1 WHERE col = ?返回key: NULL或退化为type: ALL,才说明隐藏生效 - 别信
SHOW INDEX FROM t1的Comment字段(恒为NULL),要看Visible列是否为NO - 查
information_schema.STATISTICS更可靠:SELECT INDEX_NAME, IS_VISIBLE FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_NAME = 't1' AND INDEX_NAME = 'idx_name',注意IS_VISIBLE是字符串'YES'或'NO',不是布尔值
怎么临时启用隐藏索引来对比执行计划
想真实验证“有/无该索引”的差异,必须显式打开优化器开关;否则所有对比都是无效的。默认 use_invisible_indexes=off,所以 EXPLAIN 看不到它,是正常现象。
- 会话级启用(推荐):
SET SESSION optimizer_switch = 'use_invisible_indexes=on';,然后立刻跑EXPLAIN - SQL 级启用(更安全,仅限 MySQL 8.0.22+):
SELECT /*+ SET_VAR(optimizer_switch = "use_invisible_indexes=on") */ * FROM t1 WHERE col = ?; - 两次
EXPLAIN必须在同一会话下,且确认没被其他optimizer_switch设置干扰(比如use_index_extensions=off可能掩盖效果) - 全局设置
SET GLOBAL optimizer_switch会影响所有新连接,生产环境禁用
哪些索引根本不能设为 INVISIBLE
MySQL 对不可见索引有硬性限制,违反会直接报错,不是静默忽略。
- 主键索引(含隐式主键):执行
ALTER TABLE t1 ALTER INDEX PRIMARY INVISIBLE报ERROR 3522 (HY000): A primary key index cannot be invisible - 第一个
UNIQUE NOT NULL索引(若表无显式主键):同样触发ERROR 3522 - FULLTEXT 和 SPATIAL 索引:不支持
INVISIBLE关键字,语法报错 - 唯一约束索引可以设为不可见,但
INSERT/UPDATE仍强制检查唯一性,失败逻辑不变
为什么写了 ALTER 却没生效?常见漏点
操作本身很快(毫秒级元数据变更),但容易因环境或认知偏差导致“看似没生效”。
- GUI 工具(Workbench、DBeaver、Navicat)完全不支持切换可见性,右键 → 修改 → 点确定 = 白忙活
- 刚执行完
ALTER TABLE ... INVISIBLE就查information_schema,可能有毫秒级延迟,建议优先以EXPLAIN结果为准 - ORM 或中间件里硬编码了
FORCE INDEX,上线后直接报错,必须提前清理 - 备份恢复后,
mysqldump保留可见性,但xtrabackup不保证,需手动复查 - 隐藏索引仍被写入维护(B+树更新、buffer pool 占用、redo log 产生),它只省掉“被选中”这一步,别指望写入变快或磁盘释放
真正难的不是让索引“看不见”,而是判断它到底该不该消失——执行计划只是起点,慢日志、performance_schema.table_io_waits_summary_by_index_usage、Handlerread* 指标、凌晨批处理任务表现,都得一起看。











