一、背景
Binlog 是 MySQL 中一个很重要的日志,主要用于 MySQL 主从间的数据同步复制。正是因为 Binlog 的这项功用,它也被用于 MySQL 向其它类型数据库同步数据,以及业务流程的事件驱动设计。通过研究分析,我们发现使用 MySQL Binlog 实现事件驱动设计并没有想象中那么简单,所以接下来带大家了解 MySQL 的 Binlog、Redo Log、数据更新内部流程,并通过对这些技术原理的介绍,来分析对业务流程可能造成的问题,以及如何避免这些问题。希望通过本文的解析,能够帮助大家了解到 MySQL 的一些原理,从而帮助大家能够更顺利地使用 MySQL。
二、基于 Binlog 的事件驱动
首先介绍一下系统设计。起初,我们的订单系统直接向 MQ 发送消息,通过异步消息驱动后续业务流程,以实现消息驱动的设计。大致的业务流程如下:
▼ 图:直接发送消息的订单事件驱动
这种设计需要保证数据库操作和消息操作的数据一致性,即数据保存和消息发送要不全部成功,要不全部失败。显然在数据保存前和事务中进行消息发送都是不合适的。我们是在数据更新操作后,数据库事务外发送消息。如果数据保存成功,但消息发送失败,支付系统需要重新通知(上图步骤1),直至通知成功。
这种设计虽然实现了功能和对可用性的基本要求,但存在如下缺点:
业务系统直接依赖消息中间件:消息中间件的故障,不仅会影响支付通知的处理,也可能影响业务系统上的其它接口。
业务系统必须实现可靠的重试:不论是请求发起方还是请求接收方,都必须实现可靠的重试,才能实现最大努力通知的目标。
重试间隔增大会造成业务延迟:随着重试次数增加,每次重试的间隔通常也越来越大,这被称为 Exponential Backoff(指数级退避)。这种设计能够让请求接收方的故障处理更加从容,避免因密集重试造成请求接收方服务难以恢复。但这样做可能会使请求接收方在恢复服务之后很长时间后才处理完积压的消息,从而造成业务延迟。我们可以采用类似 Hystrix 的自适应设计,在请求接收方服务恢复后回到到正常的请求速率。但这样的设计显然会复杂许多。
为了解决上述问题,简化技术架构,我们采用事件表的设计思想,将订单表作为事件表。通过订阅订单表的 Binlog,生成订单事件,驱动后续业务流程。在系统架构上,业务系统不用直接依赖消息中间件,只