分布式事务

1.本地事务ACID的保证

A:原子性,C:一致性,I:隔离性,D:持久化

原子性:依靠undolog,保证的,失败时成功回滚。

隔离性:数据库支持四种隔离级别,读未提交,度已提交,可重复读,串行化。mysql的隔离性是通过,多版本并发控制(MVCC)+ 行锁实现的。

多版本并发控制(MVCC)的底层实现:

i)记录的三个隐藏字段
1.DB_TRX_ID:最近修改的事务id,事务id递增的
2.DB_ROLL_PTR:指向当前记录的上一版本。配合undolog
3.DB_ROW_ID:数据库的主键,如果没有,innodb会生成一个row_id(6字节)

ii)undolog 回滚日志

iii)read view
read view 可读视图;在RC隔离级别下,是每个快照读都会生成并获取最新的Read View,而在RR隔离级别下,则是同一个事务中的第一个快照读才会创建Read View,之后的快照读获取的都是同一个Read View.

针对当前版本的数据,当前事务中我是否能看到?这就依据readview的可见性算法。

Read View的可见性规则如下所示:

​ 首先要知道Read View中的三个全局属性:

​ trx_list:一个数值列表,用来维护Read View生成时刻系统正活跃的事务ID(1,2,3)

​ up_limit_id:记录trx_list列表中事务ID最小的ID

​ low_limit_id:Read View生成时刻系统尚未分配的下一个事务ID

​ 具体的比较规则如下:

​ 1、首先比较DB_TRX_ID < up_limit_id,如果小于,则当前事务能看到DB_TRX_ID所在的记录,如果大于等于进入下一个判断

​ 2、接下来判断DB_TRX_ID >= low_limit_id,如果大于等于则代表DB_TRX_ID所在的记录在Read View生成后才出现的,那么对于当前事务肯定不可见,如果小于,则进入下一步判断

​ 3、判断DB_TRX_ID是否在活跃事务中,如果在,则代表在Read View生成时刻,这个事务还是活跃状态,还没有commit,修改的数据,当前事务也是看不到,看不到的话,就根据DB_ROll_PRT和undolog找到上一版的记录,重新判断;如果不在,则说明这个事务在Read View生成之前就已经开始commit,那么修改的结果是能够看见的。

持久化:redolog+binlog。并且两者通过两阶段提交方式保证数据库宕机不会丢失数据的问题。

2.全局事务

2.1 单服务多数据源的场景

XA 协议,为了解决分布式事务的一致性问题。这个框架的核心内容是,定义了全局的事务管理器(Transaction Manager,用于协调全局事务)和局部的资源管理器(Resource Manager,用于驱动本地事务)之间的通讯接口。

XA 并不是 Java 规范(因为当时还没有 Java),而是一套通用的技术规范。Java 后来专门定义了一套全局事务处理标准,也就是我们熟知的 JTA接口。它有两个最主要的接口:
1.事务管理器的接口:javax.transaction.TransactionManager,这套接口是给 Java EE 服务器提供容器事务(由容器自动负责事务管理)使用的。
2.它还提供了另外一套 javax.transaction.UserTransaction 接口,用于给程序员通过程序代码手动开启、提交和回滚事务。满足 XA 规范的资源定义接口:javax.transaction.xa.XAResource。任何资源(JDBC、JMS 等)如果需要支持 JTA,只要实现 XAResource 接口中的方法就可以了

两段式提交
XA 将事务提交拆分成了两阶段过程,也就是准备阶段和提交阶段。

准备阶段,又叫做投票阶段。在这一阶段,协调者询问事务的所有参与者是否准备好提交,如果已经准备好提交回复 Prepared,否则回复 Non-Prepared。这里的“准备”操作,其实和我们通常理解的“准备”不太一样:对于数据库来说,准备操作是在重做日志中记录全部事务提交操作所要做的内容它与本地事务中真正提交的区别只是暂不写入最后一条 Commit Record。这意味着在做完数据持久化后并不会立即释放隔离性,也就是仍继续持有锁,维持数据对其他非事务内观察者的隔离状态。

