目录
[① 导读卡片](#① 导读卡片)
[② 背景与目标](#② 背景与目标)
[③ 核心概念与原理](#③ 核心概念与原理)
[3.1 并发连接数 ≠ TPS](#3.1 并发连接数 ≠ TPS)
[3.2 连接池大小的限制因素](#3.2 连接池大小的限制因素)
[3.3 各硬件级别下的合理连接数](#3.3 各硬件级别下的合理连接数)
[④ 核心详解:调优工具与方法](#④ 核心详解:调优工具与方法)
[4.1 Druid SQL 监控(线上数据源)](#4.1 Druid SQL 监控(线上数据源))
[4.2 JMeter 压测(对比验证)](#4.2 JMeter 压测(对比验证))
[4.3 完整调优流程](#4.3 完整调优流程)
[⑤ 典型应用案例](#⑤ 典型应用案例)
[5.1 案例:巡检提交系统的连接池调优](#5.1 案例:巡检提交系统的连接池调优)
[5.2 自测小问](#5.2 自测小问)
[⑥ 常见坑与最佳实践](#⑥ 常见坑与最佳实践)
[6.1 易错点清单](#6.1 易错点清单)
[6.2 最佳实践](#6.2 最佳实践)
[⑦ 总结与学习路线图](#⑦ 总结与学习路线图)
① 导读卡片
🧩 一句话读懂 :数据库连接池的大小不是固定值,而是根据服务器硬件能力和业务压测数据「调出来」的 🎯 适合人群 :Java 后端开发者、数据库运维人员、面试进阶选手 📊 难度等级 :★★★☆☆(中等) ⏱ 阅读时长 :约 10 分钟 💡 前置知识:Spring Boot + Druid / HikariCP 基础、基本 MySQL 知识
② 背景与目标
为什么学?
很多开发者在配置数据库连接池时习惯"抄别人的配置"------看到 springboot + Druid 官方示例写 20,自己也写 20。但这样配置大概率不对:
-
2 核 4G 的服务器和 16 核 32G 的服务器,连接池大小能一样吗?
-
连接数设多了,反而导致性能下降------"上下文切换"开销大过实际处理
-
连接数设少了,QPS 上不去,用户排队等连接
连接池调优的目标:找到让 TPS 最高的那个连接数,既不会空闲浪费,也不会过度竞争。
学完能怎样?
-
理解连接池大小的核心计算公式(连接数 vs TPS 的曲线关系)
-
能根据服务器硬件估算合理连接池范围
-
学会用 Druid 监控 + JMeter 压测定出最优配置
-
面试时能讲清楚"你们的连接池怎么配的"
③ 核心概念与原理
3.1 并发连接数 ≠ TPS
最常见的误区是把 max_connections 和 TPS 混为一谈:
并发连接数 ≠ TPS
假设 MySQL 的 max_connections 是 151,这不是说每秒只能处理 151 个请求。一个连接可以串行处理多个事务:
连接1: [事务1]→[事务2]→[事务3]→...(1 秒内可以处理好几笔)
连接2: [事务1]→[事务2]→[事务3]→...
| 概念 | 含义 | 默认值 |
|---|---|---|
max_connections |
最大同时打开的连接数 | 151 |
| TPS | 每秒完成的事务数 | 取决于硬件和配置,无固定上限 |
MySQL 单机实测(INSERT 为主):
| 配置 | TPS |
|---|---|
| 普通云服务器 4核8G | 5000~10000+ |
| 中等配置 8核16G | 20000~50000+ |
| 日常业务 300~400 TPS | 非常轻松 |
3.2 连接池大小的限制因素
连接池的大小取决于三个维度:
合理的连接池大小 = f(CPU 核心数, 单事务耗时, 系统负载)
公式 (Tomcat 建议):连接数 = CPU核心数 × 2 + 硬盘数
但这只是粗估。更精确的方法是压测找拐点------不断增加连接数,观察 TPS 的增长曲线:
为什么连接数过多会降低 TPS?
当连接数超过 CPU 能处理的线程数时,操作系统会在大量线程之间频繁切换上下文。一个线程还没处理完就被切换走,另一线程进来,切换本身消耗 CPU,导致真正的处理时间反而减少。
3.3 各硬件级别下的合理连接数
| 硬件 | 单事务耗时 | 合理连接数 | TPS 上限 |
|---|---|---|---|
| 2核4G 普通服务器 | 50ms | 10~15 | ~200 |
| 4核8G 中等配置 | 30ms | 20~30 | ~500 |
| 8核16G 较好配置 | 20ms | 30~50 | ~1500 |
| 16核32G 高配 | 10ms | 50~100 | ~5000 |
规律:硬件越强 → 每个事务处理更快 → 能同时养更多连接 → TPS 上限更高
④ 核心详解:调优工具与方法
4.1 Druid SQL 监控(线上数据源)
先看线上真实数据,而不是拍脑袋定参数。
配置开启 Druid SQL 监控:
XML
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
打开 http://localhost:8080/druid/sql.html,可以看到:
-
每个 SQL 的执行次数:高峰期每秒执行了多少次 INSERT/UPDATE
-
每个 SQL 的平均耗时:单条 SQL 执行花了多少 ms
-
慢查询 SQL:哪条 SQL 拖后腿了
-
连接池使用情况:活跃连接数、池中总连接数
4.2 JMeter 压测(对比验证)
优化前后要跑同样的场景对比,不能靠感觉判断。
JMeter 压测的核心参数:
| 参数 | 设置 |
|---|---|
| 并发线程数 | 日常峰值的 1.5~2 倍 |
| 请求超时 | 一般设 3 秒 |
| 压测时长 | 持续 5~10 分钟(排除瞬时波动) |
| 结果指标 | TPS、平均响应时间、错误率 |
4.3 完整调优流程
线上 Druid 监控 → 确认当前指标
│
▼
JMeter 压力测试 → 找出当前瓶颈
│
▼
调整连接池参数 + SQL 优化
│
▼
再次 JMeter 压测 → 验证优化效果
│
▼
回到 Druid 监控 → 持续观察线上表现
⑤ 典型应用案例
5.1 案例:巡检提交系统的连接池调优
📋 需求描述:2000 个猪舍,早高峰 6 分钟窗口集中提交。优化前 300 并发超时率 40%,平均响应 3 秒。
📊 优化前数据(Druid 监控 + JMeter 压测):
| 指标 | 优化前 | 来源 |
|---|---|---|
| 日常峰值 TPS | ~200 | Druid SQL 监控 |
| 压测并发 | 300~400 | JMeter(1.5~2 倍峰值) |
| 平均响应 | 3 秒 | JMeter |
| 超时率 | 40% | JMeter |
| 单事务耗时 | 50ms | Druid SQL 监控 |
💻 配置调整:
XML
spring:
datasource:
druid:
# 原配置
initial-size: 10
max-active: 20
min-idle: 10
# 优化后配置(4核8G服务器)
initial-size: 10
max-active: 25 # 原来 20 偏保守,小幅度上调
min-idle: 10
max-wait: 3000 # 获取连接超时 3 秒
# 关键优化:开启连接池监控
filter:
stat:
enabled: true
log-slow-sql: true
slow-sql-millis: 200
⚡ 配合优化点:
-
批量 INSERT :把 7 条单条 INSERT 改为批量
INSERT INTO ... VALUES (...), (...), (...) -
补索引:给查询频繁的条件字段加索引,减少锁等待时间
-
缩短事务时长:减少事务内不必要的操作,让连接更快释放
📊 优化后数据(同压测条件下重跑):
| 指标 | 优化前 | 优化后 |
|---|---|---|
| 平均响应 | 3 秒 | 500ms |
| 超时率 | 40% | <1% |
| 单事务耗时 | 50ms | <20ms |
5.2 自测小问
Q:你们连接池参数怎么配的?
连接池大小不是拍脑袋定的。我们先用 Druid 监控看线上真实数据------高峰期 TPS 约 200,单事务耗时 50ms。然后按日常峰值的 1.5~2 倍做 JMeter 压测,发现 300 并发下超时率 40%、响应 3 秒。调整连接池从 20 到 25、配合批量 INSERT 和索引优化后,同样条件重跑,响应降到 500ms、超时率降到 1% 以下。最终线上用的参数是压测验证过的,不是抄的配置。
Q:你们压测打 300~400 TPS,会不会把数据库打崩?
300~400 TPS 对 MySQL 来说并不高,单机轻松能扛这个量。MySQL 的 max_connections 默认 151,但一个连接可以串行处理多个事务,所以 151 连接 ≠ 151 TPS。真正打崩的不是 TPS 高,而是慢 SQL 卡住连接、锁竞争、或者连接池耗尽。我们压测发现瓶颈恰恰在连接池配置不合理和缺少批量 INSERT 上。
Q:Druid 的监控数据可靠吗?
Druid stat filter 是拦截在 JDBC 层面的,每条 SQL 的执行时间和调用次数都真实统计。我们上线后持续观察了高峰期的 SQL 执行次数和平均耗时,确认压测时的数据能反映线上真实情况。
⑥ 常见坑与最佳实践
6.1 易错点清单
| # | 坑点 | 现象 | 原因 | ✅ 避坑方案 |
|---|---|---|---|---|
| 1 | 连接数设置过大 | TPS 反而下降 | 上下文切换消耗 CPU | 从 CPU 核数 × 2 开始,逐步上调找到拐点 |
| 2 | 不压测直接抄配置 | 上线后扛不住 | 各项目硬件/业务不同 | 必须用压测验证参数 |
| 3 | 只调参数不优化 SQL | 连接池放大但事务耗时不变 | 慢 SQL 吃掉了所有连接 | 先优化慢 SQL,再调连接池 |
| 4 | 单条 INSERT 反复执行 | 网络往返多 | 未开启批量操作 | 加 rewriteBatchedStatements=true |
6.2 最佳实践
-
✅ 两步走策略:Druid 监控负责线上真实数据,JMeter 负责优化前后对比验证
-
✅ 滚动调整:连接池每次调整 5~10,不要一下从 20 跳到 100
-
✅ 先 SQL 后连接池:先优化慢 SQL 和锁竞争,再调连接池大小
-
✅ 持续监控:上线后保持 Druid 监控开启,根据业务增长定期复查
核心数字体系速览
| 数字 | 含义 | 来源 |
|---|---|---|
| ~200 TPS | 日常业务峰值 | Druid SQL 监控 |
| 300~400 TPS | 压测并发量(1.5~2 倍峰值) | JMeter |
| 50ms | 优化前单事务耗时 | Druid 监控 |
| 3 秒 → 500ms | 优化前后平均响应 | JMeter 对比 |
| 40% → <1% | 优化前后超时率 | JMeter 对比 |
⑦ 总结与学习路线图
核心要点
| 维度 | 要点 | 一句话记住 |
|---|---|---|
| 连接数 | 不是越多越好 | 找到拐点,过剩反降 |
| 调优方法 | Druid 监控 + JMeter 压测 | 线上看数据,压测验证 |
| 配套优化 | 批量 INSERT + 索引 | 先优化 SQL,再调连接池 |
| 基准 | 公式只是起点 | 压测找到最优值 |
自检清单
- 我能说清楚并发连接数和 TPS 的区别
- 我知道连接数过多为什么反而降性能
- 我能讲出完整的调优流程(监控 → 压测 → 调整 → 验证)
- 我能应对面试官关于"300 TPS 会不会打崩数据库"的追问
下一步学习
阶段一(基础):连接池配置 + 参数含义 → 完成本文
阶段二(进阶):深入了解 MySQL 锁机制、慢查询优化
阶段三(高阶):读写分离、分库分表、连接池监控告警体系搭建