深入分析mysql中group by、order by的工作原理

知其然要知其所以然,探索每一个知识点背后的意义,你知道的越多,你不知道的越多,一起学习,一起进步,如果文章感觉对您有用的话,关注、收藏、点赞,有困惑的地方请评论,我们一起交流!


一、GROUP BY 的工作原理

GROUP BY 用于按指定列对数据进行分组,通常结合聚合函数(如 SUMCOUNTMAX 等)使用。其核心机制如下:

1. 执行流程
  • 无索引时的处理

    1. 全表扫描:读取所有满足条件的行。
    2. 构建临时表 :将数据按 GROUP BY 列分组,存储分组键和中间聚合结果。
    3. 排序与聚合
      • 默认按分组字段隐式排序(类似 ORDER BY)。
      • 若未显式关闭排序(ORDER BY NULL),可能触发文件排序(Using filesort)。
    4. 返回结果:遍历临时表输出分组后的数据。
  • 有索引时的优化

    • 松散索引扫描(Loose Index Scan)
      • GROUP BY 列是索引的最左前缀且无范围查询时,直接跳过索引中的重复值,仅读取每组的第一行。
      • 示例:索引 (a, b),查询 GROUP BY a
    • 紧凑索引扫描(Tight Index Scan)
      • 按索引顺序逐行读取数据,隐式利用索引有序性完成分组。
      • 示例:索引 (a, b),查询 GROUP BY a, b
2. 性能优化策略
  • 索引设计

    • 创建联合索引,确保 GROUP BY 列是索引的最左前缀。
    • 使用覆盖索引(包含所有查询字段)避免回表。
    • 示例:ALTER TABLE orders ADD INDEX idx_user_status (user_id, status);
  • 避免隐式排序

    sql 复制代码
    SELECT category, COUNT(*) FROM products 
    GROUP BY category ORDER BY NULL; -- 禁用默认排序
  • 监控临时表

    sql 复制代码
    SHOW STATUS LIKE 'Created_tmp%'; -- 查看临时表使用情况

二、ORDER BY 的工作原理

ORDER BY 用于对结果集排序,其执行效率高度依赖索引和内存管理。

1. 执行流程
  • 无索引时的处理

    1. 全表扫描:读取所有满足条件的行。
    2. 排序缓冲区(sort_buffer)
      • 若数据量小于 sort_buffer_size,在内存中排序(单路排序)。
      • 若数据量过大,使用磁盘临时文件进行多路归并排序(双路排序)。
    3. 返回结果:按排序后的顺序输出数据。
  • 有索引时的优化

    • 若排序字段与索引顺序一致,直接按索引顺序读取数据(避免排序)。
    • 支持正向或反向扫描索引(MySQL 8.0+ 支持降序索引)。
2. 性能优化策略
  • 索引设计

    • 排序字段与索引顺序一致(包括升降序)。
    • 示例:索引 (created_at DESC),查询 ORDER BY created_at DESC
  • 调整排序缓冲区

    ini 复制代码
    sort_buffer_size = 8M          -- 增大内存缓冲区
    max_length_for_sort_data = 8192 -- 控制单行数据长度,优先单路排序
  • 避免深分页

    sql 复制代码
    -- 低效写法(扫描前100010行)
    SELECT * FROM logs ORDER BY id LIMIT 100000, 10;
    
    -- 高效写法(基于游标)
    SELECT * FROM logs WHERE id > 100000 ORDER BY id LIMIT 10;

三、GROUP BYORDER BY 的联合使用

当查询同时包含 GROUP BYORDER BY 时,需注意执行顺序和索引设计。

1. 执行顺序
  • 默认顺序 :先执行 GROUP BY 分组,再对分组结果排序。
  • 优化思路
    • ORDER BY 字段与 GROUP BY 字段一致,可省略 ORDER BY(分组后默认有序)。
    • 若不一致,需确保索引覆盖分组和排序字段。
2. 优化示例
  • 场景 :统计每个用户的最新订单并按时间倒序输出。

    sql 复制代码
    -- 低效写法(临时表 + 排序)
    SELECT user_id, MAX(created_at) 
    FROM orders 
    GROUP BY user_id 
    ORDER BY MAX(created_at) DESC;
    
    -- 优化方案(覆盖索引 + 延迟关联)
    ALTER TABLE orders ADD INDEX idx_user_created (user_id, created_at);
    
    SELECT o.user_id, o.created_at 
    FROM orders o 
    INNER JOIN (
      SELECT user_id, MAX(created_at) AS max_time 
      FROM orders 
      GROUP BY user_id
    ) AS tmp ON o.user_id = tmp.user_id AND o.created_at = tmp.max_time 
    ORDER BY o.created_at DESC;

四、监控与分析工具

  1. EXPLAIN 解析执行计划

    • type 字段:index 表示索引扫描,ALL 表示全表扫描。
    • Extra 字段:
      • Using index:覆盖索引。
      • Using temporary:使用临时表。
      • Using filesort:文件排序。
  2. 性能状态监控

    sql 复制代码
    SHOW STATUS LIKE 'Sort%';       -- 查看排序统计
    SHOW STATUS LIKE 'Created_tmp%'; -- 查看临时表统计
  3. Profiling 工具

    sql 复制代码
    SET SESSION profiling = 1;
    SELECT ...; -- 执行查询
    SHOW PROFILE CPU, BLOCK IO FOR QUERY 1;

五、核心优化总结

操作 无索引时的开销 有索引优化策略 关键参数
GROUP BY 临时表 + 文件排序 松散/紧凑索引扫描,覆盖索引 tmp_table_size, sql_mode
ORDER BY 内存/磁盘排序 索引顺序扫描,延迟关联分页 sort_buffer_size, max_length_for_sort_data
联合使用 双重临时表 + 排序 索引覆盖分组和排序字段 联合索引设计,查询重写

最终建议

  • 索引为王 :为高频查询的 GROUP BYORDER BY 字段设计联合索引。
  • 避免全表扫描:通过覆盖索引减少 I/O 和排序开销。
  • 监控先行 :定期分析慢查询日志和 EXPLAIN 结果,针对性优化。
相关推荐
sd21315127 分钟前
springboot3 spring security+jwt实现接口权限验证实现
java·后端·spring
张国荣家的弟弟8 分钟前
【Yonghong 企业日常问题07 】 东方通TongWeb替代Tomcat的实战指南!
java·tomcat
局外人_Jia8 分钟前
Tomcat 新手入门指南
java·tomcat
Dreamboat-L8 分钟前
手写Tomcat
java·tomcat
出门撞大运9 分钟前
手写一个简易版的tomcat
java·tomcat
m0_7482480212 分钟前
Spring Boot 集成 MyBatis 全面讲解
spring boot·后端·mybatis
A阳俊yi12 分钟前
SpringMVC中有关请求参数的问题(映射路径,传递不同的参数)
java·前端·javascript
qq_4476630512 分钟前
《Spring日志整合与注入技术:从入门到精通》
java·开发语言·后端·spring
晴天Y2812 分钟前
tomcat应用的作用以及安装,以及tomcat软件的开机自启动。
java·tomcat
源码姑娘14 分钟前
基于SpringBoot的智慧停车场小程序(源码+论文+部署教程)
spring boot·后端·小程序