MySQL 不可见索引适合怎样做上线前回归验证
不可见索引最适合用在“准备删索引,但还没有足够证据”的阶段。它让 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、语句摘要和慢查询证据为准。
不可见索引真正有价值的地方,是把“直接删除后祈祷没问题”改成“先制造一个可回退的无索引环境”。只要基线、证据和回退条件提前写清,它就是非常实用的上线前回归工具;如果这些准备缺失,它也只会让风险晚一点暴露。
Go 泛型方法为什么无法声明自己的额外类型参数
- 上一篇
- Go 泛型方法为什么无法声明自己的额外类型参数
- 下一篇
- Redis 客户端缓存如何用广播模式减少失效消息
-
- 数据库 · MySQL | 3小时前 |
- Optimizer Trace 适合解决哪些执行计划疑问
- 480浏览 收藏
-
- 数据库 · MySQL | 8小时前 | MySQL · InnoDB · 批量插入 MySQL 批量写入 InnoDB事务 事务大小 回滚成本
- 批量写入如何兼顾吞吐与回滚成本:事务大小实测方法
- 160浏览 收藏
-
- 数据库 · MySQL | 14小时前 |
- GTID 复制切换前要检查什么:一致性与故障回退清单
- 153浏览 收藏
-
- 数据库 · MySQL | 1天前 |
- 间隙锁何时出现:用范围更新解释幻读保护边界
- 313浏览 收藏
-
- 数据库 · MySQL | 1天前 |
- 死锁日志怎么看:还原事务交叉加锁的最短路径
- 351浏览 收藏
-
- 数据库 · MySQL | 1天前 | MySQL教程 · ROW_NUMBER MySQL窗口函数 DENSE_RANK 分组排名 每组TopN
- 窗口函数做分组排名时,ROW_NUMBER 与 DENSE_RANK 怎么选
- 112浏览 收藏
-
- 数据库 · MySQL | 1天前 | MySQL · 数据库 · WITH RECURSIVE MySQL 递归 CTE 组织树 层级查询 循环防护
- 递归 CTE 生成组织树:终止条件、层级与循环防护
- 127浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 383次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 454次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 467次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 406次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 237次使用
-
- MySQL 明明加了索引,为什么查询还是很慢?先查这 6 个点
- 2026-06-27 374浏览
-
- golang MySQL实现对数据库表存储获取操作示例
- 2022-12-22 499浏览
-
- golang 基于 mysql 简单实现分布式读写锁
- 2023-01-07 384浏览
-
- 详解如何利用GORM实现MySQL事务
- 2023-01-07 184浏览
-
- Go语言实现操作MySQL的基础知识总结
- 2023-01-23 265浏览

