MySQL 不可见索引适合怎样做上线前回归验证
来源:17golang原创
时间:2026-10-08 22:46:25 422浏览 收藏
不可见索引最适合用在“准备删索引,但还没有足够证据”的阶段。它让 MySQL 优化器默认忽略某个二级索引,却继续维护该索引,因此可以先观察查询计划和工作负载会不会退化;一旦出现风险,只需把索引恢复为 VISIBLE,不必在大表上重新建索引。
我更愿意把它理解成一次可快速回退的上线演练,而不是“删索引的安全开关”。它能降低回退成本,却不会自动替你选择查询样本、定义延迟阈值,也不能证明所有流量都已经覆盖。
MySQL 8.4 官方文档:https://dev.mysql.com/doc/refman/8.4/en/invisible-indexes.html
先理解不可见索引改变了什么
索引设为不可见后,默认的优化器计划不会使用它。索引本体没有被删除:表数据发生变化时仍会更新它;如果它是唯一索引,唯一性约束也仍然生效。主键不能设为不可见,某些承担隐式主键作用的 UNIQUE NOT NULL 索引同样不能直接隐藏。

这也是我不会把不可见索引当成存储优化结果的原因。观察期内,写入仍然承担维护成本;它验证的是“查询能不能离开这个索引”,不是“删掉后能立刻省下多少空间和写放大”。
回归前先做一份最小基线
第一次做这类验证时,最容易犯的错是先改索引,再临时寻找受影响 SQL。更稳妥的做法是先把候选索引、使用它的查询摘要和当前计划固定下来。下面以订单表的 idx_orders_created 为例。
-- 记录候选索引当前是否可见,并确认它不是主键 SELECT INDEX_NAME, NON_UNIQUE, IS_VISIBLE, SEQ_IN_INDEX, COLUMN_NAME FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = 'app' AND TABLE_NAME = 'orders' ORDER BY INDEX_NAME, SEQ_IN_INDEX; -- 保存关键 SQL 的树形计划,重点关注访问方式、扫描行估算和排序节点 EXPLAIN FORMAT=TREE SELECT id, customer_id, created_at FROM app.orders WHERE created_at >= '2026-10-01' ORDER BY created_at DESC LIMIT 100;
基线不要只留一条“看起来会用索引”的查询。我通常至少覆盖四类样本:高频读取、低频报表、后台批处理,以及带索引提示的历史 SQL。尤其要搜索 USE INDEX、FORCE INDEX 和 IGNORE INDEX;官方文档明确指出,引用不可见索引的索引提示可能报错,这类依赖比单纯计划变慢更容易在回归中被漏掉。
把索引切成不可见,并预先写好恢复语句
确认变更对象后,先在预发布环境或受控发布窗口执行可见性切换。可见性修改是原地操作,通常比删除后再重建快得多,但仍应按团队的 DDL 变更流程评估元数据锁和并发影响。
-- 变更前再次核对索引名,避免隐藏错误对象 SHOW INDEX FROM app.orders; -- 让优化器默认忽略候选二级索引 ALTER TABLE app.orders ALTER INDEX idx_orders_created INVISIBLE; -- 回退语句提前准备好,出现超阈值退化时立即恢复 ALTER TABLE app.orders ALTER INDEX idx_orders_created VISIBLE;
这里我会把“恢复可见”视为第一回退动作,而不是直接重新建索引。因为索引一直在随数据更新,恢复后可以重新进入优化器候选集合。若环境中存在连接池,要注意不同连接可能保留各自的会话设置,不能把一个会话的比较结果当成全局状态。
把回归验证拆成四组证据
单看一次 EXPLAIN 很容易得到过于乐观的结论。我会把证据拆成四组,并要求它们都能回到变更前的基线。

