MySQL 5.7未触发的CASE ELSE分支CHAR长度导致排序内存溢出咨询
问题原因解析
核心原因是MySQL 5.7的查询优化器在预分配排序内存时,只计算表达式的声明最大长度,不考虑运行时分支是否会实际执行,具体逻辑如下:
- 执行包含GROUP BY、ORDER BY的SQL前,优化器会先预估所有参与排序/分组的字段需要的内存空间,内存总预估值超过
sort_buffer_size参数配置的阈值时,就会抛出1038内存溢出错误。 - CASE表达式的返回类型长度由所有分支的返回值最大长度决定,就算ELSE分支永远不会触发,优化器也会把ELSE分支里
CAST声明的CHAR长度作为整个CASE表达式的最大返回长度参与内存计算。 - 你原来的CASE表达式声明最大长度为255字符,配合UTF8字符集每个字符最多占3字节,单个CASE表达式的最大预估字节长度为765字节,加上GROUP BY里同时用到了原CASE表达式和它的BINARY转换结果,单个分组键的预估内存就超过1500字节,当表数据量较大时,总内存预估值很容易超过
sort_buffer_size的默认配置。 - 修改为
CAST(app_name AS CHAR(25))后,整个CASE表达式的最大预估长度直接降到原来的1/10左右,总内存预估值随之下降到阈值以内,因此SQL可以正常执行。
补充优化建议
- 若确认ELSE分支永远不会触发,可以直接删除ELSE分支(MySQL中CASE表达式无匹配项时默认返回NULL),或者将ELSE分支改为
ELSE '',也能达到相同的内存优化效果。 - 也可通过
SHOW VARIABLES LIKE 'sort_buffer_size'查看当前排序缓存配置,适当调大该参数(注意每个连接会独立占用自己的sort_buffer,不建议配置超过2M,避免并发高时占用过多整机内存)。
内容的提问来源于stack exchange,提问作者user2894829
相关产品推荐
相关产品推荐

