概述
导语:看完《软件架构设计-大型网站技术架构与业务架构融合之道》这本书之后,一股脑灌到脑子里很多关于事务一致性的概念,比如说2PC、TCC。但是似乎只是留下了一个模糊的印象,对他们使用的场景,有什么区别没有清晰的概念,所以想借这篇文章,捋捋清楚。
数据库事务
对于数据库来说,事务就是一个“代码块”,这个代码块要么不执行,要么全部执行,事务要操作数据(数据库里的表),事务与事务之间会存在并发冲突,就好比在多线程编程中,不同的线程操作同一份数据,存在线程间的并发是一个道理。
数据库事务的四个特性ACID:
Atomicity(原子性): 事务是一个不可分割的整体,所有操作要么全做,要么全不做;只要事务中有一个操作出错,那么之前执行过的操作要全部回滚到事务开始前的状态
Consistency(一致性): 事务执行前后,事务从一个状态到另一个状态必须是一致的,比如A向B转账(A、B的总金额就是一个一致性状态),不能出现A扣了钱,但是B没有收到的情况发生
Isolation(隔离性): 多个并发事务之间相互隔离,不能互相干扰。这里的并发事务是指两个事务操作了同一份数据的情况;而对于并发事务操作同一份数据的隔离性问题,则是要求不能出现脏读、幻读的情况。
Durability(持久性): 事务完成(提交)后,对数据库的更改是永久保存的,不能再回滚
那么事务的并发就会导致如下的几类问题:
问题 | 描述 |
---|---|
脏读 (未提交读) | 事务A读取了一条记录的值,然后基于这个值做业务逻辑,在事务A提交之前,事务B回滚了该记录,导致事务A读到了一条脏数据 |
不可重复读(已提交读) | 在同一个事务里,两次读取同一行记录,但结果不一样。因为有别的事务在期间对这条数据进行了修改并且提交了 |
幻读 | 在同一个事务里,同样的select语句,执行两次,返回的记录条数不一样。因为再次期间有别的事务进行了insert/delete操作 |
丢失更新 | 两个事务同时修改同一条记录,事务A的修改被事务B覆盖了。举个例子,x=5,事务A、B同时把x读取出来,减1之后再写回,得到x=4,其实x正确的值应该等于3 |
针对上述的几类问题,数据库设置了不同的隔离级别,接下来以mysql为例,分析隔离级别是如何定义的:
名称 | 解决问题 |
---|---|
RU (Read Uncommited) | 相当于什么都没有做,上面的四个问题一个都没有解决,所以实际中这个隔离级别不会被采用 |
RC (Read Commited) | 解决了脏读问题,事务只能读到别的事务已经提交的数据 |
RR (Repeatable Read) | 解决了脏读、不可重复读、幻读问题,是innodb默认的隔离级别 |
Serialization | 序列化(串行化)解决全部问题 |
分布式事务
首先要抛出的一定是概念,什么是分布式事务?分布式事务和数据库事务从本质上看是相同的概念,它也需要满足事务的四大特性ACID。只不过分布式事务相对于本地事务而言表现形式有很大不同。数据库事务更多是在同一个数据库里通过加锁或者其他方式实现的,而分布式事务是指在多个不同的数据库中的事务。以一个典型的分布式事务问题–“转账”为例,如下图
如上图的例子,如果在支付宝余额扣减成功,但是通知余额宝失败,或者余额宝接到通知后执行余额增加时操作失败,因为两份(支付宝余额和余额宝)数据不在同一个数据库里,单纯的数据库事务就没有办法解决了。这个时候就出现了分布式事务问题。
所以简单总结来说,分布式事务和数据库事务的区别就在于,操作的是否是同一个数据库!
2PC
两阶段提交又称为2PC(two-phase commit protocol),2PC是一个非常经典的强一致、中心化的原子提交协议。2PC中有两类角色,协调者和参与者,具体到数据库的实现来说,每一个数据库就是一个参与者,调用方就是协调者。
第一阶段:准备阶段
协调者向各个参与者发起询问,参与者收到询问请求后,一般会打开本地数据库事务,然后开始执行数据库本地事务,但在执行完成后刽立马提交事务,而是向协调者恢复yes或者no,当然也有超时的情况存在。
第二阶段:提交阶段
如果第一阶段所有参与者均回复了yes,则协调者向所有参与者发起事务提交操作,所有参与者各自执行事务,然后回复ack。如果第一阶段有参与者回复了no或者超时了,那么这个时候第二阶段协调者就要发起回滚操作,让参与者均回到初始状态。
2PC 的问题
- 性能问题,在阶段1,资源锁定之后,要等所有参与者都返回之后,才能一起进入第二个阶段,不能很好的应对高并发场景。
- 阶段1完成之后,阶段2执行的过程中事务的协调者宕机,这个时候所有的参与者都收不到commit或者rollback的指令,将处于“悬而不决”的状态
- 阶段1完成之后,在阶段2,事务协调者向所有参与者发送了commit指令,但其中一个参与者超时或者出错了,那么此时其他参与者是提交呢还是回滚呢,也不能确定
3PC
三阶段提交又称为3PC,他在2PC的基础上增加了can commit阶段,并引入了超时机制。一旦参与者迟迟没有收到协调者的commit请求,就会自动进行本地commit,这样相对有效的解决了协调者的单点故障问题
相对2PC而言,3PC给协调者和参与者都设置了超时机制,这点避免了由于协调者宕机而造成的资源浪费。但是3PC似乎加重了性能问题,也没有解决数据一致性问题。
TCC
2PC、3PC 通常用来解决两个数据库之间的分布式事务问题。但是现在企业采用的是各种各样的SOA服务,更需要解决两个服务之间的分布式事务问题。为了解决SOA系统中分布式事务的问题,支付宝提出了TCC。TCC(Try Confirm Cancel),他是一个应用层面的的2PC协议,confirm对应2PC的commit操作,cancel对应rollback操作。
第一阶段:准备阶段
调用调用所有服务方提供的try接口,这个阶段各调用方做资源检查和资源锁定,为接下来的阶段2做准备。
第二阶段:确定/取消阶段
如果所有服务方都返回yes,则进入提交阶段,调用方调用各个服务方的confirm接口,各服务方进行事务提交。如果有一个服务在第一阶段返回no或者超时了,则调用方调用各服务方的cancel接口。
这里有个问题,TCC既然借鉴了2PC的思路,那么他是如何解决2PC的问题的呢?也就是说,在阶段2,调用方发生宕机,或者某个服务超时了,他是如何处理的?答案是:不断重试,不管是confirm失败了还是cancel失败了,都不断重试。这就要求confirm和cancel操作都必须支持幂等操作。
最后
以上就是单身白昼为你收集整理的2PC、3PC、TCC数据库事务分布式事务的全部内容,希望文章能够帮你解决2PC、3PC、TCC数据库事务分布式事务所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复