URL
date
AI summary
slug
status
tags
summary
type
背景
一张数据量3亿级别的表需要增加一个冗余字段,并且这个字段需要根据原始表的数据做初始化。因为涉及的数据量过大,为了防止性能问题,开发给出的方案是写代码刷数据,单线程,并且控制每次更新数据量的大小。这看起来已经规避了一定的风险。殊不知,一旦涉及的数据量过大,任何环节都有可能存在风险。
按照刷数据程序运行的速率,刷完3亿多条数据差不多要8个小时左右,从晚上7点多开始,开发观察了一段时间,确认逻辑上没什么问题,就让程序自己慢慢地执行了。但是到了半夜的时候,kafka所在机器的磁盘开始告警,不过这个时候已经是深夜了,并且这块告警也没有升级到开发的机制。又过了一段时间,kafka所在机器的磁盘就写满了,并且这台机器上还运行着RocketMQ(核心消息队列,各个微服务之间解耦的关键),这导致了当时MQ消息全部发送不成功。
处理
运维收到告警,紧急扩容了磁盘大小,恢复了服务。并且观察到了空间占用较高的kafka topic,运维把该topic的消息保留策略的时间缩短了。开发同步开始排查问题原因。不过由于以下两个原因,对线上服务没有造成影响:
- 时间点比较早,发生问题的时候,是早上5-6点左右,用户量和请求量较小
- 我们应用发MQ都是异步的,是通过统一组件封装,采用的是本地事务消息表的方式发送的MQ消息。所以对业务操作没有影响,只是会导致消息延迟。
问题分析
虽然没有造成线上故障,但是还是要敬畏生产,珍惜每一次的生产问题,好好复盘。其实问题原因已经很清楚了,就是因为
- 更新的数据量非常大(3亿+)
- row模式下的binlog默认(binlog_row_image=ful)会记录变更前后的所有字段的值,所以binlog的量也会很大
- 这张表的数据变更有业务场景需要监听,所以通过Canal监听binlog转存到了kafka里
这就导致了kafka里某个topic(对应这张表的topic)的消息量暴增,然后磁盘就不够了(但是整个过程是平缓增长的)。下面这张图里的260G是调整后保留的6个小时的日志量。调整前应该更大。
磁盘扩容解决了上面的问题,但是我们又发现了一个新的问题:canal-server消费binlog产生了大量的延迟。
可以看到delay这条线在12点前都是一直往上涨的。所以此时,如果有订阅binlog往es同步的相关表数据的变更,那么同步的延迟就会非常长,因为canal-server消费binlog是顺序的。
而产生delay的原因,就是因为消费速度跟不上生产速度。当时的消费速度主要卡在什么地方呢?感觉上应该是卡在
CanalMQStarter
这一层。也就是canal-server内置的client,它主要负责从canal-server的store队列里批量消费对应的event,然后投递到对应的MQ里。我们以kafka为例,CanalMQStarter
的机制是每次批量取50条消息,而发送到kafka是异步的,每个消息都会调用send方法,只往buffer里写,达到一个batch的量(默认16k)或者是到了一定时间(liner.ms)或者是整个一批消息消费结束,才会触发往kafka broker发送,并且发送消息的是单线程。我估计消费是卡在这个层面上,应该可以通过调整一些个参数来提升消费速率,因为当时看kafka整体的瓶颈还没有到。不过,这里我采用了一种更激进的办法,因为其实这张表的消息并不是一个核心业务场景使用的,是我们用来做冗余数据比对用到的,所以丢了也不要紧。那在这个背景下,我们直接修改了instance的配置,直接不订阅这张表的binlog,那么在消费到这张表的binlog的时候,会直接跳过,也不会和kafka交互。于是,你就看到了上面监控图里那个急转直下的拐点了。
等delay降下来之后,我们在把这张表的订阅加上。至此,这个问题才算彻底解决。
深入分析
这里主要是解答一下自己从中产生的一些疑问,也想从更多的角度来分析下
问题1:kafka broker有3台,为什么貌似就只有一台磁盘满了
可以看到
- 只有第三台机器(10.10.1.36)的磁盘使用率满了,其他两台都没有到100%。
- 3台机器的磁盘使用率曲线都是先平稳上升,然后会稳定在一个小范围内
- 6点多的断崖式下跌是因为扩容和调整了保留策略
我们先来解释第二个现象,这个现象应该是因为保留策略。刚开始,大量的写入,磁盘使用率肯定是稳步上升的,到了某个时间点,保留策略保留到了开始写入的那个时间点,写入和失效几乎稳定在一个量级了,所以就平稳了。
那么为什么只有第一台满了呢?答案就是它被分配了更多的partition。我们从下图里能看到这个topic的partition分布在各个broker的情况
可以看到,10.10.1.36这个broker被分配了6个partition,而其他2个只有5个。注意,这里as leader和as follower如果在同一个broker上有相同的partition,肯定只会保存一份。因为保存多了也没意义。
思考
我感觉这个看似不经意的刷数据问题,其实隐藏了不少的”玄机“,是不少初学者很容易踩到的坑
超大数据量的变更到底应该怎么做?
可以看到我们的开发同学选择的是刷数据,可以通过代码控制一次变更的量。我们不用SQL刷数据无非就是SQL要手动做拆分比较麻烦,但是如果这个功能平台化了其实就不需要写代码了。比如阿里云的DMS上就有提供类似的功能
超大数据量的变更还应该考虑什么?
超大数据量变更的场景,各个环节都有可能隐藏着风险,所以一定要考虑到位,慎之又慎。比如这里没考虑到的产生大量binlog,由于监听binlog产生大量的kafka消息等等。包括会不会因为变更量太大产生大量的同步延迟?这些都应该在考虑的范围之内。
监控告警是不是完善?
其实如果监控告警完善的话,没有考虑到的问题,理论上来说都可以及时发现。监控告警是最好的兜底机制。比如像canal-instance相关的延迟的告警、kafka的写入量突然上升的告警。我们都无需等到磁盘满了才感知到。
- 作者:黑微狗
- 链接:https://blog.hwgzhu.com/article/update-big-table
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
相关文章