MySQL sys.schema_unused_indexes 的结果为什么不能直接删索引
来源:17golang原创
时间:2026-09-14 23:57:36 327浏览 收藏
看到 sys.schema_unused_indexes 里出现一个索引,不能马上执行 DROP INDEX。这个视图表达的是:在当前 MySQL 实例、当前 Performance Schema 统计周期内,没有记录到该索引的 I/O 事件。它不是业务代码的“永久不用证明”。实例刚重启、报表只在月底运行、统计信息被清零,都会让结果暂时失真。
schema_unused_indexes是候选清单,先看观察窗口是否覆盖峰值、批处理和低频任务。- 索引可能承担唯一性、外键、排序或备用查询路径,不能只凭“没有事件”判断可删。
- 先用 invisible index 做可恢复停用,再比较
EXPLAIN、慢查询和业务指标。 - 真正删除前保留
SHOW CREATE TABLE里的索引定义,并安排可重建方案。

先把“未使用”还原成统计数据的快照
这个视图只有三个关键字段:库名、表名和索引名。它的上游是 performance_schema.table_io_waits_summary_by_index_usage,按表索引汇总 I/O 等待;其中 PRIMARY 表示使用主索引,NULL 表示没有使用索引,插入操作也会计入 INDEX_NAME = NULL。因此,视图回答的是“有没有被监测到事件”,不是“应用层是否设计上需要它”。
-- 先查看候选索引,并限定到目标业务库,避免误读系统库
SELECT object_schema, object_name, index_name
FROM sys.schema_unused_indexes
WHERE object_schema = 'shop'
ORDER BY object_name, index_name;
-- 同时记录统计来源;Performance Schema 数据属于当前实例,重启后不会保留
SELECT OBJECT_SCHEMA, OBJECT_NAME, INDEX_NAME,
COUNT_STAR, COUNT_READ, COUNT_WRITE
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE OBJECT_SCHEMA = 'shop'
AND OBJECT_NAME = 'orders';
MySQL 官方特别提醒:服务器必须运行足够久,而且处理的流量要能代表真实工作负载,否则出现在这个视图里的索引不一定有意义。维护前先记下实例启动时间、最近一次统计清零时间、DDL 变更时间,并确认观察窗口包含白天高峰、夜间任务、月末报表和故障补偿任务。若刚执行过 TRUNCATE TABLE performance_schema.table_io_waits_summary_by_table,之前的观察不能直接拿来做删除结论。
从表定义和真实 SQL 查出索引的隐藏职责
候选索引还要经过三层交叉检查。第一层是表定义:看它是否是唯一索引、外键相关索引,是否被某些排序或连接条件使用。第二层是应用代码、定时任务和运维脚本:低频导出、租户切换、数据修复往往不会出现在日常流量里。第三层是执行计划:对照核心 SQL 的 possible_keys、key、rows 和 Extra,确认优化器是否曾经把它作为可行路径。
-- 保存定义和可见性,后续可据此写回滚脚本 SHOW CREATE TABLE shop.orders; SHOW INDEX FROM shop.orders; -- 用真实条件观察优化器当前选择;示例 SQL 需替换成业务语句 EXPLAIN SELECT order_id, customer_id, created_at FROM shop.orders WHERE customer_id = 2088 AND status = 'paid' ORDER BY created_at DESC LIMIT 50;
| 观察结果 | 说明 | 下一步 |
|---|---|---|
| 长时间无事件且无业务依赖 | 更像冗余候选,但仍需灰度停用 | 进入 invisible index 试验 |
| 只在低频窗口使用 | 日常统计不足以代表全年负载 | 延长观察,补看任务和报表 |
| 承担唯一性或约束语义 | 性能视图不能覆盖完整数据语义 | 保留,另行评估结构变更 |
| EXPLAIN 计划会选择它 | 候选清单与当前计划出现冲突 | 先查统计清零、计划样本和版本差异 |
真正决定删除的是停用验证,不是候选清单
对普通二级索引,MySQL 支持把索引设为 INVISIBLE。默认情况下优化器不会使用它,但索引还在,恢复为 VISIBLE 比重新创建更容易。这个阶段要在副本、灰度实例或明确的低风险窗口进行,并为核心查询保留计划对比。
-- 先让优化器忽略索引,停用前确认它不是 PRIMARY KEY ALTER TABLE shop.orders ALTER INDEX idx_customer_status INVISIBLE; -- 逐条对比关键查询的计划;只对本次语句临时启用 invisible index EXPLAIN SELECT /*+ SET_VAR(optimizer_switch = 'use_invisible_indexes=on') */ order_id, customer_id, created_at FROM shop.orders WHERE customer_id = 2088 AND status = 'paid' ORDER BY created_at DESC LIMIT 50; -- 发现慢查询、错误或计划恶化时立即恢复 ALTER TABLE shop.orders ALTER INDEX idx_customer_status VISIBLE;
停用后观察的不只是单条 EXPLAIN:还要看慢查询数量、关键接口延迟、批处理耗时、锁等待和错误日志。官方文档列出的可见信号包括执行计划改变、原本不慢的查询进入慢查询日志,以及 Performance Schema 中受影响工作负载增加。若索引被 hint 明确引用,设为 invisible 还可能直接暴露错误,这正是提前灰度的价值。

