目录
[① 导读卡片](#① 导读卡片)
[② 背景与目标](#② 背景与目标)
[③ 核心概念与原理](#③ 核心概念与原理)
[3.1 两个核心工具](#3.1 两个核心工具)
[3.2 关键性能指标](#3.2 关键性能指标)
[3.3 压测的黄金法则](#3.3 压测的黄金法则)
[④ 核心详解:完整调优流程](#④ 核心详解:完整调优流程)
[4.1 第一步:Druid 监控采集线上数据](#4.1 第一步:Druid 监控采集线上数据)
[4.2 第二步:JMeter 压测](#4.2 第二步:JMeter 压测)
[4.3 第三步:诊断与优化](#4.3 第三步:诊断与优化)
[4.4 第四步:再次压测验证](#4.4 第四步:再次压测验证)
[⑤ 典型应用案例](#⑤ 典型应用案例)
[5.1 案例:巡检提交系统性能优化](#5.1 案例:巡检提交系统性能优化)
[5.2 数据溯源口诀](#5.2 数据溯源口诀)
[⑥ 常见坑与最佳实践](#⑥ 常见坑与最佳实践)
[6.1 易错点清单](#6.1 易错点清单)
[6.2 面试话术](#6.2 面试话术)
[6.3 最佳实践清单](#6.3 最佳实践清单)
[⑦ 总结与学习路线图](#⑦ 总结与学习路线图)
① 导读卡片
🧩 一句话读懂 :用 Druid SQL 监控看线上真实数据,用 JMeter 压测做优化前后对比,构建一套从"发现问题 → 定位瓶颈 → 验证优化"的完整性能调优打法 🎯 适合人群 :Java 后端开发者、性能调优入门/进阶者、面试准备者 📊 难度等级 :★★★☆☆(中等) ⏱ 阅读时长 :约 12 分钟 💡 前置知识:Spring Boot + Druid 基础、MySQL 基本操作
② 背景与目标
为什么学?
很多开发者做性能优化会陷入两个误区:
-
凭感觉优化------"我觉得这里慢",改完没有数据对比
-
只会背概念------知道连接池、索引、批量操作,但面试官问"你们怎么测的"就卡住了
真正的性能优化不是拍脑袋猜,而是基于数据的:
监控线上数据 → 发现瓶颈 → 制定方案 → 压测验证 → 上线观察
学完能怎样?
-
掌握 Druid SQL 监控的线上数据采集方法
-
能用 JMeter 做压测并正确解读结果
-
能说清楚性能指标的含义(TPS、P50、P99、超时率)
-
面试时能讲出一套完整的优化案例,有数据有对比有逻辑
③ 核心概念与原理
3.1 两个核心工具
| 工具 | 用途 | 面试官问"怎么测的" |
|---|---|---|
| Druid SQL 监控 | 线上真实数据,统计每条 SQL 的执行时间、调用次数、慢查询比例 | "线上用 Druid 的 stat filter 监控的" |
| JMeter 压测 | 优化前后对比验证,模拟高并发 | "上线前用 JMeter 压测对比的" |
核心打法:Druid 负责线上真实数据,JMeter 负责优化前后对比验证。缺一不可。
3.2 关键性能指标
| 指标 | 全称 | 含义 | 怎么用 |
|---|---|---|---|
| TPS | Transactions Per Second | 每秒完成的事务数 | 衡量系统吞吐量 |
| P50 | 50th Percentile | 50% 的请求在此时间内完成 | 代表大多数用户体验 |
| P99 | 99th Percentile | 99% 的请求在此时间内完成 | 反映尾部延迟 |
| 超时率 | Timeout Rate | 超时请求占总请求比例 | 衡量系统稳定性 |
| 平均响应 | Average Response Time | 所有请求的平均处理时间 | 宏观性能指标 |
3.3 压测的黄金法则
压测并发量 = 日常峰值 × 1.5~2 倍
不是随便设个并发数往上打,而是基于线上实际数据来设:
-
先看 Druid 监控确认日常峰值 TPS
-
压测在这个基础上 ×1.5~2,验证系统冗余
-
如果这个量都扛不住,说明正式上线不安全
④ 核心详解:完整调优流程
4.1 第一步:Druid 监控采集线上数据
配置开启:
spring: datasource: druid: stat-view-servlet: enabled: true url-pattern: /druid/* login-username: admin login-password: admin123 filter: stat: enabled: true log-slow-sql: true slow-sql-millis: 200 # 超过 200ms 的 SQL 记录为慢查询
打开 http://localhost:8080/druid/sql.html 可以看:
-
SQL 执行统计:每个 SQL 的执行次数、总耗时、平均耗时
-
连接池状态:活跃连接数、等待数、创建数
-
URI 监控:哪个接口最慢、调用量最大
从 Druid 能获取的数字:
| 数据 | 获取方式 |
|---|---|
| 日常峰值 TPS | 高峰时段 SQL 执行次数 ÷ 时间窗口 |
| 单事务耗时 | Druid SQL 监控的平均耗时统计 |
| 慢查询 SQL | Druid 慢查询日志 |
| 活跃连接数 | 连接池监控页 |
4.2 第二步:JMeter 压测
压测前的准备工作:
-
确认压测环境和线上环境配置一致(或按比例缩容)
-
设定超时时间(一般 3 秒)
-
压测持续 5~10 分钟(排除瞬时波动)
-
记录优化前的全部指标
JMeter 核心配置:
| 参数 | 设置依据 |
|---|---|
| 线程数(并发) | 日常峰值 TPS × 1.5~2 |
| Ramp-Up 时间 | 5~10 秒(逐步加压,避免瞬间打崩) |
| 持续时间 | 300~600 秒 |
| 超时 | 3000ms |
| 监听器 | 聚合报告、TPS 曲线、错误率 |
4.3 第三步:诊断与优化
常见性能瓶颈与对应方案:
| 瓶颈 | 表现 | Druid 特征 | 优化方案 |
|---|---|---|---|
| SQL 无索引 | 单条 SQL 耗时 > 200ms | 慢查询日志频繁 | 分析执行计划,补索引 |
| 连接池太小 | 大量获取连接超时 | activeCount=最大值,waitCount 高 | 适当调大 maxActive |
| 连接池太大 | TPS 不上反降 | 无异常但性能差 | 减少连接数到拐点 |
| 单条 INSERT 过多 | 网络往返多 | 执行次数高但每单耗时低 | 开启批量操作 |
| 事务内操作过多 | 连接释放慢 | 事务时长 > 100ms | 缩短事务范围 |
4.4 第四步:再次压测验证
优化后在同条件下重新压测,对比两轮数据:
| 指标 | 优化前 | 优化后 | 改善幅度 |
|---|---|---|---|
| 平均响应 | 3 秒 | 500ms | -83% |
| 超时率 | 40% | <1% | -98% |
| 单事务耗时 | 50ms | <20ms | -60% |
注意:一定要同条件对比------同样并发数、同样时长、同样硬件。否则对比无效。
⑤ 典型应用案例
5.1 案例:巡检提交系统性能优化
📋 需求描述:2000 个猪舍,早高峰 6 分钟窗口集中提交巡检结果。优化前接口性能差,超时率高。
📊 优化前数据(Druid 监控):
| 指标 | 值 | 来源 |
|---|---|---|
| 日常峰值 TPS | ~200 | Druid 监控高峰期统计 |
| 单事务耗时 | 50ms | Druid SQL 平均耗时 |
| 连接池 | maxActive=20 | 配置值 |
| 慢查询 | 无 | Druid 监控 |
📊 优化前压测数据(JMeter,300 并发):
| 指标 | 值 |
|---|---|
| 平均响应 | 3 秒 |
| 超时率 | 40% |
| P50 | 1.2 秒 |
| P99 | 10+ 秒 |
🔍 诊断: 单事务耗时 50ms 偏高,原因是 7 条巡检明细分 7 次单条 INSERT------网络往返 7 次,占了绝大部分时间。连接池 20 个连接也有点紧。
⚡ 优化方案:
-
批量 INSERT :开启
rewriteBatchedStatements=true,7 条 INSERT 合并为 1 条 -
调整连接池:maxActive 从 20 调到 25(4核8G 服务器)
-
补索引:条件查询字段加索引,减少锁等待
# 连接池+JDBC 配置优化 spring: datasource: druid: max-active: 25 min-idle: 10 max-wait: 3000 url: jdbc:mysql://localhost:3306/mes_db?rewriteBatchedStatements=true
📊 优化后压测数据(同条件重跑):
| 指标 | 优化前 | 优化后 |
|---|---|---|
| 平均响应 | 3 秒 | 500ms |
| 超时率 | 40% | <1% |
| 单事务耗时 | 50ms | <20ms |
| TPS | ~200 | >500 |
5.2 数据溯源口诀
Druid → 线上真实数据(TPS、单事务耗时)
JMeter → 优化前后对比验证
压测并发 = 日常峰值 × 1.5~2
面试官问任何数字,先说来源再说值:"这个数是从 Druid 监控/JMeter 压测得来的",顺序不能反。
⑥ 常见坑与最佳实践
6.1 易错点清单
| # | 坑点 | 现象 | ✅ 避坑方案 |
|---|---|---|---|
| 1 | 优化后和优化前压测条件不一致 | 对比数据无效 | 同一台机器、同并发数、同时长 |
| 2 | 只压测不线上验证 | 压力少了看起来好,上线又崩 | 压测OK后,上线持续监控一周 |
| 3 | 并发连接数 = TPS 的误解 | 担心压测 300 TPS 会打崩 MySQL | 300 TPS 对 MySQL 很轻松,不是问题 |
| 4 | 只调连接池不优化 SQL | 连接池放大了但慢SQL还在 | 先优化 SQL,再调连接池 |
| 5 | 凭感觉"我觉得这里慢" | 改了不该改的地方 | 基于 Druid 监控数据做决策 |
6.2 面试话术
Q:你们 200 TPS 怎么算的?
Druid SQL 监控页面上看高峰期 SQL 执行次数,除以时间窗口算的。比如高峰期 6 分钟内 SQL 执行次数统计后换算成每秒大约 200 次。
Q:为什么压测要打 300~400 TPS,不是 200?
压测一般按日常峰值的 1.5~2 倍做。日常 200,压测打到 300~400,是验证系统在有突发流量时能不能扛住。如果这个量都扛不住,正式上线高峰期有个小波动就崩了。
Q:把单事务耗时从 50ms 降到 20ms 是怎么做到的?
核心优化是批量 INSERT。原来 7 条明细分 7 次 INSERT,网络往返就占了大部分时间。开启rewriteBatchedStatements=true后合并成一条多值 INSERT,网络往返从 7 次降到 1 次,加上连接池和索引的小幅调整,整体耗时就降下来了。
Q:300 并发打进去,数据库扛得住吗?
300~400 TPS 对 MySQL 来说不算高,单机轻松能扛。MySQL 的 max_connections 是最大连接数(默认 151),但一个连接可以串行处理多个事务。真正扛不住的不是 TPS 高,而是慢 SQL 卡住连接、锁竞争或者连接池耗尽。我们压测发现瓶颈恰恰是配置问题,不是数据库本身。
Q:P50 和 P99 是什么?
P50 是中位数------50% 的请求在这个时间内完成,代表大多数用户的体验。P99 是 99% 的请求在此时间内完成,反映尾部延迟------不管服务器多快,总有些请求因为各种原因(GC、网络抖动、排队)特别慢,P99 就是看这个最慢的 1% 能不能接受。优化前 P99 超过 10 秒,说明有一批用户体验极差。
6.3 最佳实践清单
-
✅ 先监控,后优化:没有 Druid 数据不出方案
-
✅ 压测条件一致:同一硬件、同一并发、同一时长
-
✅ 逐步调整:连接池每次调 5~10,反复压测找到拐点
-
✅ 上线后持续观察:优化后至少监控一周,确认线上表现
-
❌ 不说来源的数字都是废话:"大约 200 TPS" = "从 Druid 监控看到的 200 TPS"
⑦ 总结与学习路线图
核心要点速览
| 维度 | 要点 | 一句话记住 |
|---|---|---|
| 工具 | Druid + JMeter | Druid 看线上,JMeter 做对比 |
| 压测并发 | 峰值 × 1.5~2 | 验证冗余,不是打崩 |
| 优化顺序 | SQL → 连接池 → 架构 | 先解决慢查询,再调连接池 |
| 数据来源 | 先说工具再说值 | "Druid 监控显示......" |
核心数字体系
| 数字 | 定位 | 来源 |
|---|---|---|
| ~200 TPS | 日常业务峰值 | Druid SQL 监控 |
| 300~400 TPS | 压测并发量(1.5~2 倍峰值) | JMeter |
| 50ms | 优化前单事务耗时 | Druid 监控 |
| 3 秒 → 500ms | 优化前后平均响应 | JMeter 对比 |
| 40% → <1% | 优化前后超时率 | JMeter 对比 |
自检清单
- 我能说清楚 Druid 和 JMeter 各自负责什么
- 我知道压测并发量怎么确定(1.5~2 倍峰值)
- 我能讲一个完整的优化案例(发现问题 → 定位 → 优化 → 验证)
- 我知道 P50、P99、TPS 的含义和用途
下一步学习
阶段一(基础):掌握 Druid 配置 + JMeter 基本用法 → 完成本文
阶段二(进阶):MySQL 执行计划分析、索引优化、连接池参数深入调优
阶段三(高阶):读写分离、分库分表、缓存优化、全链路压测