登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  数据库 >  MySQL

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 索引同样不能直接隐藏。

业务查询、MySQL 优化器、可见索引与不可见索引的静态关系说明图
图1:不可见索引的静态边界说明图。优化器默认忽略它,但索引维护与约束并未消失。

这也是我不会把不可见索引当成存储优化结果的原因。观察期内,写入仍然承担维护成本;它验证的是“查询能不能离开这个索引”,不是“删掉后能立刻省下多少空间和写放大”。

回归前先做一份最小基线

第一次做这类验证时,最容易犯的错是先改索引,再临时寻找受影响 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 很容易得到过于乐观的结论。我会把证据拆成四组,并要求它们都能回到变更前的基线。

基线 SQL、执行计划、Performance Schema、慢查询日志和回退阈值的结构说明图
图2:回归证据结构图。不要靠一次 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、语句摘要和慢查询证据为准。

不可见索引真正有价值的地方,是把“直接删除后祈祷没问题”改成“先制造一个可回退的无索引环境”。只要基线、证据和回退条件提前写清,它就是非常实用的上线前回归工具;如果这些准备缺失,它也只会让风险晚一点暴露。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>