现象,第一步

同步任务的http接口查询任务列表接口突然变卡,很久才相应,进入服务器后执行top命令发现CPU已经爆满。

top (查看进程占用资源)

机器是4核,所以占用了400%

mysql 内存占用超过配置阈值 mysql内存占用越来越大_jvm

top -H -p 276965 (查看进程ID下的子进程占用资源)

4个子线程,每个各占了100%

mysql 内存占用超过配置阈值 mysql内存占用越来越大_mysql 内存占用超过配置阈值_02

printf “%x\n” 276970

jstack 276965 | grep 439ea -A 100

通过查看当前线程执行的详情,发现输出的是gc线程

jmap -heap 276965

jmap的使用方式:blog.csdn.net/heihaozi/ar…

查看jvm当前的堆内存情况,老年代、新生代皆已满

mysql 内存占用超过配置阈值 mysql内存占用越来越大_mysql_03

上面的命令具体怎么用参考这篇blog:blog.csdn.net/cainiao1412…

第二步

既然是堆内存满了,此刻就要拿到整个堆内存的快照,执行

jmap -dump:live,format=b,file=heap.dump 276965

下载到本地使用 VisualVM 打开,有很亮眼的TableMapEventData,但是此时还是不清楚何处引起的泄露。

VisualVM的使用方式:blog.csdn.net/weixin_4546…t.zoukankan.com/wangzun-p-1…

mysql 内存占用超过配置阈值 mysql内存占用越来越大_数据_04

此时使用工具MAT继续打开dump,这里看就比较明显,是一个名为MySql…EventSource类中的tableMap…ByTableId的一个HashMap巨大,

MAT的使用方式:zhuanlan.zhihu.com/p/482860374 , blog.csdn.net/w2009211777…

mysql 内存占用超过配置阈值 mysql内存占用越来越大_mysql 内存占用超过配置阈值_05

第三步

根据MAT的提示,找到该处代码,可以看见在收到TABLE_MAP这个Event事件时,会将数据放到HashMap中

mysql 内存占用超过配置阈值 mysql内存占用越来越大_mysql_06

数据既然存,就也需要清理,在源码中只找到了一处清理该Map的地方,当收到ROTATE事件时,清理Map,那么由此可见如果不收到ROTATE事件,Map将会无限增长

mysql 内存占用超过配置阈值 mysql内存占用越来越大_mysql 内存占用超过配置阈值_07

什么是ROTATE事件?

参考:www.freesion.com/article/125…

也就是说我们的Map会等到binlog文件大小达到 max_binlog_size 的值时,才会进行清空,或者手动执行flush logs,但是生产环境中一般不会去手动执行,max_binlog_size 的值我们的库默认为 256MB。

mysql 内存占用超过配置阈值 mysql内存占用越来越大_mysql 内存占用超过配置阈值_08

什么是 Table Map Event?

意思就是 Table Map Event 会存储表的元数据,例如数据库名、表名、字段等,后续的Row Event会通过table id 和其关联,取到对应的元数据,因此这个存储是有必要的,

但是table id会随着缓存淘汰、刷新等机制出现变化,也就是说同一个库的同一个表的table id会不停的变化,因此table id并不是唯一的,所以我们的Map会随着程序的运行可能存储很多明明是同库同表,但是因为table id不同导致存map中很多重复的数据,导致占用内存越来越大。

mysql 内存占用超过配置阈值 mysql内存占用越来越大_数据_09

mysql 内存占用超过配置阈值 mysql内存占用越来越大_jvm_10

解决方式

Debezium对mysql binlog同步默认使用的是 github.com/osheroff/my… , 这个库很久没有更新了,

但是Flink CDC使用的也是 Debezium,也就是说也依赖了这个库,所以可能也会存在这种问题,

两种解决方式,一是增大内存,均匀任务,让内存足够存储这么多的冗余数据,二是修改源码,在特定时机,例如新的table id过来时,将老的table id对应的数据清除?

针对binlog的同步,还有一个工具叫做maxwell,在生产环境中使用并没有出现这种问题,追踪了一下源码,也是类似的原理,但是maxwell使用我们使用了docker做资源隔离,每个进程给了2g,而Debezium任务则跑在同一个jvm中,因此更多倾向于均匀分配一些资源。

最终解决方式

我们的系统采用的版本是1.9.0.Final,在查看其源码时,在 MySqlStreamingChangeEventSource 类中,针对 ROTATE 事件发生时,并没有对map进行clear处理,所以导致了内存泄露,为了避免升级版本影响过大,我们采用了修改源码的方式,针对ROTATE发生时,对map进行清除。

mysql 内存占用超过配置阈值 mysql内存占用越来越大_jvm_11

mysql 内存占用超过配置阈值 mysql内存占用越来越大_mysql 内存占用超过配置阈值_12