MySQL 中如何进行 SQL 调优?
- 标签:后端、MySQL、数据库、场景题
🏛 业务架构师
SQL 调优我有一套按业务优先级排的标准动作,而不是罗列技巧:
第一步:定位,用数据说话
- 慢查询日志(long_query_time)+ pt-query-digest 聚合,按"总耗时 × 频率"排序——先治业务损失最大的,而不是最慢的那条;
- 结合监控判断是偶发(锁等待、批量任务撞车)还是持续劣化(数据量增长)。
第二步:EXPLAIN 诊断四个信号
- type=ALL 全表扫描、rows 远超预期:索引问题;
- Extra 出现 Using filesort / Using temporary:排序与临时表问题,常见于 order by、group by 未命中索引;
- key 为 NULL 或选错索引:统计信息过旧,analyze table;
- 注意隐式类型转换、函数包裹索引列、前导模糊匹配这些"索引失效"高发写法。
第三步:按成本从低到高实施
- 改 SQL:明确列替代 select *、深分页改游标、大事务拆小;
- 改索引:联合索引调整列顺序、建覆盖索引——注意评估写放大;
- 改架构:读多写少的走缓存/读写分离,统计类的走 ES/ClickHouse,把查询流量迁离交易库是终极手段。
第四步:闭环机制:慢 SQL 接入告警、上线前 EXPLAIN 卡点、季度索引审计。
架构师视角的核心:调优优先级等于资损排序——支付链路的 100ms 优先于报表页的 3 秒。技术动作要贴着业务价值排期。
🏗 系统架构师
SQL 调优我按"发现 → 分析 → 改写 → 验证"四步的方法论答:
发现:慢日志(long_query_time)+ pt-query-digest 聚合出 TOP SQL。
分析:EXPLAIN 看 type(至少 range,ref/eq_ref 更好)、key、rows(扫描量)、Extra(Using filesort / temporary 都是坏味道);怀疑优化器选错索引时对比 force index。
改写手段:
- 只 select 需要的列,配合覆盖索引消回表;
- 深度分页用延迟关联或游标(where id > x limit n);
- 深层 or 改 union all、not in 改 not exists;join 小表驱动大表,被驱动表连接列有索引;
- 大事务大 delete 分批(limit 循环),避免长事务与主从延迟;
- 隐式类型转换、函数包列、前导 % 这类索引失效写法逐条治理。
验证:从库跑真实量级数据对比执行计划与耗时,用 performance_schema / slow log 确认收益。
架构视角:单 SQL 优化有天花板,量级问题要上升手段——读写分离、缓存(Redis 扛读)、ES 扛复杂查询、分库分表扛容量,SQL 调优是容量治理链路的最后一环而非唯一手段。
💻 全栈开发专家
我有一套固定 SOP,比背知识点有用:
第一步:找到慢 SQL
sql
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 0.5;配合 pt-query-digest 聚合慢日志,按"总耗时排序"而不是单次耗时,优先治理占比最高的。
第二步:EXPLAIN 诊断,我重点看四列:
- type:ALL(全表扫)必改;index 也不行(扫整棵索引树);
- key / key_len:走没走索引、联合索引用了几列;
- rows:预估扫描行数,和结果集差一个数量级以上就有优化空间;
- Extra:
Using filesort(排序没走索引)、Using temporary(临时表,group by 常见)。
第三步:三板斧改写
- 加/改联合索引(等值在前、范围在后,覆盖查询列);
- 深分页改游标
WHERE id > ?或延迟关联; - 消灭
SELECT *、隐式类型转换、索引列函数。
第四步:验证:同一条 SQL EXPLAIN FORMAT=TREE 对比,实测 RT 前后对比,别只看优化器估算。
架构层兜底:读多写少的走缓存,聚合查询挪到从库或数仓,别把 OLAP 查询打到交易库。