如何通过MySQL检测索引是否合理避免冗余并提高查询覆盖率?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1251个文字,预计阅读时间需要6分钟。
判断索引是否合理,第一步是查看它长什么样子。直接执行以下命令最快:
注意:CARDINALITY 是采样估算值,不一定准确;SEQ_IN_INDEX = 1 的列才是最左前缀起点;如果同一列多次出现在不同索引里且顺序一致,就埋下了冗余隐患。
常见误判点:
- 只看索引名(如
idx_user_id和idx_user_id_status)就认为后者“更全”,其实前者可能完全被后者覆盖 - 忽略
NON_UNIQUE字段——值为 0 表示唯一索引,影响查询优化器对等值查找的路径选择 - 没比对
COLLATION:若列为utf8mb4_0900_as_cs而索引排序规则是A,可能导致无法使用索引做范围扫描
用 EXPLAIN 验证查询是否真走索引、走了哪几列
EXPLAIN 不是“有没有用索引”的开关灯,而是告诉你优化器实际选了哪个索引、用了其中前几列、是否回表、是否用到索引排序。重点盯三个字段:key(实际使用的索引名)、key_len(用到的字节数)、Extra(比如 Using index 表示覆盖索引,Using filesort 表示丢了索引排序能力)。
实操建议:
- 对 WHERE + ORDER BY + LIMIT 组合语句,必须检查
key_len是否匹配最左前缀长度——例如索引是(a,b,c),而WHERE a=1 ORDER BY b可能只用上a,b,但WHERE a=1 ORDER BY c就会丢失排序能力,触发Using filesort - 联合索引中,跳过中间列(如
WHERE a=1 AND c=3)会导致只用上a,b,c彻底失效 - 字符串字段用
LIKE 'abc%'可走索引,但LIKE '%abc'或LIKE '%abc%'一定不走,除非是全文索引
识别冗余索引:用 sys.schema_redundant_indexes 视图快速定位
MySQL 5.7+ 自带 sys 库,其中 schema_redundant_indexes 视图能自动比对索引列前缀关系。它把一个索引列为“冗余”,当且仅当存在另一个索引,其前缀列完全包含该索引所有列(顺序一致、类型一致、长度一致)。
例如:idx_a_b(a,b)和 idx_a_b_c(a,b,c)共存时,idx_a_b 就被标记为冗余——因为任何能用 idx_a_b 的查询,idx_a_b_c 全都能更好支持。
但要注意边界情况:
- 唯一索引不能被非唯一索引覆盖(即使列前缀相同),
sys视图已排除这类误报 - 前缀索引(如
name(10))和全列索引(name)不算冗余,因覆盖能力不同 - 如果两个索引列顺序不同(
a,bvsb,a),不视为冗余,各自有适用场景
评估查询覆盖率:统计慢查询中未命中索引的 WHERE 条件组合
索引是否“合理”,最终得看线上真实查询。别只盯着单条 SQL 的 EXPLAIN,要聚合分析慢查询日志(slow_query_log)或 performance_schema.events_statements_summary_by_digest 中高频 WHERE 条件模式。
比如发现大量查询都带 WHERE status IN (1,2) AND created_at > '2024-01-01',但现有索引只有 (status) 或 (created_at),那就说明缺一个 (status, created_at) 联合索引。
容易被忽略的细节:
- 日期范围查询中,高选择性字段(如
status)放前面,低选择性字段(如created_at)放后面,才利于索引过滤;反过来建(created_at, status)可能导致前导列过滤效果差 - 如果查询常带
OR(如WHERE a=1 OR b=2),单个 B+ 树索引基本无效,得考虑改写为UNION或引入全文索引 - JSON 列上的虚拟列索引(
ALTER TABLE t ADD COLUMN j_name VARCHAR(100) AS (json_unquote(json_extract(data, '$.name'))))容易被漏掉,但它能让 JSON 字段查询真正走索引
冗余索引删起来容易,但补索引得看查询模式——没有慢日志或查询抽样,光靠表结构猜索引,大概率建偏。最危险的不是没索引,而是建了一堆互相干扰、还拖慢写入的“假有用”索引。
本文共计1251个文字,预计阅读时间需要6分钟。
判断索引是否合理,第一步是查看它长什么样子。直接执行以下命令最快:
注意:CARDINALITY 是采样估算值,不一定准确;SEQ_IN_INDEX = 1 的列才是最左前缀起点;如果同一列多次出现在不同索引里且顺序一致,就埋下了冗余隐患。
常见误判点:
- 只看索引名(如
idx_user_id和idx_user_id_status)就认为后者“更全”,其实前者可能完全被后者覆盖 - 忽略
NON_UNIQUE字段——值为 0 表示唯一索引,影响查询优化器对等值查找的路径选择 - 没比对
COLLATION:若列为utf8mb4_0900_as_cs而索引排序规则是A,可能导致无法使用索引做范围扫描
用 EXPLAIN 验证查询是否真走索引、走了哪几列
EXPLAIN 不是“有没有用索引”的开关灯,而是告诉你优化器实际选了哪个索引、用了其中前几列、是否回表、是否用到索引排序。重点盯三个字段:key(实际使用的索引名)、key_len(用到的字节数)、Extra(比如 Using index 表示覆盖索引,Using filesort 表示丢了索引排序能力)。
实操建议:
- 对 WHERE + ORDER BY + LIMIT 组合语句,必须检查
key_len是否匹配最左前缀长度——例如索引是(a,b,c),而WHERE a=1 ORDER BY b可能只用上a,b,但WHERE a=1 ORDER BY c就会丢失排序能力,触发Using filesort - 联合索引中,跳过中间列(如
WHERE a=1 AND c=3)会导致只用上a,b,c彻底失效 - 字符串字段用
LIKE 'abc%'可走索引,但LIKE '%abc'或LIKE '%abc%'一定不走,除非是全文索引
识别冗余索引:用 sys.schema_redundant_indexes 视图快速定位
MySQL 5.7+ 自带 sys 库,其中 schema_redundant_indexes 视图能自动比对索引列前缀关系。它把一个索引列为“冗余”,当且仅当存在另一个索引,其前缀列完全包含该索引所有列(顺序一致、类型一致、长度一致)。
例如:idx_a_b(a,b)和 idx_a_b_c(a,b,c)共存时,idx_a_b 就被标记为冗余——因为任何能用 idx_a_b 的查询,idx_a_b_c 全都能更好支持。
但要注意边界情况:
- 唯一索引不能被非唯一索引覆盖(即使列前缀相同),
sys视图已排除这类误报 - 前缀索引(如
name(10))和全列索引(name)不算冗余,因覆盖能力不同 - 如果两个索引列顺序不同(
a,bvsb,a),不视为冗余,各自有适用场景
评估查询覆盖率:统计慢查询中未命中索引的 WHERE 条件组合
索引是否“合理”,最终得看线上真实查询。别只盯着单条 SQL 的 EXPLAIN,要聚合分析慢查询日志(slow_query_log)或 performance_schema.events_statements_summary_by_digest 中高频 WHERE 条件模式。
比如发现大量查询都带 WHERE status IN (1,2) AND created_at > '2024-01-01',但现有索引只有 (status) 或 (created_at),那就说明缺一个 (status, created_at) 联合索引。
容易被忽略的细节:
- 日期范围查询中,高选择性字段(如
status)放前面,低选择性字段(如created_at)放后面,才利于索引过滤;反过来建(created_at, status)可能导致前导列过滤效果差 - 如果查询常带
OR(如WHERE a=1 OR b=2),单个 B+ 树索引基本无效,得考虑改写为UNION或引入全文索引 - JSON 列上的虚拟列索引(
ALTER TABLE t ADD COLUMN j_name VARCHAR(100) AS (json_unquote(json_extract(data, '$.name'))))容易被漏掉,但它能让 JSON 字段查询真正走索引
冗余索引删起来容易,但补索引得看查询模式——没有慢日志或查询抽样,光靠表结构猜索引,大概率建偏。最危险的不是没索引,而是建了一堆互相干扰、还拖慢写入的“假有用”索引。

