MySQL 中的“双路排序”与“单路排序”:原理、判别与实战调优
一句话导读
ORDER BY
不能走索引时,MySQL 会在 Server 层做一次 filesort。内部实现分 单路(全字段) 与 双路(rowid) 两种;了解它们的触发条件、判别方法与调优思路,是 SQL 性能优化的必修课。
一、为什么会有 filesort?
-
当查询无法利用 覆盖索引 或 索引顺序 满足
ORDER BY
时,MySQL 需要把结果集读出来再排序。 -
这个排序逻辑统称 filesort,但它未必落盘,绝大多数情况下在内存完成。
二、单路 vs 双路:一张图看懂差异
二元组max_length_for_sort_data
TEXT/BLOB
三、内部流程拆解
-
单路排序
-
扫表/索引 → 把需要的 所有列 拷进 sort_buffer
-
在内存(或磁盘临时文件)里按排序键快排/归并
-
直接返回结果给客户端
-
-
双路排序
-
只取 排序键 + 聚簇主键(rowid) 进 sort buffer
-
排序后得到“排好序的 rowid 列表”
-
按 rowid 顺序回表 取其余列 → 返回
-
四、如何查看 MySQL 使用了哪一种?
MySQL 不直接写“单路/双路”字样,而是把信息藏在 optimizer trace 与 EXPLAIN FORMAT=json 里。
方法 1:EXPLAIN FORMAT=json(MySQL 8.0 推荐)
EXPLAIN FORMAT=jsonSELECT * FROM ordersWHERE order_date >= \'2025-01-01\'ORDER BY total_amount DESC LIMIT 20\\G
在输出里查找:
\"filesort_information\": [ { \"sort_mode\": \"\" <-- 双路 /* 或 \"\" */ <-- 单路 }]
-
→ 双路
-
或
→ 单路
方法 2:optimizer trace(所有版本通用)
-- 会话级开启SET optimizer_trace=\"enabled=on\";-- 执行目标 SQLSELECT ... ORDER BY ...;-- 查看 traceSELECT * FROM information_schema.optimizer_trace\\G
搜索关键字:
\"filesort_summary\": { \"sort_mode\": \"\"}
含义同上。
方法 3:慢查询日志 / performance_schema(线上无侵入)
-
MySQL 8.0.13+ 的 慢日志 JSON 会记录
\"sort_mode\"
字段。 -
performance_schema 表
events_statements_history_long
中:-
SUM_SORT_ROWS
累计排序行数 -
SUM_SORT_ROW_ID
> 0 可侧面反映双路排序
-
五、调优策略速查表
(order_col, ...)
,使 EXPLAIN
出现 Using index
SELECT *
;调大 max_length_for_sort_data
TEXT/BLOB
拆子表延迟加载示例调优:
-- 会话级只对当前连接生效SET sort_buffer_size = 4*1024*1024; -- 4 MBSET max_length_for_sort_data = 4096; -- 允许更长列走单路
六、实战案例
场景:订单宽表 orders
30+ 列,含 TEXT
备注字段。
-
初始 SQL:
SELECT * FROM ordersWHERE order_date >= \'2025-01-01\'ORDER BY total_amount DESCLIMIT 20;
EXPLAIN FORMAT=json
看到 \"sort_mode\": \"\"
,慢日志显示 Sort_row_id: 125000
。
→ 触发双路+大量回表,耗时 1.2 s。
-
优化:
-
去掉
*
只取需要的 5 列,列宽 < 3 KB -
新建复合索引
(order_date, total_amount DESC)
并 覆盖查询列
-
-
结果:
-
EXPLAIN
出现Using index; Using filesort
消失 -
查询降至 12 ms,CPU 降 90%。
-
七、结论
-
单路排序 用内存换 I/O,适合小字段;
-
双路排序 用 I/O 换内存,适合大字段;
-
通过
EXPLAIN FORMAT=json
或optimizer_trace
查看sort_mode
即可判定; -
真正的高性能优化是 让排序走索引,彻底告别 filesort。
一句话:看不到 Using filesort
,才是 ORDER BY
的终极答案。