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,不建议盲目调大

相关推荐
猫咪很可爱43 分钟前
Oracle Data Pump 导入报错 ORA-02375:字符集不一致问题分析与处理
数据库·oracle
java1234_小锋1 小时前
MySQL 宣布正式拥抱 AI
数据库·人工智能·mysql
SabrinaW7111 小时前
基于 C++17 与 RTTR 的注册式对象工厂:配置驱动的动态对象创建
java·前端·数据库
NineData1 小时前
MySQL 迁移到 OceanBase 怎么做?NineData 全量迁移、CDC 实时同步与数据校验指南
数据库·mysql·oceanbase
standxy1 小时前
索引设计实战:pgvector HNSW × JSONB GIN × 复合索引
数据库·开发
guo_wen_qiang1 小时前
使用spring-data-mongodb操作mongo记录
数据库·mongodb
大鳥1 小时前
JSON 解析最佳实践:从入门到生产级优化
hive·sql·spark·json
pjj198541 小时前
Spark SQL 核心知识详解:DataFrame、DSL 与数据源
大数据·sql·spark
凤山老林1 小时前
授权模型只会RBAC?深入 ABAC 授权模型,实现权限判断做成策略引擎
大数据·数据库·算法