SQL调优

SQL 调优的完整链路是:

定位慢 SQL → 看执行计划 → 分析扫描/回表/排序/连接/锁 → 改 SQL 或索引 → 更新统计信息 → 必要时改表结构/参数/架构 → 生产验证。

发现慢 SQL

↓

定位问题

↓

EXPLAIN 分析执行计划

↓

判断:索引?SQL写法?数据量?表结构?

↓

优化

↓

重新 EXPLAIN / 压测

↓

确认执行效率提升

定位慢 SQL

  1. 开启慢查询日志 slow_query_log,设置阈值(比如超过 1s 的 SQL 记录下来)

  2. MySQL:show processlist 查看正在执行的长耗时 SQL

  3. 核心工具:explain 分析执行计划

EXPLAIN------SQL 调优最重要的工具

EXPLAIN SELECT id, user_id, amount

FROM orders

WHERE user_id = 123 AND status = 1

ORDER BY created_at DESC

LIMIT 10;

|---------------|-------------------------------------------------------------------------|
| 字段 | 含义 |
| type | 访问类型,性能从好到差大致:system > const > eq_ref > ref > range > index > ALL |
| possible_keys | 可能用的索引 |
| key | 实际用的索引 |
| key_len | 索引使用长度,可判断联合索引用了几列 |
| rows | 预估扫描行数 |
| filtered | 过滤后剩余比例 |
| Extra | 额外信息,最关键( Extra 经常能够直接暴露 SQL 的问题。) |

常见 Extra:

  • Using index:覆盖索引,无需回表,好。

  • Using where:存储引擎返回后还要过滤。

  • Using index condition:索引条件下推。

  • Using temporary:用了临时表,常见于 GROUP BY、DISTINCT、UNION。

  • Using filesort:额外排序,常见于 ORDER BY 无法用索引。

  • Using join buffer:连接无索引,可能较慢。

type=ALL 通常代表全表扫描;type=index 是扫描整个索引,也不一定好。

索引优化

1. 索引底层

InnoDB 是 B + 树索引,主键是聚簇索引;普通索引(二级索引)叶子节点存主键值,回表查询拿完整数据。

2. 索引失效常见场景

  • 索引列做函数运算、隐式类型转换,where substr(name,1,2)='xx'、where id='123'(id 是 int)

  • 索引使用不等于!=、<>、not in、is not null,会放弃索引

  • like '%关键词' 前缀模糊查询,不走索引;like '关键词%' 可以走

  • 联合索引最左前缀原则,跳过左边字段,后面索引失效

  • MySQL 优化器判断全表扫描比索引更快,主动放弃索引(数据量少时常见)

3. 联合索引设计原则

  • 把等值查询放前面,范围查询(> < between)放最后

例:where a=1 and b>2 and c=3,联合索引 (a,b),c 无法用到索引

  • 区分度高的字段优先放在联合索引靠前位置

  • 尽量建立覆盖索引:查询的所有字段都在索引里,避免回表(Extra:Using index)

SQL 语句写法优化

  1. 只取需要列,不要 select *,只查业务需要的字段,减少回表、网络传输

  2. 减少子查询,优先改成 JOIN;子查询容易创建临时表

  3. IN 里面数据量大的时候,替换成 JOIN;小集合可以用 IN

  4. 避免在大事务里执行大查询;大查询拆成分页查询

  5. 深分页优化:limit 10000,20 ( 扫描/定位前1000000条, 再取20条)偏移量大性能差, 使用基于游标/上一次最大 ID 的分页:where id>10000 limit 20

  6. OR 尽量拆分,或者建立包含 OR 所有字段的联合索引

  7. 批量操作:用 insert values (...),(...) 代替多次单条 insert,减少事务提交次数

表结构优化

  1. 字段类型尽量小:能用 int 不用 bigint,varchar 设置合理长度,char 固定短字符

  2. 禁止 NULL:NULL 会占用额外空间,索引效率差,用默认值代替

  3. 大字段(text、blob)单独拆到扩展表,减少主表行数据大小

  4. 适当分表:水平分表(按时间 / 设备 id,IoT 项目常用)、垂直分表。分库分表是最后手段,会带来分布式事务、跨片查询复杂度。

  5. 主键设计:自增主键,避免随机主键(UUID 会造成页分裂,B + 树频繁调整)

架构层面优化

  1. 读写分离:主库写,从库读,查询流量走从库

  2. 缓存:热点查询放入 Redis,挡掉大部分 DB 查询,减少 SQL 请求

  3. 时序数据(IoT 设备上报):使用 TDengine 这类时序库,不要全部塞进 MySQL

  4. 分库分表:海量数据场景,Sharding-JDBC 做分库分表,注意跨库 join、分页、排序问题

  5. 消息队列:削峰填谷。

  6. 搜索引擎:全文检索走 ES。

  7. OLTP/OLAP 分离:报表走数仓/列存。

数据库配置优化

my.cnf/my.ini

  • innodb_buffer_pool_size:InnoDB 缓冲池,核心,一般设置服务器内存 50%~70%,缓存索引和数据页,减少磁盘 IO

  • innodb_flush_log_at_trx_commit 刷盘策略,平衡性能和数据安全

  • 调整连接数、排序缓冲区 sort_buffer_size、join 缓冲区 join_buffer_size,不建议盲目调大

相关推荐
数据库急诊室_阿哲3 小时前
一次加索引建议的复核记录:从报告到签字
数据库·ai数据库
青瓦梦滋4 小时前
Redis主从复制
服务器·数据库·redis·缓存·bootstrap
慧都小妮子4 小时前
Wyn商业智能国产数据库适配:数据接入、分析缓存与审计日志验证清单
数据库·数据分析·数据可视化·审计日志·商业智能
Joy T5 小时前
Redis Lua 原子限流:Lua 脚本、竞态条件与并发控制
数据库·redis·lua·限流·竞态条件·redis scripting
杨凯凡6 小时前
MySQL 死锁怎么排查?InnoDB 锁等待与 Spring Boot 整事务重试实战
数据库·spring boot·mysql
Aloudata6 小时前
Claude 做数据分析的这两年:一份复盘与发展线索
大数据·数据库·人工智能·数据挖掘·数据分析·dataagent
Decay__6 小时前
01_SQLServer 体系架构全解 —— 实例进程、关系引擎、SQLOS 与事务日志深入
运维·数据库·sql·mysql·架构
zhanghaha13146 小时前
C# 系列教程_12 字符串 string 保姆级系统教程(面向新手,大量示例 + 原理讲解)
开发语言·数据库·c#
韩曙亮6 小时前
【错误记录】安卓 adb 调试 5037 端口占用排查 ( adb 的三层结构 | 找出本机占用 5037 的进程 )
android·数据库·adb·安卓·错误记录
瑞雪兆丰年兮7 小时前
JavaWeb-day07-MySQL(DQL&多表设计)
java·开发语言·数据库·sql·mysql·javaweb·web