2pc 事务 2段式提交的原理是一 资源管理器 管理着一群长事务。
1 资源管理器告诉每事务管理器都做数据修改以后,不提交单前事务,并且告诉 rm 我完成了,可以提交了
2 如果 有一个事务 失败,那么 大家因为都还没提交,好吧,失败事务通知 rm ,让大家一起回滚
3 如果大家都 走到 处理完逻辑,走到 提交这里,rm 收到 全部的 事务都可以提交了, 那么 rm 所有数据库事务提交。
4 这里有一个明显的问题,批量通知 事务提交的 会不会出被中断( 数据库到了提交这一步,没有逻辑执行我们认为他不会抛出异常,但是可能rm 死了,部分rm 没通知)
5 所以 rm 必须搞可用,死了一个 rm 还有 别的rm 来通知。
6 rm 高可用了,数据库死了怎么办,这个就需要 数据库的支持了,2pc 不是所有数据库都支持,数据库大有 一个事务日志,写硬盘上的,等他起来以后,rm 通过这个 事务日志,重新提这个事务就可以了。
缺点:很明显,事务时间长,锁定力度大,并且需要最后的一步提交需要数据库的支持。
TCC try 的时候 预处理资源,并且这个预留资源的操作在 try 结束时会提交事务的。
1 如果部分try 成功,那么后面的 try 也就别做了,失败的try 回滚,前面成功的try 执行 cancel( cancel 使用用来取消 cry 的 资源锁定的 ),并且 cancel 要是幂等的。
2 如果所有的 try 都成功了,那么就 comfirm, comfirm 因为 try 里面预留了资源,所以主观理论 的认为 comfirm 不会失败(编码问题不算),comfirm 也要实现幂等。
3 如果程序正常执行那么,肯定数据就会一致性了,那么 为啥 comfirm cancel 需要幂等呢?
4 如果 部分try 失败,当前try 回滚(单前的 try 还没提交,可以回滚),前面的try 已经提交了,会调用 cancel 来取消 这是一个不同节点批处理,只有全部成功才成功,如果失败了一个或者以上(比如 RM 调用 部分 cancel 以后挂了 ,或者 部分cancel 代码问题异常)。那么 这个分布式事务的状态就不是完成。 然后 资源管理器 活了,或者 过一会再调用 这个事务 失败的cancel。这里有些人就有疑问了,rm 应该知道哪些失败了呀,只调用它们不就完了吗?这就说到另一个网络问题,网络通讯,收到 处理成功我们认为他一定处理成功了,如果没收到成功,它 可能也已经做了( 处理方做了,响应你时候失败了,或者说响应超时)所以,失败了可能已经做过一次,只有成功的才是成功。
5 上面的 cancel 过程。rm 管理的 分布式事务状态取消,那么这个理论上可以回到最初的 数据状态。
6 在说 comfirm 的时候。同理 有一群 需要 批量 的 comfirm ,这个也会失败呀,处理办法和cancel 一样。 失败了这个分布式事务就不是完成状态。如果失败的 comfirm 就要重新再调用一次。即便寄到哪些节点失败,网络通讯也会有重复调用问题,所以需要幂等。保证每一个comfirm 都成功了,也保证即便 网络原因 认为失败调用了多次,也是数据一致的。
综上 tcc事务 ,try,comfirm ,cancel 都是独立的小事务,并且也可以保证 数据的一致性。不需要数据库的特殊支持,有事务就行。
参考框架: ByteTCC、Himly、TCC-transaction
通过 消息来实现 (本地消息,或者独立消息)备注这个消费服务器,可用用基于 消息队列(MQ) 来实现,也可以不用,这里的 消息服务器是处理 分布式事务消息的,不是指的 MQ。
1 本地有一个消息表,和本地事务处于 同一事务中。处理调用别的服务的时候,吧这个消息和本地事务一起持久化到本地数据库。( 本地事务数据处理 和消息 是同一个事务,强一致性)
2 事务提交的时候,把个消息发送出去,并且消息 标记成已发送,未完成。
3 消费服务器 标记 收到这个消息,正常响应 A 以后,A 标记这个消息 一成功发送。这样A 就可以不管了,也可以保留一个 B 消费完成的状态。
4 消息服务器,可能 多次收到这条消息,所以消息要有唯一Id,消息服务器这边只保留这个消息一份。
5消息 服务器收到消息,让B 去消费这个消息,如果 B功消费了这个消息,那么标记这个消息完成,可能需要回复A,也可以不回复。B如果处理失败,消息服务器应该重发。
6 因为 B 的消息可能重复 ,那么B这里一定要是幂等的。
7 面 大致的流程,我们看看看 消息发出 以后的一些控制,A 在事务提交的时候 发出这个消息,发送消息这个 方法可以发送失败,但是必须不能影响 主事务的回滚( 网络环境发送失败,不一定真的没发出去)
明显,这个种效率比较高,不依赖任何 资源管理器,都是依赖的本地事务,和分布式锁。 编码不用写 3 边,但是 他不能同时回滚,只能保证最终一致性。不求同年同月生,但求同年同月死。
分布式事务框架类似tx-lcn
1 txlcn 有个 是管理中心,在 A 调用B的 时候,会吧 A 的本地事务,和B的 本地事务加入到 txm 管理的 事务组中,如果 A,B 执行完成以后都不会 提交。
2 然后 txm 会统一 通知 A,B提交 。这个 其实 这个 2pc 的事务很像了。 txm 可以支持高可用。 避免 txm 通知了A 提交,没有通知到B提交。
3 txm 改写了 spring 的服务请求调用方式,在 服务调用的时候,带上了当前事务的Id,通过出售事务Id,然后续事务都加入到同一个事务组。
4 和2px 一样的 ,txm 保证一定会通知 事务提交,但是 如果 数据库挂了,或者 服务节点挂了,就会出问题,这个不清楚 txm 是怎么处理的。如果是依赖于 数据的 事务日志,感觉意义不大。 更大可能是 txm 知道 调用的那个 服务接口,在服务接口实现幂等的情况下,在调用一次。让B完成。
阿里正在开发的seata
大概原理是这样:TCC的 变种,吧 cancel 的内容 和 写到 try 的 事务里面。这样cancel 就 可以免写了。即便 try 提交了,后面也可以容易的回滚。
显然这需要改数据库驱动的实现了,对数据库底沉认识不够深入是没法做的。
部分思想是我推测的,没有验证,后面有时间的话,我会每个 方式都做一个 简单的案例,并且验证。