1、简述
在 Java 后端开发中,数据库是系统性能瓶颈的高发地带,而 慢 SQL 查询 往往是系统响应迟缓的"罪魁祸首"。本文将全面梳理慢 SQL 的优化思路,并结合 Java 示例进行实战演练。

2、慢查询的常见表现
慢查询通常表现为:
- 接口响应时间缓慢
- 数据库 CPU 占用高
- 表锁、死锁频繁
- Java 应用线程池阻塞严重
慢 SQL 的主要成因
成因类型 | 说明 |
---|---|
未使用索引 | 全表扫描,查询耗时 |
使用了低效的函数或表达式 | 如 LIKE '%xx%' , DATE() |
多表关联不当 | join 条件缺失或不走索引 |
过多返回字段 | 只用到了部分字段却 SELECT * |
where 条件不精准 | 无法过滤大量无关数据 |
数据库设计不合理 | 字段冗余、缺乏范式、字段类型错误等 |
3、慢查询优化的通用思路
✅ 加索引(重点)
- 为
WHERE
、JOIN
、ORDER BY
、GROUP BY
中涉及的字段加索引 - 避免使用函数包裹字段,如
LEFT(name, 3)
,会导致无法使用索引
✅ 使用 EXPLAIN
分析执行计划
sql
EXPLAIN SELECT * FROM orders WHERE user_id = 100;
关注字段:
字段 | 说明 |
---|---|
type |
连接类型(越接近 const 越好) |
rows |
扫描行数(越小越好) |
key |
使用的索引名称 |
Extra |
是否使用临时表、排序等 |
✅ 分页优化
避免深度分页:
sql
-- 慢查询(跳过大量行)
SELECT * FROM orders LIMIT 1000000, 20;
-- 推荐(使用上次主键记录)
SELECT * FROM orders WHERE id > 1000000 LIMIT 20;
✅ 拆表分区
- 垂直拆分:将大表按字段拆分为多个表
- 水平分表:按业务字段分库分表(如 user_id 分表)
- 分区表:MySQL 原生支持(适合历史归档数据)
✅ 减少嵌套子查询
使用 JOIN 或临时表替代子查询,更高效。
✅ SQL 只查需要的字段
sql
-- 慎用
SELECT * FROM user;
-- 推荐
SELECT id, name, email FROM user;
4、慢 SQL 实践排查与优化
✅ 示例:慢查询前后对比
原始 SQL(慢)
sql
SELECT * FROM orders WHERE DATE(create_time) = '2024-01-01';
📉 问题:
- 使用了
DATE()
函数,索引失效 - 全表扫描,耗时严重
优化 SQL(快)
sql
SELECT * FROM orders
WHERE create_time >= '2024-01-01 00:00:00'
AND create_time < '2024-01-02 00:00:00';
📈 优点:
- 范围查询走索引
- 支持时间范围过滤
✅ Java 中日志配置监控慢 SQL
yaml
# application.yml 示例(Spring Boot)
logging:
level:
com.zaxxer.hikari.HikariConfig: DEBUG
com.zaxxer.hikari: TRACE
spring:
datasource:
url: jdbc:mysql://localhost:3306/demo
username: root
password: root
hikari:
maximum-pool-size: 10
connection-timeout: 3000
使用工具(如 p6spy)打印 SQL 及耗时,或开启 MySQL 慢查询日志:
sql
-- MySQL 开启慢查询日志
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1; -- 超过 1 秒记录
✅ SQL 优化 checklist
- 是否使用了合适的索引
- 是否避免了函数、表达式阻碍索引
- 是否使用了 EXPLAIN 检查执行计划
- 是否合理分页、避免深度翻页
- 是否控制了查询字段数量
- 是否考虑拆分大表或分区表
- 是否避免了嵌套子查询
5、SQL 优化实战样例
场景 1:模糊查询优化
sql
-- 慢:前置通配符无法使用索引
SELECT * FROM user WHERE name LIKE '%abc%';
-- 优化:使用全文索引或右模糊匹配
SELECT * FROM user WHERE name LIKE 'abc%';
场景 2:避免函数阻碍索引
sql
-- 慢
SELECT * FROM orders WHERE YEAR(create_time) = 2024;
-- 快
SELECT * FROM orders
WHERE create_time >= '2024-01-01'
AND create_time < '2025-01-01';
场景 3:多字段组合索引使用顺序
sql
-- 有联合索引 (user_id, status)
-- 推荐:user_id 和 status 都参与
SELECT * FROM orders WHERE user_id = 100 AND status = 'PAID';
-- 不推荐:只用 status,索引无法生效
SELECT * FROM orders WHERE status = 'PAID';
6、结语
慢查询是系统性能优化的重要战场。对于 Java 开发者而言,理解 SQL 执行机制和优化原则,比"用缓存"更根本、更有效。
日常开发中,应做到:
- 编写 SQL 前先考虑是否能走索引
- 查询慢时第一时间用 EXPLAIN 排查
- 数据库设计时就考虑查询结构