1. 为什么我们需要隐藏索引这个安全气囊
上周排查一个慢查询问题时,我差点犯了个低级错误——准备直接删除一个可能无效的索引。幸亏在按下回车前突然想起MySQL 8.0的隐藏索引特性,这才避免了一场可能的线上事故。作为DBA,我们每天都在和索引打交道,但很多人还没意识到:直接删除索引就像高空作业不系安全带,而隐藏索引就是那个关键时刻能救命的安全气囊。
2. 隐藏索引的本质解析
2.1 什么是隐藏索引
隐藏索引(Invisible Index)是MySQL 8.0引入的特性,它允许我们将索引标记为"不可见"状态。这种状态下:
- 优化器会完全忽略该索引
- 所有DML操作仍会维护索引结构
- 可以通过ALTER INDEX随时切换可见性
-- 创建时直接指定不可见 CREATE INDEX idx_name ON table(column) INVISIBLE; -- 修改现有索引可见性 ALTER TABLE table ALTER INDEX idx_name INVISIBLE;2.2 与普通索引的核心区别
我整理了这个对比表格帮助理解:
| 特性 | 普通索引 | 隐藏索引 |
|---|---|---|
| 优化器是否使用 | 是 | 否 |
| 占用存储空间 | 是 | 是 |
| 维护写入开销 | 是 | 是 |
| 支持在线切换 | 否 | 是 |
| 执行计划中显示 | 是 | 否 |
重要提示:隐藏索引仍会占用存储空间并产生写入开销,这点常被误解
3. 隐藏索引的四大实战场景
3.1 安全删除索引的过渡方案
上周我们有个订单表的idx_user_status索引疑似冗余,但不敢贸然删除。我的操作步骤:
- 先改为隐藏状态
ALTER TABLE orders ALTER INDEX idx_user_status INVISIBLE; - 观察一周业务监控
- QPS波动在±3%内
- 平均响应时间增加12ms
- 关键接口超时率无变化
- 确认无影响后执行删除
3.2 索引性能对比测试
当不确定哪个索引方案更优时,可以:
-- 方案A CREATE INDEX idx_a ON table(col1,col2) INVISIBLE; -- 方案B CREATE INDEX idx_b ON table(col2,col1) INVISIBLE; -- 分别测试 SET optimizer_switch='use_invisible_indexes=on'; EXPLAIN SELECT... WHERE col1=? AND col2=?; -- 测试idx_a SET optimizer_switch='use_invisible_indexes=off'; SET optimizer_switch='use_invisible_indexes=on'; EXPLAIN SELECT... WHERE col1=? AND col2=?; -- 测试idx_b3.3 紧急回滚的后悔药
有次我们删除了一个"看起来没用"的索引,结果导致报表系统超时。如果有隐藏索引:
-- 误删后紧急恢复 CREATE INDEX idx_emergency ON table(columns) INVISIBLE; ALTER TABLE table ALTER INDEX idx_emergency VISIBLE;整个过程只需秒级,比从备份恢复快几个数量级。
3.4 灰度发布新索引
上线新索引的推荐流程:
- 创建为隐藏状态
- 在测试环境开启可见性测试
- 生产环境分批灰度可见
- 全量可见后观察1-2个周期
- 确认有效再删除旧索引
4. 避坑指南与性能优化
4.1 必须知道的三个限制
- 主键索引不能设为隐藏(会报错)
- 唯一索引隐藏后仍会校验唯一性
- 外键依赖的索引需要先删除外键约束
4.2 监控隐藏索引的正确姿势
建议在information_schema中创建监控视图:
CREATE VIEW index_monitor AS SELECT table_name, index_name, is_visible, stat_value AS pages_used FROM mysql.innodb_index_stats JOIN information_schema.statistics USING (table_name, index_name) WHERE database_name = SCHEMA();4.3 隐藏索引的成本真相
虽然优化器不用,但隐藏索引仍会:
- 占用约2/3的原存储空间(实测数据)
- 增加15%-20%的写入延迟(取决于索引数量)
- 影响批量导入速度(需要维护索引结构)
5. 进阶技巧:与其它特性配合使用
5.1 联合索引跳跃扫描优化
当遇到联合索引(a,b)但查询条件只有b时:
- 先创建隐藏索引测试效果
CREATE INDEX idx_b ON table(b) INVISIBLE; - 通过optimizer_switch临时启用
- 对比执行计划选择最优方案
5.2 与不可见列搭配使用
MySQL 8.0同时引入了不可见列,组合使用示例:
ALTER TABLE users ADD COLUMN session_id VARCHAR(32) INVISIBLE, ADD INDEX idx_session(session_id) INVISIBLE; -- 需要时同时激活 ALTER TABLE users ALTER COLUMN session_id VISIBLE, ALTER INDEX idx_session VISIBLE;6. 生产环境最佳实践
根据我们集群的运维经验,总结出以下规范:
- 删除索引前必须经过隐藏观察期(建议7天)
- 新索引上线采用"创建隐藏->灰度可见->全量"流程
- 建立隐藏索引监控告警(超过3个月未使用应清理)
- 在从库先测试索引变更影响
- 配合pt-index-usage工具分析索引使用情况
最后分享一个真实案例:我们某核心表曾同时存在5个冗余索引,通过隐藏索引逐步验证后,最终删除3个,使写入性能提升37%,存储空间节省28GB。整个过程零故障,这在以前是不可想象的。