分析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去做这件事。这个架构就会变成这样。

总结

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

相关推荐
AI人工智能+电脑小能手2 小时前
【大白话说Java面试题 第87题】【Mysql篇】第17题:分布式事务的实现原理?
java·数据库·分布式·mysql·面试
红尘散仙3 小时前
我把终端小说阅读器接上了 AI Agent:TRNovel 现在能用 skill 生成书源了
人工智能·后端·rust
卷毛的技术笔记4 小时前
告别硬编码!Spring AI Alibaba 实现 AI Agent 智能工具调用(Tool Calling)
java·人工智能·后端·python·spring·ai编程
会编程的土豆4 小时前
Go 语言反射(Reflection)详解
开发语言·后端·golang
喵个咪5 小时前
GoWind Toolkit Go后端代码生成 完整全流程实战
后端·go·orm
basketball6165 小时前
Go 语言从入门到进阶:4. 数组和MAP使用方法总结
开发语言·后端·golang
qq_2518364575 小时前
SpringBoot+Vue 共享电池柜管理系统 完整实现 前后端分离项目实战 完整代码
vue.js·spring boot·后端
zhangxingchao6 小时前
AI 大模型核心六:量化、Workflow 与 Agent、多轮 RAG
前端·人工智能·后端
键盘上的猫头鹰6 小时前
【MySQL 教程(八)】索引、事务、用户管理、导入导出与分页查询
数据库·python·mysql
IT_陈寒7 小时前
Vite打包时遇到的坑,原来问题出在这里
前端·人工智能·后端