EXPLAIN进阶:读懂key_len和filtered
关键词:EXPLAIN;key_len;filtered;执行计划;联合索引;SQL优化
大家好,我是小耶,写功课只是为了我踩过的坑,你们别再踩了!
做SQL优化就像体检。你拿到一份体检报告(EXPLAIN的输出),大部分人只盯着“红细胞”(type列)和“白细胞”(Extra列)有没有超标,却忽略了“关键蛋白”(key_len)和“炎症因子”(filtered)。这两个指标恰好能告诉你:联合索引到底用了几列?索引用得有多好?
key_len和filtered是什么?
用快递分拣系统来类比:
- key_len:相当于你扫描条形码的长度。条形码越长,包含的信息越多(比如省、市、区、街道、门牌号)。key_len越大,说明联合索引中实际使用的列越多,查询定位越精准。
- filtered:相当于分拣员根据条形码初步分拣后,剩下的包裹中还需要人工二次分拣的比例。filtered越高(越接近100%),说明索引定位已经很准确,不需要额外过滤;filtered越低,说明索引只帮你筛掉了一小部分,还要花大量时间在回表后过滤剩下的数据。
一、key_len的计算方法
MySQL中各数据类型的字节长度如下表:
| 数据类型 | 字节长度 | 备注 |
|---|---|---|
| TINYINT | 1 | |
| SMALLINT | 2 | |
| INT | 4 | |
| BIGINT | 8 | |
| DATE | 3 | |
| TIMESTAMP | 4 | |
| DATETIME | 5 | MySQL 5.6+ |
| CHAR(n) | n × 字符集字节数 | utf8mb4为4字节/字符 |
| VARCHAR(n) | n × 字符集字节数 + 1~2 | 长度标识 |
| 允许NULL | 额外+1 |
示例计算
假设联合索引 (a, b, c):
- a:INT NOT NULL → 4字节
- b:INT允许NULL → 4 + 1 = 5字节
- c:VARCHAR(10) utf8mb4 NOT NULL → 10×4 + 2 = 42字节
| 使用列 | key_len |
|---|---|
| 只用a | 4 |
| a+b | 4+5=9 |
| a+b+c | 4+5+42=51 |
实战案例
sql
CREATE TABLE user_log (
id INT PRIMARY KEY,
user_id INT NOT NULL,
log_date DATE NOT NULL,
log_type TINYINT NOT NULL,
msg VARCHAR(255),
INDEX idx_union (user_id, log_date, log_type)
);
| 查询条件 | key_len | 说明 |
|---|---|---|
user_id = 10086 AND log_date = '2026-06-01' |
4+3=7 | 用到前两列 |
user_id = 10086 AND log_type = 1 |
4 | 跳过了log_date,只能用到第一列(最左前缀原则) |
二、filtered的解读
filtered表示存储引擎返回的行中,满足剩余WHERE条件的比例(估算值)。
| filtered值 | 含义 |
|---|---|
| 100% | 索引精准定位,无需额外过滤 |
| 30% | 索引定位后还要过滤掉70%的行,回表开销大 |
| 5% | 索引选择性很差,几乎没用 |
- 在单表查询中:filtered帮助判断索引设计是否合理。如果联合索引全用到但filtered仍然很低,说明索引列的选择性差(比如status只有几个值)。
- 在多表JOIN中:优化器会估算“驱动表行数 × filtered”作为被驱动表的匹配次数。filtered低可能导致优化器选择错误的驱动顺序。
三、key_len + filtered 组合分析矩阵
| key_len | filtered | 诊断 | 优化建议 |
|---|---|---|---|
| 大(多列) | 高(>90%) | 索引设计优秀 | 无需调整 |
| 小(单列) | 中低 | 查询条件未覆盖索引前列 | 调整联合索引列顺序或改写SQL |
| 大(多列) | 低 | 索引列选择性差 | 换用更高选择性的列,或使用覆盖索引 |
| 小(单列) | 高 | 单列索引选择性好 | 可考虑扩展为联合索引,避免回表 |
四、真实优化案例
原SQL:
sql
SELECT * FROM orders WHERE create_time > '2026-05-01' AND status = 'PAID';
原索引: (create_time, status)
EXPLAIN结果: type=range,key_len=5(create_time为DATE),filtered=10%。
分析: 只用了create_time索引,status过滤在回表后执行。filtered=10%意味着扫描行中90%被过滤掉,回表开销大。
优化方案: 将索引顺序改为 (status, create_time)。因为status选择性虽然不高,但作为前导列可以快速定位到PAID行,再通过create_time范围扫描。
优化后: key_len = status + create_time,filtered提升到100%,查询时间从3秒降到0.2秒。
五、注意事项
- key_len不是越大越好:如果用了低选择性列,反而可能扫描更多行。
- filtered是估算值:依赖统计信息。如果统计信息过旧,执行
ANALYZE TABLE更新。 - 版本限制:MySQL 5.6及以下版本没有filtered列。
- JOIN中的重要性:驱动表的filtered值直接影响被驱动表的访问次数。
六、价值总结
学会解读key_len和filtered,你就能从“大概知道用了索引”升级到“精确知道索引怎么用的、哪里需要优化”。配合 ANALYZE TABLE 更新统计信息,让优化器做出更准确的决策,是DBA走向高级优化的必经之路。
小耶在手,SQL 不愁
还有什么想了解的,欢迎留言!小耶一定知无不言言无不尽……我们下次见~
参考文献
- MySQL官方文档:《EXPLAIN Output Format》
- 《高性能MySQL》第4版,第9章:查询优化
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)