详解MySQL排序:内存不够时会发生什么?

77 阅读4分钟

大家好,我是BiggerBoy。在上一篇文章咱们提到如果排序字段没有索引,MySQL需将全部数据加载到内存排序,深分页场景下直接“内存爆炸”。
但如果内存不够用呢?所以今天我们一起来探究一下。

当你在MySQL中执行一个包含 ORDER BY 的查询时,如果排序字段没有索引,且数据量过大导致内存不足,MySQL确实会使用文件排序(Filesort) 。这个过程就像“临时借硬盘当内存用”,虽然能完成任务,但效率会大打折扣。下面我们通过一个实际场景,拆解它的原理和影响。


1. 一个没有索引的排序查询会经历什么?

假设有一张用户表 users(100万条数据),执行以下查询:

SELECT * FROM users ORDER BY register_time DESC LIMIT 99999010;

如果 register_time 没有索引,MySQL的处理流程如下:

步骤1:全表扫描,加载数据

  • • MySQL需要扫描整个表,把所有满足条件的行(这里是全部100万行)的 register_time 和对应的主键 id 加载到内存。
  • • 如果内存不够,会分批次处理,但深分页场景下几乎必然触发文件排序。

步骤2:内存排序 vs 文件排序

内存排序:如果数据量小于 sort_buffer_size(默认256KB~1MB),直接在内存中排序。

文件排序:如果数据量太大,MySQL会将数据分成多个小块:

    1. 每次从磁盘读取一部分数据到内存排序;
    1. 将排序结果写入临时文件;
    1. 最终合并所有临时文件,得到完整排序结果。

步骤3:丢弃数据,返回结果

排序完成后,MySQL需要跳过前999990行,仅返回最后10行。但由于没有索引,它依然需要遍历整个排序结果。


2. 文件排序的性能有多差?

通过一个对比实验感受一下:

场景:100万行数据,register_time 无索引,执行 LIMIT 999990,10

测试结果

  • • 内存排序(小数据量):耗时约0.01秒;
  • • 文件排序(大数据量):耗时约15秒~30秒(取决于磁盘速度)。

性能杀手

  1. 1. 磁盘I/O:频繁读写临时文件,机械硬盘尤其明显;
  2. 2. CPU计算:多轮排序和合并;
  3. 3. 数据丢弃:即使最终只返回10行,仍需处理全部数据。

3. 延迟关联在无索引时还能用吗?

如果排序字段没有索引,延迟关联的优化效果会大打折扣。比如:

SELECT * FROM users  
INNER JOIN (  
  SELECT id FROM users  
  ORDER BY register_time DESC  -- 无索引,触发文件排序  
  LIMIT 99999010  
) AS tmp USING(id);  

虽然子查询只返回10个ID,但子查询内部的 ORDER BY 仍然需要全表扫描+文件排序,性能可能比传统分页更差(因为多了一次JOIN操作)。

结论
延迟关联生效的前提是排序字段有索引!否则,它只是“换汤不换药”。


4. 如何避免文件排序?

方案1:为排序字段加索引

  • • 如果按 register_time 排序,直接创建索引:

    ALTER TABLE users ADD INDEX idx_register_time(register_time);
    
  • • 如果查询中还有 WHERE 条件,需创建联合索引(如 (status, register_time))。

方案2:业务妥协

  • • 禁止深分页:只允许“上一页/下一页”,用游标分页(如记录最后一条的 register_time 和 id)。
  • • 分页限制:比如最多允许翻到第100页。

方案3:预计算或缓存

  • • 定时统计热门数据,将结果缓存到Redis;
  • • 使用Elasticsearch等搜索引擎优化复杂查询。

5. 文件排序的“救命”配置

如果实在无法加索引,可以调整MySQL参数缓解痛苦:

  1. 1. 增大 sort_buffer_size

    SET sort_buffer_size = 8*1024*1024;  -- 8MB
    

    让更多数据在内存中排序,减少临时文件数量。

  2. 2. 使用SSD:提升临时文件的读写速度。


总结

  • • 文件排序是深分页的“性能黑洞” ,尤其在排序字段无索引时;
  • • 延迟关联不是万能药,必须结合索引才能发挥威力;
  • • 终极解决方案:索引设计 + 业务妥协 + 合理分页策略。

互动讨论
你在项目中是否遇到过文件排序导致的性能问题?是如何解决的?欢迎留言分享你的实战经验~

往期推荐:#mysql系列文章

如果对你有用还请各位点赞、转发支持一下吧!

关注【BiggerBoy】公众号,持续获取更多技术干货!

图片