分析MySQL数据变更增多导致CDC服务变慢的问题

背景

在之前的文章中有提到过,维护一个CDC模块是我工作 ( 饭碗 ) 的一部分, 所谓的CDC呢,就是Change Data Capture, 变化数据捕捉,他能捕捉到类似MySQL这样的存储模块的数据变化,并且发送到别的地方,比如Kafka,ElasticSearch。在这个领域内比较成熟的开源项目有Debizium,阿里的Canal,Flink-CDC。我们的是用Go开源的框架 go-mysql 提供的 CDC 功能搭建的平台。最近呢,这个平台又开始作妖了。

发生了什么事情呢?让我来抽象一下这个case,比如其实你的数据库中有30张表,但是你只关注其中的20张表的数据变动,但是这20张之外的一些表的数据变动量如果是这20张的10倍。你该怎么办。这时候你会说了,MySQL可以配置只写某些表的binlog,这个问题很轻松就可以解决啦。是的,不过很明显,因为一些原因,我们没有做这样的配置。我们正是面临着这样的问题,这个问题导致我们这个服务的内存被打趴下,同步数据的 lag 也大大延长了。

作为技术狂魔的我,当然会开始研究研究这个东西啦。除了追踪这些数据变动的来源之外,我当然会探究一下有没有一些技术性的解决方案来解决这个问题啦,毕竟这样可以一劳永逸。这片文章记录下我的探究结果,以及一些个人的想法。不当之处还请指教。

为什么会变慢呢?

这里首先要向大家解释一下MySQL CDC一般实现是怎么样的,一般来说MySQL的数据变动都会写进一个叫binlog的东西里面,然后MySQL的主从同步实际上就是同步这个binlog,那么很显然,CDC服务就是把自己伪装成一个MySQL的从属节点,构建长链接,然后让MySQL向你同步数据。

一开始我以为问题是因为我们收到了我们不需要的表的变动,占用了大量的TCP缓存。才导致的内存变高。但是细想之下,这个想法其实是力不足脚的。因为TCP的buffer的生命周期是,从发送端收到这个packet,到应用层读取这个packet,换而言之,等到我们的程序从Linux内核读取了这个packet,这个buffer就可以被回收了。 所以关键的地方是我们读取了这个东西,然后造成了内存高涨,并且一直居高不下,因为一直收到那么多MySQL的变更事件,即便是 go 的 GC 看到这种情况也有点有心无力。

深入看这个开源项目的源码实现就会发现 (源码太长,我这里就不展示了), 他们并没有针对一些table做特殊处理,无论什么数据都会decode出来。虽然我们在程序中会判断是哪张table的变更,如果不是我们关注数据变更的就不要了。但是走到这一步的时候他已经帮我们把所有数据都decode出来了,剩下的就是把所有东西都摆在餐桌上供我们挑选。

如何解决这个问题呢?

这个时候我很自然的想到了,如果可以在这个开源项目内部就不解析我们不关注的表,那不就完事了嘛,那这片内存的生命周期最多就到从网络空间里拿出来,然后再到这个判断里面,如果用户配置了不需要这个东西,那就拜拜了您嘞。多么美妙啊。

于是我直接去提issue了。想问下可不可以直接不关注一些table的变更,如果不行的话,我找个业余时间给他们贡献一下也是可以的。

因为之前给也给这个项目提过issue,知道对面是个中国人,所以直接用中文提了,但是这个老哥第二天回复我让我翻译成中文,hhhhh。

他的回复大致意思是这样,他们之前也遇到过这样的场景,所以做了一个东西,rowsEventDecodeFunc,这是一个回调函数,允许用户自定义如何处理所有接收到的变更。另外他给我附上了他们的一个数据同步项目 tiflow(没错,就是tidb的数据同步项目)的一个使用案例。

这个case的大致意思是如果这个表在blocklist里面,就不decode出来了。这个正好是我想要的效果。其实到这里这个问题基本上就解决了。

我的一些想法

但是我想提一下我的一些想法,很明显,给用户开放这样的一个回调函数,我感觉太重了,如果看不到这个使用的case,我都不知道怎么用他。比较好的做法我感觉可能是需要在这个基础上再封装一次。给用户一些比较清晰的语意配置。这个我有时间想做做,给他们提个PR,岂不是美滋滋?

另外这个只是针对了一个场景,也就是说用户并不想处理所有数据库的变更的场景。但是用户如果想处理所有数据,但是内存不够怎么办?很明显CDC平台是IO密集型的服务,从数据库接受信息,然后稍微处理一些转发给下游。一般这种情况下CPU的使用率不会很高。所以我感觉这种场景下,可以加一条本地的队列,这个队列的信息是存在磁盘里的,先把从数据库来的数据写入到磁盘里,然后读出来消费。另外需要一个第三方的东西记录你的处理到的数据。因为如果服务崩溃了,你需要这个位置做恢复。虽然这样会慢一些,但是CDC的平台其实对 lag 要求不是很高。这个是我比较放飞的想法啦,可能会存在很大的问题,只是分享给大家看看,大家看看笑笑就好。

当然最治本的方法是让MySQL只写那几张你关注的表的binlog。可以开一个专门的replica去做这件事。这个架构就会变成这样。

总结

好了,感谢你还看到最后,这里分享了一下在工作上对一些问题的想法和探究过程。希望你看了之后有所收获。感谢。

相关推荐
程序员爱钓鱼28 分钟前
Go语言实战案例-项目实战篇:新闻聚合工具
后端·google·go
IT_陈寒29 分钟前
Python开发者必须掌握的12个高效数据处理技巧,用过都说香!
前端·人工智能·后端
一只叫煤球的猫9 小时前
写代码很6,面试秒变菜鸟?不卖课,面试官视角走心探讨
前端·后端·面试
bobz9659 小时前
tcp/ip 中的多路复用
后端
bobz9659 小时前
tls ingress 简单记录
后端
你的人类朋友11 小时前
什么是OpenSSL
后端·安全·程序员
bobz96511 小时前
mcp 直接操作浏览器
后端
程序新视界12 小时前
MySQL中什么是回表查询,如何避免和优化?
mysql
前端小张同学13 小时前
服务器部署 gitlab 占用空间太大怎么办,优化思路。
后端
databook13 小时前
Manim实现闪光轨迹特效
后端·python·动效