错误主因是MySQL 8.0.20+对JSON/TEXT字段启用“packed addons”,导致排序时整字段加载进sort_buffer_size;应优先用覆盖索引、精简SELECT字段,而非盲目调大缓冲区。

别急着改 sort_buffer_size——这个错误绝大多数时候不是内存不够,而是MySQL 8.0.20+在排序时把JSON/TEXT字段整个塞进内存导致的。
ORDER BY + JSON字段是罪魁祸首
MySQL 8.0.20起,对JSON、GEOMETRY字段默认启用“packed addons”机制:即使你只查10条记录,只要SELECT里包含JSON字段,且它参与了查询(哪怕没用于ORDER BY),MySQL就会尝试把整段JSON内容加载进排序缓冲区。单条几MB的JSON,直接压垮默认256KB的 sort_buffer_size。
- 典型触发SQL:
SELECT id, content_json, created_at FROM logs WHERE app_id=42 ORDER BY created_at DESC LIMIT 20 - 验证方法:去掉
content_json字段再执行,如果不再报错,基本锁定是大字段问题 - 注意:
WHERE条件匹配行数无关紧要——哪怕表里只有3条数据,只要JSON字段大,照样溢出
优先用覆盖索引绕过排序内存
让MySQL不用排序缓冲区,是最彻底的解法。核心是让 ORDER BY 字段和 LIMIT 所需字段都落在同一个索引里,避免filesort。
- 给排序字段建索引:
CREATE INDEX idx_created_at ON logs(created_at DESC) - 如果还要查其他字段(比如
id),建联合索引:CREATE INDEX idx_app_created_id ON logs(app_id, created_at DESC, id) - 关键点:索引顺序必须匹配查询模式——
WHERE字段在前,ORDER BY在中,SELECT中需要的非排序字段在最后(覆盖索引) - 执行
EXPLAIN确认type是range或ref,且Extra不含Using filesort
SELECT字段必须精简,尤其避开JSON/TEXT
这是见效最快的操作。很多分页接口根本不需要返回完整JSON,前端只展示摘要或ID即可。
- 错误写法:
SELECT *, created_at FROM logs ...——*会把所有字段(包括大JSON)拖进排序流程 - 正确写法:
SELECT id, title, created_at FROM logs ...—— 只取业务真正需要的字段 - 如果必须返回JSON内容,拆成两次查询:先查ID列表,再用
IN按需拉取详情(避免排序阶段加载大字段) - 临时应急可加
STRAIGHT_JOIN或强制使用索引,但治标不治本
真要调 sort_buffer_size?请看清它的坑
这个参数是每个连接独占的,盲目调大会引发连锁反应。
-
SET GLOBAL sort_buffer_size = 2097152(2MB)是相对安全的上限,超过4MB在高并发下极易OOM - Linux系统下,该值 > 2MB 时会触发
mmap()分配,反而降低性能 - 修改后必须断开重连才生效——当前连接仍用旧值,
SHOW VARIABLES LIKE 'sort_buffer_size'查的是会话级值 - 若已调到2MB仍报错,说明问题不在缓冲区大小,而是SQL或表结构设计本身有问题
最常被忽略的一点:MySQL 8.0.20+的排序行为变化是静默升级,旧SQL在新版本上可能突然崩溃。排查时永远先看SELECT列表里有没有JSON/TEXT字段,再看有没有覆盖索引,最后才碰配置参数。


















