sql排序慢的根源是内存不足触发外部排序。当数据超出sort_buffer_size或work_mem时,数据库从内存排序降级为磁盘归并排序,性能断崖式下降;优化关键在于控制排序数据量、加索引、合理调参和拆解复杂排序。

SQL排序慢,往往不是因为SQL写得不够“漂亮”,而是底层排序机制在数据量超过内存阈值时,自动从高效内存排序切换为低效外部排序——这才是性能断崖的真正分水岭。
内存排序(In-Memory Sort):快的关键在于“全在内存里”
当排序所需空间 ≤ sort_buffer_size(MySQL)或 work_mem(PostgreSQL),数据库直接在内存中完成快速排序(通常是堆排或快排),无磁盘IO,响应极快。
- MySQL默认 sort_buffer_size 通常仅256KB~2MB,小到连1万行整型主键都可能撑爆
- PostgreSQL的 work_mem 默认常为4MB,但它是每个操作(如一个ORDER BY、一个JOIN)独立分配,非全局共享
- 若查询含多个排序(如子查询+外层ORDER BY),会多次申请 work_mem,极易触发落盘
外部排序(External Sort):慢的根源是“被迫读写磁盘”
一旦排序数据超出内存缓冲区,数据库必须拆分数据块→分别排序→写入临时文件→归并读取,形成多轮磁盘随机IO与合并操作,性能呈指数级下降。
- MySQL会生成 #sql-*.MYD 临时文件(MyISAM引擎)或使用 InnoDB 临时表空间
- PostgreSQL生成 pgsql_tmp/ 下的临时文件,归并阶段频繁 seek + read,IOPS压力陡增
- 观察执行计划:出现 Using filesort(MySQL)或 Sort Method: external merge(PG)即已落入外部排序
如何判断当前排序是否“已溢出”?
别只看执行时间,要抓真实证据:
- MySQL:开启 optimizer_trace,查 trace 中
filesort_summary的rows与examined_rows;或用 SHOW PROFILE FOR QUERY N 查Sorting result阶段耗时占比 - PostgreSQL:EXPLAIN (ANALYZE, BUFFERS) 中关注
Sort Method字段,若显示external merge或external quicksort,且Write/Read行数 > 0,说明已落盘 - 通用方法:监控临时目录磁盘写入量(如
/tmp、pg_default表空间、MySQL tmpdir)突增,大概率正在做外部排序
提速核心思路:让排序尽量留在内存里
不是盲目调大内存参数,而是精准控制“参与排序的数据量”:
- 加索引:对 ORDER BY 字段建联合索引(尤其含 WHERE 条件时),让优化器走 index scan + index order,彻底规避排序
- 减少排序行数:用 WHERE 先过滤,避免
SELECT * FROM t ORDER BY x LIMIT 10对千万行全表排序 - 合理调参:MySQL 可为高并发短查询设较小 sort_buffer_size,为少数关键报表查询通过
SET SESSION sort_buffer_size = 64M临时提升 - 拆解复杂排序:含多层嵌套、窗口函数、GROUP BY + ORDER BY 的语句,考虑物化中间结果(如用 CREATE TEMP TABLE)分步处理