提交阶段,又叫做执行阶段,协调者如果在准备阶段收到所有事务参与者回复的 Prepared 消息,就会首先在本地持久化事务状态为 Commit,然后向所有参与者发送 Commit 指令,所有参与者立即执行提交操作;否则,任意一个参与者回复了 Non-Prepared 消息,或任意一个参与者超时未回复,协调者都会将自己的事务状态持久化为“Abort”之后,向所有参与者发送 Abort 指令,参与者立即执行回滚操作。

“准备”和“提交”这两个过程,被称为“两段式提交”协议。那么,使用了两阶段提交协议,就一定可以成功保证一致性吗?也不是的,它还需要两个前提条件。

第一,必须假设网络在提交阶段这个短时间内是可靠的,即提交阶段不会丢失消息。同时也假设网络通讯在全过程都不会出现误差,即可以丢失后消息,但不会传递错误的消息,XA 的设计目标并不是解决诸如拜占庭将军一类的问题。两段式提交中投票阶段失败了可以补救(回滚),而提交阶段失败了无法补救(不再改变提交或回滚的结果,只能等崩溃的节点重新恢复),因而提交阶段的耗时应尽可能短,这也是为了尽量控制网络风险的考虑。
第二,必须假设因为网络分区、机器崩溃或者其他原因而导致失联的节点最终能够恢复,不会永久性地处于失联状态。由于在准备阶段已经写入了完整的重做日志,所以当失联机器一旦恢复,就能够从日志中找出已准备妥当但并未提交的事务数据,再向协调者查询该事务的状态,确定下一步应该进行提交还是回滚操作。

两段式提交的三个非常明显的缺点:

单点问题:协调者在两段提交中具有举足轻重的作用,协调者等待参与者回复时可以有超时机制,允许参与者宕机,但参与者等待协调者指令时无法做超时处理。一旦协调者宕机,所有参与者都会受到影响。如果协调者一直没有恢复,没有正常发送 Commit 或者 Rollback 的指令,那所有参与者都必须一直等待。

性能问题:两段提交过程中,所有参与者相当于被绑定成为一个统一调度的整体,期间要经过两次远程服务调用、三次数据持久化(准备阶段写重做日志,协调者做状态持久化,提交阶段在日志写入 Commit Record),整个过程将持续到参与者集群中最慢的那一个处理操作结束为止。这就决定了两段式提交的性能通常都比较差。

一致性风险:当网络稳定性和宕机恢复能力的假设不成立时,两段式提交可能会出现一致性问题。

三段式提交
为了解决两段式提交的单点问题、性能问题和数据一致性问题,3PC协议出现了。但是三段式提交,也并没有解决一致性问题。

三段式提交把原本的两段式准备阶段再细分为两个阶段,分别称为 CanCommit、PreCommit,把提交阶段改为 DoCommit 阶段。其中,新增的 CanCommit 是一个询问阶段,协调者让每个参与的数据库根据自身状态,评估该事务是否有可能顺利完成。
将准备阶段一分为二的理由是,这个阶段是重负载的操作,一旦协调者发出开始准备的消息,每个参与者都将马上开始写重做日志,这时候涉及的数据资源都会被锁住。如果此时某一个参与者无法完成提交,相当于所有的参与者都做了一轮无用功。也就是提前询问一下,减少事务不成功的业务消耗资源。

增加一轮询问阶段,如果都得到了正面的响应,那事务能够成功提交的把握就比较大了,也意味着因某个参与者提交时发生崩溃而导致全部回滚的风险相对变小了。

因为询问阶段使得事务失败回滚的概率变小了,所以在三段式提交中,如果协调者在 PreCommit 阶段开始之后发生了宕机,参与者没有能等到 DoCommit 的消息的话,默认的操作策略将是提交事务而不是回滚事务或者持续等待。你看,这就相当于避免了协调者的单点问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值