写点什么

基于业务表 Binlog 的事件驱动设计

用户头像
理帆
关注
发布于: 2020 年 06 月 26 日
基于业务表 Binlog 的事件驱动设计

我们的订单事件服务是事件驱动设计在订单领域的一个实践。订单系统将订单表 Binlog 作为事件源,通过基于 Canal 的 Binlog 服务转换为 MQ 消息,然后我们的订单事件服务将消息转换为订单业务事件,驱动后续业务流程。



接下来给大家介绍一下此设计方案的优点和在实践过程中需要注意的细节。



一、优点



这么设计好处有以下几点:



  1. 简化事件设计,集中事件生成规则

  2. 简化数据一致性设计

  3. 提高前台业务服务稳定性



接下来分别介绍一下。



简化事件设计,集中事件生成规则



在之前的设计中,订单事件是由前台系统直接在订单数据更新成功后发出。通过业务代码判断订单事件类型,不同的接口,不同的系统都会耦合不同的事件生成规则。这导致底层的订单事件生成规则不统一,存在重复的问题。这些问题在引入订单事件服务,通过 MySQL Binlog 消息生成订单事件后得以解决。订单事件生成规则集中到了订单事件服务中,可维护性和规则清晰度都有明显提升。



简化数据一致性设计



如果让业务在更新数据后直接发消息,需要考虑如何保证数据库和 MQ 的数据一致。如果数据库事务提交成功,MQ 发送失败,怎么办?通常的方法是重试,实现最大努力通知。但重试也有细节问题,同步重试容易导致服务线程挂起时间过程,异步重试又需要考虑如何保持重试任务。当然这些都有办法解决,但通常代价都是严重增加了业务系统设计的复杂度。



因此,通过使用使用 Binlog 驱动的事件设计,可以降低前台业务系统在这些方面的设计难度,将技术细节和优化工作下沉至底层。



提高前台业务系统稳定性



前台业务系统直接依赖 MQ 在当时使用 ActiveMQ 时更为明显,ActiveMQ 服务不稳定,抗消息积压能力差。前台业务系统直接作为消息 Producer 依赖 MQ Broker 势必会降低系统整体的可用性和稳定性。



二、需要注意的细节



但使用订单表的 Binlog 作为事件源的设计方法有几个技术细节需要注意。这些细节不注意就很容易引起线上问题(别问是怎么知道的)。



第一,数据库运维所带来的影响



第二,注意 Binlog 写入和事务生效时机



第三,注意链路上各服务的稳定性和可用性



接下来详细介绍这三点



数据库运维带来的影响



直接使用业务数据表作为事件表虽然简化了业务系统开发工作,但是增加了运维工作潜在影响的可能。当数据库需要增加字段、增加索引时。传统的 MySQL alter 语句显然不能使用,因为这个会导致锁表,影响线上服务。常用的方法是使用如 GitHub 开源的 gh-ost 工具、Percona 开源的工具,将整个数据表拆分为小块进行运维工作。但这样会导致每条数据都会产生一条 Binlog 数据,最终导致巨量 Binlog。具体来说,在做分库分表之后,最近一段时间内,每个表依然有百万级的热数据,如果短时间内完成运维工作,将导致大量 Binlog,从而加重 Binlog 消息系统负载,使得真正的业务 Binlog 消息无法及时处理,导致业务延迟。因为会员订单业务要求用户支付成功后秒级完成权益开通,所以这样的延迟极易导致线上保障。



解决的方法是数据库运维平台增加了数据表分块运维的间隔,降低了速度,减缓 Binlog 产生速度。同时,Binlog 消息平台做出相应优化,加快处理速度。目前新版的 Binlog 消息平台速度比过去提高了两倍。



但降低运维速度又产生新的问题,那就是加索引加字段可能需要一天的时间才能完成,有时对业务还是不容忽视。



解决这个问题有两种方法,控制业务表数据量和使用专门的事件表。此外在大厂自研的数据库也有一些解决方法,不过细节我暂时不清楚,了解的同学可以给我留言。



Binlog 写入和事务生效的时机



这个问题之前专门写文章介绍过,这里在重复一下。订单事件发出后,后续业务系统通常会再次检查订单状态,保证业务逻辑的严谨。但在事件中,我们发现过订单事件发出后,当后续系统检查订单状态时,发现主库中订单状态还是之前的,导致业务处理被跳过。



这个问题的原因是 MySQL 写 Binlog 和 InnoDB 引起更新数据页是整个事务提交动作中的两步,有前后关系。所以实际当 Binlog 发出后,数据页的内容有可能尚未变更,导致后续系统查到的数据依旧是旧的。



这可以通过在业务状态检查上适当增加重试解决。



注意链路上各服务的稳定性和可用性



在使用基于业务表 Binlog 的事件驱动设计后,整个业务链路增加了一个 Binlog 平台和一个 MQ,整个链路更加复杂。理论上依赖越多,可靠性越差,因为整体可靠性是每个分部可靠性的乘积。



为了解决这个问题,我们又为关键的订单业务处理增加了不依赖订单事件的备份通路,保证当订单事件机制出现技术问题时,核心流程依旧可使用备份通路完成。为保证备份通路和正常通路的可用性和业务逻辑与基于订单事件的处理保持一致,两者都是基于相同的系统,只是接受数据的方式存在不同。



三、如果重新设计



如果重新设计订单事件驱动设计,我会考虑将订单创建、更新操作从业务系统下沉至订单平台中的订单核心服务。在其中引入订单事件表,使用 JSON 存储数据,引入基于订单号的局部消息顺序设计,加强数据一致性。具体设计这里就不详细介绍了,欢迎大家交流讨论。



发布于: 2020 年 06 月 26 日阅读数: 71
用户头像

理帆

关注

表达是最好的记忆 2007.07.06 加入

互联网后端架构师

评论

发布
暂无评论
基于业务表 Binlog 的事件驱动设计