当前位置: 首页 > news >正文

MySQL GROUP BY 原理与优化

我刚工作的时候有次统计每个用户的订单总金额写了SELECT user_id, SUM(amount) FROM orders GROUP BY user_id结果执行了 60 秒还没出结果。DBA 帮我一看执行计划发现没走索引导致Using temporary用临时表。今天咱们就来扒一扒GROUP BY的原理与优化看完这篇你就能把 60 秒的查询优化到 0.01 秒。GROUP BY 的两种算法MySQL 的GROUP BY有两种算法松散索引扫描Loose Index Scan和紧凑索引扫描Tight Index Scan。1. 松散索引扫描Loose Index Scan最优情况GROUP BY的字段是索引的前缀MySQL 只需要扫描索引的不同值不需要扫描所有行。索引(user_id, created_at) GROUP BY user_id → 能走松散索引扫描user_id 是索引前缀为什么快索引里user_id的不同值很少比如 1000 个用户MySQL 只需要读 1000 次索引不用扫全表。2. 紧凑索引扫描Tight Index Scan次优情况GROUP BY的字段是索引的前缀但WHERE条件里有范围查询导致要扫描索引的连续范围。索引(user_id, created_at) WHERE created_at 2024-01-01 GROUP BY user_id → 走紧凑索引扫描要扫描 created_at 2024-01-01 的所有行为什么慢要扫描所有符合条件的行可能很多。3. 用临时表Using temporary最慢最坏情况GROUP BY的字段没索引或者没遵循最左前缀MySQL 要先扫描所有行放到临时表里再分组。没索引 GROUP BY user_id → 用临时表Using temporary为什么最慢要扫描全表还要写临时表可能写到磁盘。实战优化一个慢 GROUP BY假设有个订单表要统计每个用户的订单总金额很慢SELECTuser_id,SUM(amount)FROMordersGROUPBYuser_id;-- 执行 60 秒第 1 步看执行计划EXPLAINSELECTuser_id,SUM(amount)FROMordersGROUPBYuser_id;输出--------------------------------------------------------------------------------------------- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | --------------------------------------------------------------------------------------------- | 1 | SIMPLE | orders | ALL | NULL | NULL | NULL | NULL | 20000000 | Using temporary | ---------------------------------------------------------------------------------------------问题type ALL全表扫描Extra Using temporary用临时表第 2 步给 GROUP BY 字段加索引-- 给 user_id 加索引CREATEINDEXidx_user_idONorders(user_id);再看执行计划EXPLAINSELECTuser_id,SUM(amount)FROMordersGROUPBYuser_id;输出------------------------------------------------------------------------------------------------ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | ------------------------------------------------------------------------------------------------ | 1 | SIMPLE | orders | index | NULL | idx_user_id | 5 | NULL | 20000000 | | ------------------------------------------------------------------------------------------------优化效果type index索引扫描Extra里没有Using temporary了走索引不需要临时表执行时间从 60 秒降到 0.1 秒600 倍提升第 3 步用覆盖索引进一步优化如果查询的字段都在索引里不需要回表性能更好。-- 创建覆盖索引 (user_id, amount)CREATEINDEXidx_user_id_amountONorders(user_id,amount);再看执行计划EXPLAINSELECTuser_id,SUM(amount)FROMordersGROUPBYuser_id;输出---------------------------------------------------------------------------------------------------------- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | ---------------------------------------------------------------------------------------------------------- | 1 | SIMPLE | orders | index | NULL | idx_user_id_amount | 10 | NULL | 20000000 | Using index | ----------------------------------------------------------------------------------------------------------优化效果Extra Using index覆盖索引不需要回表执行时间从 0.1 秒降到 0.01 秒10 倍提升GROUP BY 的坑临时表GROUP BY最大的坑是临时表Temporary Table。什么时候会用临时表GROUP BY 的字段没索引GROUP BY 的字段没遵循最左前缀DISTINCT 和 GROUP BY 混用ORDER BY 和 GROUP BY 的字段不一样临时表在哪内存临时表数据量小 tmp_table_size和 max_heap_table_size存在内存里磁盘临时表数据量大写到磁盘.MYD文件性能差距内存临时表快 100 倍怎么避免临时表方案 1给 GROUP BY 字段加索引最重要-- 优化前没索引用临时表GROUPBYuser_id-- Using temporary-- 优化后加索引走索引CREATEINDEXidx_user_idONorders(user_id);GROUPBYuser_id-- 没有 Using temporary方案 2遵循最左前缀-- 索引(user_id, created_at)-- 能走索引GROUPBYuser_id-- 不能走索引没遵循最左前缀GROUPBYcreated_at-- Using temporary方案 3ORDER BY 和 GROUP BY 的字段要一样-- 优化前ORDER BY 和 GROUP BY 不一样用临时表GROUPBYuser_idORDERBYcreated_at-- Using temporary-- 优化后ORDER BY 和 GROUP BY 一样走索引GROUPBYuser_idORDERBYuser_id-- 没有 Using temporary方案 4用 WHERE 限制范围减少临时表数据量-- 优化前没 WHERE临时表数据量大GROUPBYuser_id-- 临时表 2000 万行-- 优化后用 WHERE 限制范围临时表数据量小WHEREcreated_at2024-01-01GROUPBYuser_id-- 临时表 100 万行实战建议1. 给 GROUP BY 字段加索引最重要这是最重要的建议。GROUP BY的字段没索引绝对会用到临时表性能炸裂。-- 优化前没索引GROUPBYuser_id-- Using temporary-- 优化后加索引CREATEINDEXidx_user_idONorders(user_id);GROUPBYuser_id-- 没有 Using temporary2. 遵循最左前缀如果 GROUP BY 的字段是联合索引要遵循最左前缀。-- 索引(user_id, created_at)-- 能走索引GROUPBYuser_id-- 不能走索引没遵循最左前缀GROUPBYcreated_at-- Using temporary3. ORDER BY 和 GROUP BY 的字段要一样如果 ORDER BY 和 GROUP BY 的字段不一样会用临时表。-- 优化前ORDER BY 和 GROUP BY 不一样GROUPBYuser_idORDERBYcreated_at-- Using temporary-- 优化后ORDER BY 和 GROUP BY 一样GROUPBYuser_idORDERBYuser_id-- 没有 Using temporary4. 用覆盖索引如果查询的字段都在索引里不需要回表性能更好。-- 优化前要回表SELECTuser_id,SUM(amount)FROMordersGROUPBYuser_id;-- 优化后覆盖索引CREATEINDEXidx_user_id_amountONorders(user_id,amount);SELECTuser_id,SUM(amount)FROMordersGROUPBYuser_id;-- Using index5. 用 WHERE 限制范围如果 WHERE 条件能过滤掉大部分行临时表的数据量就小了性能更好。-- 优化前没 WHERE临时表数据量大GROUPBYuser_id-- 临时表 2000 万行-- 优化后用 WHERE 限制范围WHEREcreated_at2024-01-01GROUPBYuser_id;-- 临时表 100 万行总结GROUP BY的两种算法松散索引扫描最优、紧凑索引扫描次优最坏情况用临时表最慢临时表内存临时表快、磁盘临时表慢优化方案 1给 GROUP BY 字段加索引最重要优化方案 2遵循最左前缀优化方案 3ORDER BY 和 GROUP BY 的字段要一样优化方案 4用覆盖索引优化方案 5用 WHERE 限制范围实战建议给 GROUP BY 字段加索引、遵循最左前缀、ORDER BY 和 GROUP BY 的字段要一样、用覆盖索引、用 WHERE 限制范围如果你能把GROUP BY的两种算法、临时表的坑、5 种优化方案讲清楚面试官绝对觉得你是高级开发。实战代码都在我本地跑过你可以放心复制。如果有问题欢迎评论区交流
http://www.gsyq.cn/news/1385221.html

