必须立刻干预,优先减少排序需求:确认是否真需order by、检查索引匹配性、避免函数排序;其次调大tmp_table_size/max_heap_table_size(会话级);禁用order by rand(),改用id范围查询或应用层随机。

MySQL排序用到Using filesort且内存不足怎么办
当EXPLAIN显示Extra列含Using filesort,同时慢查询日志里出现Sort_merge_passes飙升或磁盘临时文件(/tmp/#sql_*.MYD)暴涨,基本能确定是排序挤爆了sort_buffer_size——不是“该不该优化”,而是“必须立刻干预”,否则并发一高就卡死。
别急着调大sort_buffer_size。这个参数是**每个连接独占**的,设成 4MB 后 100 个连接就吃掉 400MB 内存,可能直接触发 OOM。优先做减法:
- 确认是否真需要
ORDER BY:分页场景下,LIMIT 10000,20这种偏移量大的排序,99% 的数据都白排了,改用游标分页(WHERE id > ? ORDER BY id LIMIT 20) - 检查
ORDER BY字段是否有索引:联合索引要严格匹配最左前缀,比如INDEX (a,b,c)能加速ORDER BY a,b,但对ORDER BY b,c无效 - 避免在
ORDER BY里用函数或表达式:ORDER BY UPPER(name)会强制 filesort,哪怕name有索引
临时表走磁盘而不是内存?看tmp_table_size和max_heap_table_size
MySQL 用内存临时表(MEMORY引擎)存中间结果,但一旦结果集超过tmp_table_size或max_heap_table_size中**较小的那个值**,就会自动转成磁盘临时表(MyISAM或InnoDB),性能断崖下跌。
这两个值默认通常只有 16MB,而一个带GROUP BY + ORDER BY的查询很容易突破——尤其字段含VARCHAR(500)或TEXT时,内存估算会更激进。
- 查当前值:
SHOW VARIABLES LIKE 'tmp_table_size';和SHOW VARIABLES LIKE 'max_heap_table_size'; - 临时调高(仅会话级,安全):
SET SESSION tmp_table_size = 64*1024*1024;(64MB) - 真正有效的做法是让查询本身不生成大临时表:比如把
SELECT *改成只取必要字段,GROUP BY前先用子查询过滤掉 90% 的行
CREATE TEMPORARY TABLE ... ENGINE=MEMORY不是万能解药
有人想绕过优化器,手动建内存表存中间结果再排序。这思路没错,但ENGINE=MEMORY表有硬伤:不支持TEXT/BLOB字段,且所有字符串字段按最大长度预分配内存(VARCHAR(1000)当 1000 字节算),极易触发The table is full错误。
更隐蔽的问题是,MEMORY表的索引是哈希(默认)或 B-Tree(需显式声明USING BTREE),但哈希索引不支持ORDER BY——你建了索引也白搭,照样 filesort。
- 如果坚持用内存表,必须加
USING BTREE:CREATE TEMPORARY TABLE t1 (id INT, name VARCHAR(100)) ENGINE=MEMORY KEY_BLOCK_SIZE=8; - 但更推荐放弃手动建表,改用
CTE(MySQL 8.0+)或物化子查询,让优化器自己决定走内存还是磁盘 - 注意:
MEMORY表数据全在内存,实例重启就丢,别存关键中间态
为什么ORDER BY RAND()必崩
这个写法看似简单,实际是 MySQL 排序类问题的“核弹”:它要求对**全表逐行计算随机数**,然后排序取前 N 条。没有索引可走,sort_buffer根本不够用,必然落盘,且Sort_merge_passes指数级上涨。
- 替代方案只有两种:
SELECT * FROM t WHERE id >= FLOOR(RAND() * (SELECT MAX(id) FROM t)) LIMIT 1(适合主键连续) - 或者预生成随机 ID 列表(应用层),用
WHERE id IN (?, ?, ?)批量查——避免任何排序 - 切记:
RAND()在ORDER BY里永远触发全表扫描+全内存排序,无论表大小
排序瓶颈从来不在“要不要用内存”,而在“能不能不排”。索引覆盖、减少结果集、拆分逻辑——这些比调参管用十倍。真正难的不是找到sort_buffer_size该设多少,而是看出哪一行ORDER BY其实根本没必要存在。