满足删除条件后,保留一条可回滚的结构变更记录
只有当观察窗口覆盖真实任务、停用期间没有计划和业务指标恶化、索引也不承担约束语义时,才进入删除。删除本身会改变表结构,InnoDB 在线 DDL 是否适合当前表,还要结合表大小、并发写入、锁策略和发布窗口判断。
-- 删除前再次确认索引名,避免把相似名称写错 SHOW INDEX FROM shop.orders WHERE Key_name = 'idx_customer_status'; -- 在已审批的变更窗口执行;具体 ALGORITHM/LOCK 需按环境评估 DROP INDEX idx_customer_status ON shop.orders; -- 回滚脚本示例:保留原始列顺序和索引定义,禁止临时猜列 CREATE INDEX idx_customer_status ON shop.orders (customer_id, status);
最终记录至少包括:候选查询时间、实例运行和统计周期、索引原始定义、覆盖过的业务任务、停用开始与结束时间、关键 SQL 计划差异、指标结论以及重建 DDL。这样以后即使业务新增查询,也能知道这次删除基于什么证据,而不是把一行视图结果当成永久事实。
常见问题
视图里没有索引事件,是不是索引一定没用?
不是。它只代表当前实例和统计周期没有观测到事件,低频任务、重启或清零都可能造成空结果。
把索引设为 invisible 会立即释放磁盘吗?
不会。invisible 主要改变优化器是否考虑该索引,索引结构仍然存在;要释放空间需要经过正式删除和相应的表空间处理。
为什么 EXPLAIN 还会显示这个索引?
如果使用了 use_invisible_indexes=on 的会话设置或提示,优化器可以把 invisible index 纳入计划构造;排查时要记录会话级设置。
可以直接用 DROP INDEX 试错吗?
不建议。先 invisible 灰度并保留原始建索引语句,确认真实业务没有退化后再做结构变更。
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
432 收藏
-
284 收藏
-
199 收藏
-
数据库 · MySQL | 5小时前 | MySQL · 数据类型 · JSON · SQL排错 · MEMBER OF · MySQL MEMBER OF MySQL JSON 数组成员判断 MEMBER OF 类型不匹配 JSON 数字字符串区别 MySQL JSON 查询167 收藏
-
数据库 · MySQL | 7小时前 | MySQL · 数据库查询 · JSON 函数 · SQL 边界 · JSON 数组 · JSON_CONTAINS MySQL JSON_OVERLAPS JSON 数组相交 MySQL JSON 类型比较 MySQL NULL 边界130 收藏
-
418 收藏
-
410 收藏
-
452 收藏
-
425 收藏
-
488 收藏
-
232 收藏
-
数据库 · MySQL | 15小时前 | SQL查询 · group by · MySQL教程 · mysql group by ONLY_FULL_GROUP_BY 函数依赖 ERROR 1055440 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习