前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >线上问题处理案例——出乎意料的数据库连接池

线上问题处理案例——出乎意料的数据库连接池

作者头像
京东技术
发布2023-09-21 17:41:44
1900
发布2023-09-21 17:41:44
举报
文章被收录于专栏:京东技术京东技术

Tech 导读 本文是线上问题处理案例分析,旨在通过真实案例向读者介绍发现问题、定位问题、解决问题的方法。本文讲述了从垃圾回收耗时过长的表象,逐步定位到数据库连接池保活问题的全过程,并对其中用到的一些知识点进行了总结。

01

问题描述

在今年的敏捷团队建设中,我通过Suite执行器实现了一键自动化单元测试。Juint除了Suite执行器还有哪些执行器呢?由此我的Runner探索之旅开始了

大促期间,某接口超时次数增多,经排查直接原因是GC耗时过长,查看监控FullGC达500ms以上,接口超时时间与FullGC发生时间吻合。

图1 FULLGC耗时监控

02

应用基本情况

理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目标页面展示到屏幕。从设计稿出发,提升页面搭建效率,亟需解决的核心问题有: 2.1 触达技术选型

容器:8C12G;

JVM配置:-XX:+UseConcMarkSweepGC -Xms6144m -Xmx6144m -Xmn2048m -XX:ParallelGCThreads=8 -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 -XX:+ParallelRefProcEnabled;

数据库类型:MySQL;

数据库连接池:DBCP;

03

排查过程

理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目标页面展示到屏幕。DevOps流水线简介:

1、 GC耗时过长,说明内存中垃圾对象很多。

2、首先怀疑是否有内存泄漏,观察FullGC后堆内存回收情况,尚属正常,暂时排除内存泄漏原因。

图2 发生FullGC后堆内存回收监控

3、 推断FullGC耗时过长是否因为老年代有大量死亡对象,遂导出FullGC前后堆内存dump,通过比对“保留大小”,发现FullGC后大量数据库相关对象被回收。

图3 堆内存对象分析

4、 数据库连接正常应该不会频繁创建和断开,进入老年代后,正常不应该被回收,通过堆dump内容OQL分析每个数据库连接数量,发现很多库连接数都大于“maxActive”数量,可以肯定有很多失效连接。

5、 初步判断直接原因是很多失效数据库连接进入老年代,导致FullGC耗时过长。

6、 怀疑连接池验证周期过长,导致数据库因空闲过长关闭连接,将连接池参数“timeBetweenEvictionRunsMillis”由1分钟调整到10秒,问题依旧。

7、 阅读DBCP源码,发现是通过org. apache. commons. pool. impl. Generic Object Pool. Evictor定时任务,按照time Between Eviction Runs Millis配置的周期定时驱逐失效连接,驱逐条件:若连接空闲时间大于“min EvictableIdle Time Millis”,则会驱逐连接,等待垃圾回收。若开启“test WhileIdle”则会执行“validationQuery”。进一步阅读代码,发现执行“validation Query”后,连接空闲时间并不会重新计算,导致连接在业务低谷时很容易被淘汰,而数据库连接会关联大量对象,创建、回收成本昂贵,并且影响GC。

8、 反向思考,为何只有在大促期间才发生问题?

图4 平时和大促时回收频率对比

可以看到平时由于业务量小,GC不频繁,过期连接没有达到进入老年代阈值,在年轻代被回收。而大促时业务量大,GC频繁,连接在进入老年代以后才过期,导致老年代FullGC时间过长。

9、 至此,基本可以肯定问题原因是数据库连接池不具备“保活”能力,导致连接不断淘汰和新建,在业务高峰时段,连接进入老年代然后失效,造成FullGC耗时过长,最终导致接口超时次数增多。

04

解决方案

理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目标页面展示到屏幕。

方案1:改为G1回收器,对老年代回收是分块进行,可以防止长时间停顿。另外默认Max Tenuring Threshold值是15,可以防止失效连接过早进入老年代;

方案2:min EvictableIdle Time Millis设置为0,使数据库连接不会自动失效,进入老年代以后一直存活,避免在老年代失效回收;

方案3:min EvictableIdle Time Millis设置为0,使数据库连接不会自动失效,进入老年代以后一直存活,避免在老年代失效回收;

05

拓展知识点

理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目标页面展示到屏幕。

1、 Druid连接池同样存在不能“保活”问题,较新版本提供“Keep Alive”选项(未验证);

2、 Druid连接池配置的“validation Query”语句通常并不会被执行,MySql Valid Connection Checker在检查连接有效性时,会判断驱动是否实现ping Internal方法,如果实现则会通过此方法验证有效性。MySQL的JDBC驱动实现了该方法,因此“validation Query”配置的语句通常不会执行;

图5 连接有效性校验代码

3、 DBCP和Druid连接池默认都是FILO,如果业务不繁忙,会导致只有最前边的连接被使用-归还-使用,后边连接基本都在无谓的驱逐、重建连接;

4、 虚引用对GC的影响:这些引用只有经过两次GC才能被回收掉,如果进入老年代,则必须经过两次FullGC才能释放内存。本例中由于不断有新的虚引用对象在老年代失效,导致FullGC后,内存水位仍然偏高,会加剧GC压力。新版本JVM已对此做了优化,一次GC可以回收掉;

5、 类似的影响还有finalize方法;

6、 CMS回收器默认MaxTenuringThreshold为6,而ParallelGC和G1均默认15;

06

结语

理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目标页面展示到屏幕。

本文对数据库连接失效引起的GC问题进行了详细分析,希望读者通过本文对数据库连接“保活”机制、GC问题基本分析方法有所收益,后续该系列文章会继续推出其他案例分享。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-04-18 18:58:00,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 京东技术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档