MySQL 8.0排序机制变更致慢查询:废弃max_length_for_sort_data,统一单次归并排序,SELECT *易触发落盘;需调大sort_buffer_size、建覆盖索引、启用log_queries_not_using_indexes定位问题。

MySQL 5.7 升级到 8.0 后出现慢查询,大概率不是“SQL写错了”,而是排序行为、索引使用逻辑、参数语义发生了隐性变化——尤其当 ORDER BY 字段没索引、SELECT * 频繁出现时,性能可能从 1 秒退化到 4 秒以上。
为什么 ORDER BY 查询在 MySQL 8.0 变慢了
核心原因是 MySQL 8.0.20+ 彻底废弃了 max_length_for_sort_data 参数,而 5.7 依赖它控制排序方式:字段总大小 ≤ 该值时走“全字段排序”(内存快但占内存),否则退化为“rowid 排序”(IO 多但稳定)。8.0 统一采用更激进的“单次归并排序”,一旦 SELECT * 导致排序缓冲区(sort_buffer_size)不足,就会频繁落盘,耗时飙升。
- 验证方法:执行
EXPLAIN FORMAT=JSON查看sort_mode字段,若出现<sort_key additional_fields></sort_key>说明走了全字段排序;若为<sort_key rowid></sort_key>则是回表排序(5.7 常见,8.0 已不适用) - 典型现象:5.7 下
SELECT id,name FROM t ORDER BY created_at LIMIT 10和SELECT * FROM t ORDER BY created_at LIMIT 10耗时接近;8.0 下后者可能慢 3–4 倍 - 临时缓解:调大
sort_buffer_size(如从 256K → 2M),但治标不治本,且全局调大会挤占其他连接内存
如何快速定位迁移后新增的慢查询
别只盯 long_query_time,8.0 的慢日志默认不记录未用索引的查询,而很多性能退化恰恰源于“本该走索引却没走”。必须打开两个开关:
MySQL 数据库管理技能,能够通过自然语言查询和管理数据库,支持 SELECT/INSERT/UPDATE/DELETE、表管理、备份恢复等操作。当用户提及 MySQL、数据库查询、建表或数据备份时使用此技能。
- 动态启用:
SET GLOBAL log_queries_not_using_indexes = ON+SET GLOBAL min_examined_row_limit = 100(避免记录小表扫描干扰) - 检查是否漏掉隐式类型转换:
EXPLAIN看type是否为ALL或index,再查key是否为空;常见于 5.7 允许的字符串/数字隐式转换(如WHERE user_id = '123'),8.0 优化器更严格,可能直接放弃索引 - 对比升级前后
SHOW PROFILES输出,重点关注Sorting result和executing阶段耗时占比变化——如果前者占比骤增,基本锁定排序机制变更问题
哪些索引策略在 8.0 必须调整
8.0 的优化器对联合索引的“最左前缀匹配”更苛刻,且引入了隐藏主键(rowid)参与排序的新逻辑,旧索引可能失效:
- 原 5.7 有效索引
INDEX idx_status_time(status, created_at),若查询是WHERE status IN ('A','B') ORDER BY created_at,8.0 可能无法利用created_at部分做排序,需改为覆盖索引:INDEX idx_status_time_cover(status, created_at, id, name) - 涉及
GROUP BY或DISTINCT的查询,8.0 更倾向使用松散索引扫描(Loose Index Scan),要求索引顺序严格匹配GROUP BY字段顺序,否则退化为临时表 + filesort - 删除所有以
query_cache_开头的配置项——MySQL 8.0 已移除查询缓存,残留配置虽不报错,但会误导运维判断缓存命中率
最容易被忽略的兼容性陷阱
很多慢查询不是 SQL 本身的问题,而是客户端或驱动层与 8.0 新协议/认证机制的摩擦点:
- 老版本
mysqlclient(如 1.4.x)连接 8.0 时,默认使用caching_sha2_password认证,握手阶段就超时;需显式指定--default-auth=mysql_native_password或重置用户密码:ALTER USER 'user'@'%' IDENTIFIED WITH mysql_native_password BY 'pwd'; - MyBatis 中若用了
useSSL=false&serverTimezone=UTC这类 5.7 风格 URL 参数,8.0 驱动会静默忽略,导致时区解析错误,间接引发ORDER BY time_col排序结果错乱、优化器误判 -
TRUNCATE语句突然变慢?不是磁盘问题,是 8.0 改用row_drop_table_for_mysql流程清理自适应哈希索引(AHI),大表 AHI 结构复杂时单次耗时可达数百毫秒——这不是 bug,是设计使然,监控时需过滤掉这类“伪慢查询”
迁移后的性能问题,往往藏在“看起来没变”的地方:一个没索引的 ORDER BY 字段、一行残留的 query_cache_size 配置、驱动里一个未更新的认证参数。与其花时间调优单条 SQL,不如先确认排序机制、索引覆盖度、客户端协议这三层有没有被新版本悄悄改写。


















