背景
我们公司正在处于某个项目的维护阶段,领导对资源告警比较重视,服务器资源告警的就不说了,运维同学每隔一小时都会检测线上环境的应用服务信息,例如:网关日志响应时间告警/nginx日志接口响应时间告警/日志关键字异常日志告警等,每周都需要对告警信息的排查分析~
都是通过邮件发送给干系人~
例如,这个是邮件信息截图~
排查思路
日志--【检索】->traceId--【skywaling】->完整的调用链信息
1.从日志中查到TraceId。
2.通过TraceId获取调用链
通过traceId在skywalking中的可视化界面上可以看出获取数据库链接就花费了7百多毫秒
大家无需纠结接口花费了6s多,而本次却在重点说获取链接池的情况。其他的可以从代码或者sql进行入手优化~~只是获取数据库链接耗时较多,所以着重进行了分析。
3.查看该应用的数据库链接池配置
druid:
# 初始化时建立物理连接的个数
initial-size: 20
# 最大连接池数量
max-active: 100
# 最小连接池数量
min-idle: 20
# 获取连接时最大等待时间
max-wait: 60000
# 是否缓存preparedStatement,也就是PSCache。
pool-prepared-statements: false
# 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。
max-pool-prepared-statement-per-connection-size: -1
# 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。
validation-query: SELECT 'x'
# 单位:秒,检测连接是否有效的超时时间。
validation-query-timeout: 1
# 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
test-on-borrow: false
# 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
test-on-return: false
# 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
test-while-idle: true
# 有两个含义:1) Destroy线程会检测连接的间隔时间,如果连接空闲时间大于等于minEvictableIdleTimeMillis则关闭物理连接。2) testWhileIdle的判断依据,详细看testWhileIdle属性的说明
time-between-eviction-runs-millis: 6000
# 连接保持空闲而不被驱逐的最小时间
min-evictable-idle-time-millis: 1200000
max-evictable-idle-time-millis: 1800000
# 监控页面相关配置
stat-view-servlet:
url-pattern: /druid/*
allow: ""
enabled: true
4.获取该应用数据库链接池的监控(需要对应的时间段)
从图中可以看出PoolingCount在这个时刻突然下降,突然下降可能表示有突发流量
详细解释(poolingCount
)
在数据库连接池监控中,poolingCount
是一个重要的监控指标,它表示当前连接池中空闲的、可用的数据库连接数量。
含义
poolingCount
表示连接池中当前处于空闲状态、可以被应用程序直接获取使用的连接数量这些连接已经创建并与数据库建立了连接,但暂时没有被应用程序使用
它是连接池中"准备好的"连接的数量
相关指标对比
在常见的连接池实现中,通常会有以下相关指标:
指标名称 | 含义 |
---|---|
poolingCount | 当前空闲连接数 |
activeCount | 当前活跃连接数(正在被使用的连接) |
maxActive | 连接池最大允许的连接数 |
minIdle | 连接池保持的最小空闲连接数 |
maxIdle | 连接池允许的最大空闲连接数 |
监控意义
性能调优依据:
如果
poolingCount
经常为0,可能表明连接池配置过小如果
poolingCount
长期很高,可能表明配置过大
问题诊断:
poolingCount
突然下降可能表示有突发流量poolingCount
长期低位可能表示连接泄漏
容量规划:
结合
activeCount
和poolingCount
可以评估连接池使用情况帮助确定合适的
maxActive
和minIdle
参数
✅ 图表汇总分析(监控时间:近6小时)
1. IdleConnection(空闲连接)
大部分时间维持在 20 左右,直到接近 14:00 时突然拉升。
说明:
min-idle: 20
已经生效,Druid 正在努力维持至少 20 个空闲连接。
2. PoolingCount(连接池中的连接总数)
长期稳定在 5~10 左右,你说的问题验证了。
但注意这个指标来源标注为
TotalConnection(...)
,它并非严格意义的“空闲连接数”,可能是连接池当前存在的连接总数(和 Idle + Active 的关系要确认 exporter 定义)。
3. ActiveConnection(活跃连接数)
绝大多数时间活跃连接在 0~2 之间;
即使接近 14:00 的高峰期,也只有少量连接(<10)在活跃使用;
✅ 说明系统当前数据库并发负载是低的。
4. ThreadsAwaitingConnection(等待获取连接的线程数)
全程为 0;
✅ 说明连接池没有连接耗尽问题,应用线程从不需等待连接。
5. MaxConnection(连接池历史最大并发连接数)
常态是 4,只有在快到 14:00 时突增至 200;
猜测是某个服务(或某次压测)在那个时段拉高了连接数。
✅ 结论
项目 | 结论 |
---|---|
min-idle 是否生效? |
✅ 是的,空闲连接维持在 20 |
是否存在连接不足 / 堵塞? | ❌ 没有。activeCount 、waitThreadCount 都非常低 |
是否需要增加 min-idle? | ❌ 目前不需要,现有配置已满足需求 |
是否要扩大连接池? | ❌ 不需要,max-active: 100 远超实际并发需求 |
当前链接数在22左右,超过了预热的的链接数20,所以需要与mysql中建立链接。
思考:
但是为什么与mysql建立链接这么长时间?
1.mysql的监控
Threads Cached = 1
意味着什么?
这表示:
当前 MySQL 有 1 个空闲线程 在等待下一个连接复用;
如果有新连接请求到来,MySQL 可以立刻用这 1 个线程处理,而 无需调用
pthread_create()
创建新线程;
重点观察图表分析
1. 线程缓存 (MySQL Thread Cache
)
Threads Created
几乎为 0,说明线程缓存有效;但
Threads Cached
明显有下降段(21:45 前后出现断崖);表示短时间内大量连接请求导致缓存线程被用完,需新建线程。
这会造成连接响应变慢,尤其是连接池新建连接时要与数据库创建新线程,代价高。
2. 临时表 (MySQL Temporary Objects
)
每秒创建临时表数在 4~5 个,且偶尔触发
Created Tmp Disk Tables
(磁盘临时表);说明存在大量使用
GROUP BY
、ORDER BY
、子查询、联合查询等临时表消耗。
磁盘临时表严重影响 SQL 响应速度,从而增加连接持有时间。
3. 排序 (MySQL Sorts
)
Sort Rows
高峰时可达 30~40;Sort Scan
也非 0,说明未命中索引的排序操作存在。
排序越多、越复杂,单个 SQL 执行越慢 → 应用端连接持有时间越长 → 导致连接池空闲不足。
4. Select Types 分布
Select Scan
稳定在高位,说明存在大量全表扫描;Select Range
较低,表示索引未能充分利用。
5. 慢查询 (MySQL Slow Queries
)
21:45 左右出现明显慢 SQL 峰值,最高达 1.45/s;
时间点与线程缓存耗尽、临时表升高高度吻合!
综合分析:导致连接获取慢的根因
MySQL 层 | 临时表多 / 排序多 / 索引未命中 | SQL 响应时间延长,连接被长时间占用 |
MySQL 层 | Thread Cache 命中率下降 | 新建线程成本高,拖慢新连接初始化 |
应用层 | 连接池配置不足 / minIdle 过低 | 无法预热足够连接,应对突发请求 |
应用层 | SQL 过慢 + 连接未及时释放 | 导致连接池回收和复用效率变差 |
优化建议
A. MySQL 层建议
慢查询排查 | 开启 slow_query_log ,排查 21:45 的慢 SQL |
索引优化 | 优化 SELECT SCAN 多的表结构,添加合适联合索引 |
避免临时表 | 检查是否有复杂嵌套子查询 / group by 未配 index |
调大线程缓存 | thread_cache_size = 128 |
临时表优化 | 调整 tmp_table_size 和 max_heap_table_size 至 128M 起步 |
排序优化 | 优化 ORDER BY 相关 SQL,能使用索引就用索引 |
B. 应用层建议(Druid)
spring:
datasource:
druid:
initial-size: 30
min-idle: 30
max-active: 100
max-wait: 30000
validation-query: SELECT 1
test-on-borrow: true
test-while-idle: true
time-between-eviction-runs-millis: 5000
remove-abandoned: true
remove-abandoned-timeout: 180
log-abandoned: true
重点说明:
initial-size
,min-idle
: 提高初始连接和空闲连接数,快速预热;test-on-borrow
: 确保连接可用,避免使用“死连接”;remove-abandoned
: 回收长时间未关闭连接,防止泄露;max-wait
: 缩短应用等待连接时间,提高失败可控性;
总结一句话:
MySQL 当前存在执行慢 / 缓存线程耗尽 / 排序临时表密集的问题,间接导致应用端连接获取慢,连接池设置不当进一步放大了问题
当前mysql数据库设置:
SHOW VARIABLES LIKE 'thread_cache_size';
68
将thread_cache_size 调整到128后,后续再看结果