1、基础理论知识篇“两阶段提交”如果你了解可以跳过这段,当然如果你想深入了解你可以购买相关书籍或去搜索相关资料阅读
两阶段提交分为 正常提交和异常提交或异常回滚
上面是正常提交的示意图,协调者发起预提交请求,参与者回复成功之后协调者再次发起commit请求,统一提交事物。事物结束。
如果这两阶段提交过程当中有任何一个请求出现异常就会回滚,如下流程:
异常请求包括预提交 返回预提交的应答,commit请求 等任何一个失败都会导致整个事物回滚。
二阶段提交的问题
“二阶段提交”还有一个很严重的问题就是如果commit过程当中失败了 就导致了全部事物失败,代价很大,简单粗暴的处理方式
还有一个问题是如果 commit过程中网络出现问题 commit没有被整个事物的参与者之一或者多个收到,这个时候就会出现数据不一致现象。
可能大家会提到 协调者是谁,参与者又是谁那?
这里简单说下自己的理解
如果在你的应用程序中你是通过 begin等相关操作语句开始的,比如 你使用了spring的@Transactional注解等,
那协调者就是你的“应用程序”,参与者就是 mysql或其他支持事物的数据库系统。
如果你就直接向mysql发送了一条sql语句mysql是自行提交的,那协调者和参与者都是mysql数据库自己
2、这里说下mysql对所谓的“重复数据”提供的相关sql或关键字。
unique 唯一主键约束
在sql事物中和应用程序中都可以捕获这个错误码或异常,可以作为幂等判断的一个依据。
upset 操作,发现唯一主键冲突然后更新相关数据,mongodb有直接使用的sql方法语句
示例:insert into tablename(column1) values(123) on duplicate key update column1 =column1 +123
ignore 忽略操作对于多余的操作直接忽略
insert ignore into tablename(column1) values(123)
基础篇说完很多内容如果想深入了解可以自己找资料处理。下面是华丽分割线
3、在我们原有的认知里有一个方案就差那么一点点就可以大面积使用的。
我们之前可能想过怎样既能发送mq又能写数据库,下面这个方案会分接近我们的愿望。
我们遵从如下步骤进行代码处理:
1、开启数据库事物执行相关sql
2、发送MQ消息
3、提交数据库事物
(注意:以上每一步都是在上一步执行成功之后在执行下一步的)
根据步骤我画出了下面的流程图
其实这个流程是有一个漏洞的,如果我把上面的流程图改造为下面的二阶段提交的示意图就会很明显的看出来
不知道大家有么有发现问题,是不是 各种提交和回滚操作都是针对的数据库,而不是MQ。commit数据库事物出现异常就会造成数据不一致现象。
其实也不用在想有没有其他的流程方案能解决分布式双写问题,只要存在多写问题就存在数据不一致问题的现象,
所以就出现了3pc Paxos 等协议来解决分布式事物/一致性的问题。
下面我们开始介绍怎么使用mysql和RocketMQ来实现事物问题
华丽分割线
4、RocketMQ事物消息的过程
1、发送MQ的事物消息
2、事物消息发送成功后会同步触发对应执行本地接口来执行针对mysql数据库的操作
3、如果有未commit的消息,RocketMQ 的 broker会定时间隔时间来回查数据库事物是否已经提交完成
5、结合RocketMQ的事物消息与Mysql数据库事物的实现思想
如果上面的二阶段提交你已经理解了,你会发现我这里设计的流程(上面图的流程)有点不太一样的地方
什么地方那?
MQ事物消息回滚的时候是因为mysql数据库事物没有提交成功而导致的,也就是说如果mysql数据库事务成功了MQ的事务消息是一定要成功的
否则就会出现事物不一致的现象。
假如发送MQ的prepare消息成功了,执行mysql事物的操作也成功了,但是偏偏返回给MQ的commit消息丢失了,那这个时候数据库消息并不会回滚。
所以就有了回查本地事物消息是否成功的操作,来对MQ的消息做个补偿动作实现数据一致性
理解了二阶段提交以及RocketMQ的事物实现之后你就可以自己设计事物相关操作的执行顺序了
(这里的流程设计以及包括我的代码实现是以我的理解做出的最佳实践)
6、RocketMQ与Mysql事物结合注意事项
1、如果应用程序承担协调者的工作就尽量晚开启事物和尽量早的提交数据库事物,事物中的sql对数据竞争多的sql尽量靠后
因为执行数据库事物会有各种锁操作,减少锁的生命周期,数据库是稀缺资源,大家能省则省
2、数据库事物最好设置超时时间,超时之后自动解除,最好不超过1分钟
3、MQ默认1分钟之后回查一次已发送message但未commit的消息,最多回查15次,之后会执行回滚操作
4、应用程序一定要做好幂等处理(可以参考上面mysql相关语句实现幂等接口)
5、网络不要太差,否则会造成大量的重试,重试就会影响消息的及时性
6、适用场景
单次请求数量小
每次请求会有数据产生,而不是查询产生的数据(比如 insert操作叫生产数据,select操作不要生产数据)
下游可以接受一定的延迟(这里有两个因素,有应用程序本身和Broker,这里指broker)
下游服务或系统以接收到的消息为依据做响应的操作
MQ消息作为主要信息进行传递
==================================================================