您现在的位置是:网站首页 > 心得笔记
事务中嵌套 MQ 消息和 RPC 调用合适吗
数据库事务只能保证数据库操作的原子性(如 MySQL 的 InnoDB 事务),但无法控制外部系统的行为(如 MQ 或 RPC 服务)
事务成功提交,但 MQ 消息发送失败
事务提交失败,但 MQ 消息发送成功
我们所期望的事务原子性,就是操作要么全部执行成功,要么全部失败。以上两种情况,都将导致上下游数据不一致
解决方案
1.常用「本地消息表+定时任务」
在本地事务中,除了写入业务数据外,还要将要发送的MQ消息写入到mysql的消息表中。
而发送消息不再由业务代码决定,而是由后台定时任务来轮询「消息表」,定时发送消息。从而保证:
本地事务执行失败,不会发送 MQ 消息。因为消息表不会写入该消息(回滚),定时任务自然不会发送该消息了
本地事务执行成功,可以保证 MQ 消息一定能发送成功。定时任务查询到消息表的消息后,发送消息。如果出现失败,可以继续重试。当达到一定重试次数还发送失败,可以发送信息,让人工介入处理
如果允许数据存在一定的延迟,即不是「强一致」的场景,只需要保证数据的「最终一致性」的话,「本地消息表+定时任务」是一个非常好的选择,同时它也能解决「事务提交和消息发送的时序」问题
举例说明
电商场景中,用户支付成功后,在事务内:
更新订单状态为“已支付”(数据库操作)
发送物流服务的 MQ 消息(外部操作)
假设步骤 1 执行成功,步骤 2 执行失败,会出现——已支付,却不发货
假设步骤 1 执行失败,步骤 2 执行成功,会出现——未支付,期待收获
代码逻辑一 1.事务中操作①更新订单状态为已支付②写入本地消息表,状态为未执行 2.定时任务轮询本地消息表任务状态为已执行成功的任务,并操作步骤2
2.监听binlog
可以通过 canal 监听上游数据库的 binlog 日志,解析日志后发送到 MQ 中,由下游自行决定如何消费
3.分布式事务
分布式事务可以解决本地事务和 MQ 消息的原子性问题,但会带来可靠性、性能、使用成本等问题,给系统带来额外的复杂性。弊远大于利