1. 执行计划证据
先在默认状态下查看优化器不使用不可见索引的计划,再用单条语句级开关让优化器临时把不可见索引纳入候选,比较两种计划。这样无需把索引恢复为全局可见,也能判断它是否仍有明显价值。
-- 默认开关为 off:观察没有候选索引时的计划
EXPLAIN FORMAT=TREE
SELECT id, customer_id, created_at
FROM app.orders
WHERE created_at >= '2026-10-01'
ORDER BY created_at DESC
LIMIT 100;
-- 仅对这一条语句允许优化器考虑不可见索引,便于做对照
EXPLAIN FORMAT=TREE
SELECT /*+ SET_VAR(optimizer_switch = 'use_invisible_indexes=on') */
id, customer_id, created_at
FROM app.orders
WHERE created_at >= '2026-10-01'
ORDER BY created_at DESC
LIMIT 100;
比较时不只看 key 名称,还要看访问类型、估算扫描行数、是否新增排序或临时表,以及连接顺序有没有变化。计划变化不等于一定退化,但它提示哪些 SQL 应进入下一层工作负载观察。
2. 工作负载摘要
Performance Schema 的语句摘要更适合回答“真实流量里哪一类 SQL 变慢了”。观察前先记录团队已经使用的摘要窗口,变更后按相同窗口比较调用次数、总等待、平均等待和扫描行数。不要在没有确认影响范围时随意清空全局摘要。
-- 只读取业务库相关摘要;阈值应结合自己的基线定义
SELECT DIGEST_TEXT,
COUNT_STAR,
SUM_TIMER_WAIT,
AVG_TIMER_WAIT,
SUM_ROWS_EXAMINED
FROM performance_schema.events_statements_summary_by_digest
WHERE SCHEMA_NAME = 'app'
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 20;
3. 慢查询与错误证据
如果新的全表扫描或排序把查询推过慢日志阈值,它会成为很直接的回归信号。同时检查应用错误日志中是否出现与索引提示相关的失败。不要只盯平均延迟:低频报表可能调用次数很少,却会突然扫描大量数据。
4. 业务阈值与回退条件
“没有报错”不是通过标准。更实用的判定表如下,具体数值应来自服务自己的 SLO 和基线,而不是照搬固定百分比。
| 观察项 | 通过条件 | 触发动作 |
|---|---|---|
| 核心 SQL 计划 | 访问路径符合预期,无不可接受的全表扫描或额外排序 | 出现关键退化就恢复 VISIBLE |
| 语句摘要 | 平均等待、扫描行数和错误数保持在业务阈值内 | 定位对应摘要并延长或终止观察 |
| 慢查询日志 | 没有新增高成本 SQL 类型 | 补样本并检查索引依赖 |
| 业务指标 | 接口延迟、任务完成时间和失败率符合 SLO | 先恢复索引,再分析根因 |
什么时候可以进入删除评审
我通常不会在短暂低峰后立刻删除。候选索引至少要经历能覆盖主要业务形态的观察周期,例如工作日高峰、结算批次和定时报表。确认关键 SQL、长尾 SQL、索引提示、慢查询和业务指标都没有不可接受变化后,再把“删除索引”作为另一项独立变更评审。
删除前还要确认三件事:它不是主键或隐式主键;如果是唯一索引,业务确实不再依赖其约束;它没有被外键、运维脚本或灾备流程间接依赖。不可见阶段不会验证磁盘空间释放后的行为,也不会减少写入时的索引维护,因此删除后的容量与写入收益仍需单独观察。
常见误区与快速清单
- 误区:不可见等于停用。它只是默认不参与优化器计划,索引仍被维护。
- 误区:唯一索引不可见后不再校验重复。唯一性约束继续生效。
- 误区:一次 EXPLAIN 没变化就能删除。参数分布、连接顺序和低频任务都可能没有被覆盖。
- 误区:恢复 VISIBLE 后计划一定立刻回到原样。统计信息、数据分布和优化器选择仍会影响最终计划。
- 检查:保留完整回退语句。先恢复可见,再讨论是否需要其他修复。
相关问题
不可见索引会节省磁盘空间吗?
不会。索引结构仍然存在并随写入维护,只有真正删除后才可能释放相应空间。
可以把主键设为不可见吗?
不可以。显式主键不能设为不可见;在没有显式主键时,承担隐式主键作用的第一个 UNIQUE NOT NULL 索引也会受到限制。
怎样临时验证不可见索引仍能改善某条查询?
可以通过 SET_VAR 提示,只为单条语句临时开启 use_invisible_indexes,再把计划与默认关闭状态比较。
观察多久才够?
没有统一时长。周期应覆盖服务的主要流量形态、定时任务和低频报表,并以业务 SLO、语句摘要和慢查询证据为准。
不可见索引真正有价值的地方,是把“直接删除后祈祷没问题”改成“先制造一个可回退的无索引环境”。只要基线、证据和回退条件提前写清,它就是非常实用的上线前回归工具;如果这些准备缺失,它也只会让风险晚一点暴露。
-
374 收藏
-
499 收藏
-
384 收藏
-
184 收藏
-
265 收藏
-
480 收藏
-
222 收藏
-
160 收藏
-
337 收藏
-
420 收藏
-
153 收藏
-
313 收藏
-
351 收藏
-
112 收藏
-
127 收藏
-
291 收藏
-
500 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习