隐藏索引是MySQL 8.0引入的安全试错机制,优化器默认忽略但后台持续维护,切换可见性仅修改元数据、毫秒级完成,可零重建验证删除影响。

直接删索引风险极高,尤其在大表上——重建成本不可控,且无法回滚。用 INVISIBLE 是唯一能零重建、秒级验证“删了会不会出事”的方式。
CREATE INDEX 时加 INVISIBLE 最稳妥
新建即隐藏,避开元数据锁(MDL)阻塞风险,也省去后续状态切换和验证步骤。所有写入开销、唯一性校验、磁盘占用照常,但优化器默认不选它。
-
CREATE INDEX idx_order_status ON orders(status) INVISIBLE;—— 秒级完成,仅改元数据 -
CREATE UNIQUE INDEX idx_email ON users(email) INVISIBLE;—— 唯一索引支持,但若该列是隐式主键(表无显式PRIMARY KEY且email是首个UNIQUE NOT NULL字段),会报ERROR 3522 - 不支持
FULLTEXT、SPATIAL索引,仅限 InnoDB 的 B-tree 类型
ALTER TABLE ALTER INDEX INVISIBLE 切换已有索引
已有索引不能用 MODIFY INDEX 或 SET INVISIBLE,语法错误;必须走标准 DDL。切换本身毫秒级,但需提前确认索引非主键或外键依赖。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
-
ALTER TABLE orders ALTER INDEX idx_created_at INVISIBLE;—— 正确语法,只改元数据 - 执行前查:
SELECT CONSTRAINT_TYPE FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME = 'orders' AND INDEX_NAME = 'idx_created_at';,若返回PRIMARY KEY,直接失败 - 长事务未提交时,DDL 会被卡在 MDL 等待阶段,别在高峰期操作
验证是否真被优化器忽略
别信 SHOW INDEX 的 Visible: NO 就算完事——这是常见误判点。得看执行计划是否实际退化,且排除 optimizer_switch 干扰。
- 查系统表确认:
SELECT IS_VISIBLE FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_NAME = 'orders' AND INDEX_NAME = 'idx_created_at';,返回字符串'NO'才算设置成功(注意不是布尔值) - 跑真实查询:
EXPLAIN SELECT * FROM orders WHERE created_at > '2026-01-01';,若之前走该索引,现在key为空或换成别的索引,才算生效 - 检查开关:
SELECT @@optimizer_switch LIKE '%use_invisible_indexes=on%';,若返回1,先关掉:SET SESSION optimizer_switch = 'use_invisible_indexes=off'; -
USE INDEX (idx_created_at)或FORCE INDEX (idx_created_at)会直接报错ERROR 1176: Key 'idx_created_at' doesn't exist,因为优化器默认“看不见”它
观察指标比 EXPLAIN 更关键
单看一条 EXPLAIN 容易漏掉低频但关键的查询。真正要盯的是线上负载下的三类信号是否同步出现,且持续至少 24 小时。
- 慢查询日志里,原本走该索引的语句
Query_time明显升高,或Rows_examined暴涨 -
Handler_read_next/Handler_read_rnd_next突增(说明全表扫描或随机读变多) - 应用层超时告警、数据库连接池打满等间接现象
- 别只看
performance_schema.table_io_waits_summary_by_index_usage.COUNT_STAR = 0—— 凌晨跑一次的报表可能一天只触发一次,删了就崩
真正容易被忽略的是:不可见索引仍参与唯一性校验和写入维护。即使你确认它“没被查询用”,也要检查是否有 INSERT/UPDATE 因它触发了唯一冲突或拖慢写入。

















