不可见索引不是性能加速器,而是索引变更的“安全气囊”——它不能让查询变快,但能让你在删索引前确认不会变慢;alter table alter index invisible 是元数据级瞬时操作,仅翻转is_visible标志位,不重建索引、不锁表、不提升查询性能,专为安全验证索引价值而设计。

不可见索引不是性能加速器,而是索引变更的“安全气囊”——它不能让查询变快,但能让你在删索引前确认不会变慢。
ALTER TABLE ALTER INDEX INVISIBLE 为什么必须写全语法
MySQL 8.0 只认 ALTER TABLE t1 ALTER INDEX idx_name INVISIBLE 这一种形式。写成 SET INVISIBLE、MODIFY INDEX idx_name INVISIBLE 或 ALTER INDEX idx_name INVISIBLE ON t1 全部报错 ERROR 1064 (42000)。这不是语法糖缺失,是设计上强制你显式声明表名和索引名,避免误操作扩散。
-
INVISIBLE和VISIBLE是唯二合法关键字,大小写不敏感但建议全大写 - 主键索引(含隐式主键)禁止设为不可见,执行直接触发
ERROR 3522 (HY000): Primary key cannot be invisible - 外键依赖的唯一索引、全文索引、空间索引同样不支持,提前查
INFORMATION_SCHEMA.STATISTICS确认INDEX_TYPE和约束关系
EXPLAIN 看不到不可见索引?先关掉 use_invisible_indexes
默认情况下 EXPLAIN 压根不把 IS_VISIBLE = 'NO' 的索引纳入候选集,所以 key 字段为空或走别的索引,这正常。但很多人卡在:刚执行完 ALTER 就跑 EXPLAIN,发现还是走原索引——大概率是会话里开着 use_invisible_indexes=on。
- 查当前开关状态:
SELECT @@optimizer_switch LIKE '%use_invisible_indexes=on%' - 临时关闭(推荐):
SET SESSION optimizer_switch = 'use_invisible_indexes=off' - 别信
SHOW INDEX FROM t1\G的Comment字段,它恒为NULL;以INFORMATION_SCHEMA.STATISTICS.IS_VISIBLE为准,值是字符串'YES'或'NO'
FORCE INDEX(idx_name) 报错 ERROR 1176 怎么办
这不是 bug,是设计行为:FORCE INDEX 要求索引逻辑存在且可见。一旦设为不可见,优化器在解析阶段就把它“注销”了,所以 ERROR 1176 (HY000): Key 'idx_name' doesn't exist 是必然结果,不是失效,是彻底不可见。
- 应用层 ORM(如 MyBatis 的
@SelectKey、Django 的extra())若硬编码了FORCE INDEX或USE INDEX,切换后会直接失败,必须提前清理 - 想对比“有/无该索引”的真实影响,只能用会话级开关:
SET SESSION optimizer_switch = 'use_invisible_indexes=on',再跑EXPLAIN - 单条 SQL 启用更安全:
SELECT /*+ SET_VAR(optimizer_switch = "use_invisible_indexes=on") */ * FROM t1 WHERE ...
压测时盯住 Handler_read_rnd_next,不是只看 EXPLAIN
EXPLAIN 只反映单条语句的计划,而线上真实压力来自并发和数据分布。切换后至少观察 24 小时,覆盖完整业务周期,重点看三个信号:
-
Rows_examined暴涨的语句(说明回表或扫描范围扩大) -
Handler_read_rnd_next明显上升(典型全表扫描或随机读激增) -
performance_schema.table_io_waits_summary_by_index_usage.count_star是否归零——但注意:低频关键查询(如凌晨报表)可能漏统计,不能当唯一依据
物理备份(如 xtrabackup)不保留 IS_VISIBLE 元数据,恢复后索引自动变回 VISIBLE;而 mysqldump 默认导出 INVISIBLE 属性,但 MySQL 5.7 及更低版本无法解析,回滚链路必须提前验证。











