关于数据库:线上问题处理案例出乎意料的数据库连接池-京东云技术团队

49次阅读

共计 2310 个字符,预计需要花费 6 分钟才能阅读完成。

导读

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

一、问题形容

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

图 1 FullGC 耗时监控

二、利用根本状况

  • 容器:8C12G;
  • JVM 配置:-XX:+UseConcMarkSweepGC -Xms6144m -Xmx6144m -Xmn2048m -XX:ParallelGCThreads=8 -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 -XX:+ParallelRefProcEnabled;
  • 数据库类型:MySQL;
  • 数据库连接池:DBCP;

三、排查过程

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.GenericObjectPool.Evictor 定时工作,依照 timeBetweenEvictionRunsMillis 配置的周期定时驱赶生效连贯,驱赶条件:若连贯闲暇工夫大于“minEvictableIdleTimeMillis”,则会驱赶连贯,期待垃圾回收。若开启“testWhileIdle”则会执行“validationQuery”。进一步浏览代码,发现执行“validationQuery”后,连贯闲暇工夫并不会从新计算,导致连贯在业务低谷时很容易被淘汰,而数据库连贯会关联大量对象,创立、回收老本低廉,并且影响 GC。

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

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

能够看到平时因为业务量小,GC 不频繁,过期连贯没有达到进入老年代阈值,在年老代被回收。而大促时业务量大,GC 频繁,连贯在进入老年代当前才过期,导致老年代 FullGC 工夫过长。

9、至此,根本能够必定问题起因是数据库连接池不具备“保活”能力,导致连接不断淘汰和新建,在业务顶峰时段,连贯进入老年代而后生效,造成 FullGC 耗时过长,最终导致接口超时次数增多。

四、解决方案

计划 1 :改为 G1 回收器,对老年代回收是分块进行,能够避免长时间进展。另外默认 MaxTenuringThreshold 值是 15,能够避免生效连贯过早进入老年代;

计划 2
minEvictableIdleTimeMillis 设置为 0,使数据库连贯不会主动生效,进入老年代当前始终存活,防止在老年代生效回收;

五、问题总结

数据库连接池并不具备通常了解的“保活”能力,数据库连贯在业务不沉闷的利用中,会一直淘汰和重连,而连贯会通过虚援用形式(
com.mysql.jdbc.NonRegisteringDriver$ConnectionPhantomReference)携带大量对象,如果连贯存活工夫内 YGC 次数达到寿命阈值,则会进入老年代,老年代是应用“标记 - 革除”算法,回收老本更高,进而造成 FullGC 耗时过长。

六、拓展知识点

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

2、Druid 连接池配置的“validationQuery”语句通常并不会被执行,MySqlValidConnectionChecker 在查看连贯有效性时,会判断驱动是否实现 pingInternal 办法,如果实现则会通过此办法验证有效性。MySQL 的 JDBC 驱动实现了该办法,因而“validationQuery”配置的语句通常不会执行;

图 5 连贯有效性校验代码

3、DBCP 和 Druid 连接池默认都是 FILO,如果业务不忙碌,会导致只有最前边的连贯被应用 - 偿还 - 应用,后边连贯根本都在无谓的驱赶、重建连贯;

4、虚援用对 GC 的影响:这些援用只有通过两次 GC 能力被回收掉,如果进入老年代,则必须通过两次 FullGC 能力开释内存。本例中因为一直有新的虚援用对象在老年代生效,导致 FullGC 后,内存水位依然偏高,会加剧 GC 压力。新版本 JVM 已对此做了优化,一次 GC 能够回收掉;

5、相似的影响还有 finalize 办法;

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

结语

本文对数据库连贯生效引起的 GC 问题进行了详细分析,心愿读者通过本文对数据库连贯“保活”机制、GC 问题根本分析方法有所收益,后续该系列文章会持续推出其余案例分享。

作者:京东批发 王利辉

内容起源:京东云开发者社区

正文完
 0