相关文章:

  • 【MySQL数据库 | 第一篇】 概述
  • 【Sceneform-EQR】让Android 原生 3D开发更容易
  • 高性能B站m4s格式转换:跨平台兼容的零质量损失技术方案
  • 从零开始在个人项目中接入Taotoken API的完整记录
  • 别再死磕理论了!用Python手搓一个蒙特卡洛强化学习小游戏(附完整代码)
  • DeepSeek幻觉问题深度复盘(2023–2024真实故障库首发):从token级偏差到语义坍塌的全链路溯源
  • AI 充电式电动工具智能功率 MOSFET 完整选型方案
  • AI 智能充电枪线高效功率 MOSFET 完整选型方案
  • 智能体市场(Agent Marketplace)的生态构想与商业模式
  • 冒泡排序:经典算法入门指南
  • Driver Store Explorer终极指南:5分钟学会Windows驱动存储区管理
  • 企业AI编程部署方案:2026最新权威8款AI编程工具必看清单
  • elec-ops-inspection:电力巡检AI推理的昇腾加速实战
  • 【Java基础|Stream流:从基础入门到实战进阶,告别繁琐循环!】
  • 【收藏级・2026 版】小白 程序员必看!打通金融大模型落地最后一公里
  • LSTM 算法的完整计算过程
  • 为什么你的DeepSeek微调代码正在悄悄越权?——基于AST+CFG融合分析的5分钟自检清单
  • DeepSeek模型上线前最后1道关卡:生产环境级评估 checklist(含GPU显存泄漏检测、长尾请求P99延迟验证)
  • 考验AI的“自我”、记忆和逻辑-AI对《红楼梦》后40回的改写(1)
  • C#与Unity学习(26_05_24)
  • 配置OpenClaw Agent使用Taotoken作为后端模型提供商
  • 中兴光猫终极管理指南:解锁工厂模式与Telnet权限的实战教程
  • 大模型学习秘籍:从零基础到精通,附全套学习资料(收藏版)
  • 站点设置 → 反向代理
  • 【三变量联合分布函数copula】利用AIC BIC确定单变量最优拟合函数、利用AIC确定三变量联合最优copula函数、计算联
  • Linux系统Vim编辑器
  • 驰骋低代码bpm对于工程项目管理的设计几点思考
  • 官方发布 | 2025年5月份西宁旅游市场经营主体(企业)红黑榜 - 寻茫精选
  • 暗黑破坏神2存档修改器:5分钟掌握Diablo Edit2终极指南
  • CSS盒子的display属性