隐藏索引不省空间、不减写入开销、不规避force index;确认下线需结合慢日志、explain json的used_indexes、orm/sql中强制提示、唯一约束依赖及备份后is_visible校验。

能,但必须绕开三个关键误判:它不省空间、不减写入开销、也不自动规避 FORCE INDEX。
怎么确认一个索引真该下线?别只看 performance_schema.table_io_waits_summary_by_index_usage
这个表里 COUNT_STAR = 0 只是基础信号,不是判决书。低频但关键的查询(比如月底报表、风控对账)可能一个月跑一次,却完全依赖这个索引。真实判断要结合:
- 慢查询日志里过去7天是否有 SQL 的
key字段明确命中该索引 -
EXPLAIN FORMAT=JSON输出中used_indexes出现该索引名的频率 - 应用层是否在 ORM 配置或手写 SQL 里写了
FORCE INDEX(idx_name)或USE INDEX(idx_name) - 唯一约束是否隐式依赖它——查
INFORMATION_SCHEMA.STATISTICS的NON_UNIQUE和SEQ_IN_INDEX,若它是第一个UNIQUE NOT NULL索引,就不能设为不可见
执行 ALTER TABLE ... ALTER INDEX ... INVISIBLE 后,为什么 EXPLAIN 还是走它?
大概率是以下两种情况之一:
- 当前会话打开了
use_invisible_indexes=ON,查SELECT @@optimizer_switch就能确认;生产环境严禁全局开启,压测时只用SET SESSION optimizer_switch='use_invisible_indexes=ON' - SQL 里写了
FORCE INDEX(idx_name)—— 这个提示会直接无视可见性,强制使用并报错ERROR 1176;必须提前扫描所有代码、SQL 模板、ORM 映射配置
验证是否生效,只看两处:SELECT INDEX_NAME, IS_VISIBLE FROM INFORMATION_SCHEMA.STATISTICS 返回 'NO',且 EXPLAIN SELECT ... WHERE ... 的 key 字段为空或切换到其他索引。
压测期间必须盯住的三个指标变化
单条 EXPLAIN 不代表真实负载,得看聚合行为:
-
Handler_read_next和Handler_read_rnd_next是否突增——说明退化为全表扫描或文件排序 -
performance_schema.events_statements_summary_by_digest中对应 SQL 的AVERAGE_TIMER_WAIT和ROWS_EXAMINED是否翻倍 - 慢查询日志里同一条 SQL 的
Query_time和Rows_examined是否显著升高,尤其注意是否触发了临时表或磁盘排序
注意:innodb_rows_inserted 和 innodb_buffer_pool_reads 不会因设为不可见而下降——B+ 树照常更新,写入开销一分没少。
备份恢复后不可见状态可能丢失,这事很隐蔽
mysqldump 默认保留 INVISIBLE 属性,但 xtrabackup 等物理备份工具不保存该元数据。恢复后索引可能意外变回 VISIBLE,导致你之前一周的压测结论完全失效。每次恢复后必须立刻执行:
SELECT INDEX_NAME, IS_VISIBLE FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = 'your_db' AND TABLE_NAME = 'your_table';
主键索引、全文索引、空间索引从一开始就不支持设为不可见,尝试会报 ERROR 3522;分区表上对某一分区设为不可见,ALTER TABLE ... REORGANIZE PARTITION 可能重置所有分区索引可见性,必须事后复查。











