事务一致性

数据库事务的概念

数据库事务:多个操作可以被视作一个完整的逻辑处理单元,要么全部执行,要么全部不执行,是不可分割的工作单元。

事务具有ACID的特性:

  • 原子性:日志保证,undolog

  • 一致性:锁保证

  • 隔离性:锁保证

  • 持久性:日志保证,redolog

事务是为了保证一致性产生的,AID是方式,C是目的。

分布式事务的概念

涉及到多个数据库、多个服务时,多个库、多个服务之间就会存在数据一致性的问题,这种事务就是分布式事务。

如何解决分布式事务的问题?解决思路是什么呢?

分布式事务问题实际上就是分布式系统下的数据一致性问题。根据CAP和BASE理论解决一致性问题一般有强一致性与最终一致性方案。

强一致性方案比较适合对一致性的实时性要求高、性能要求较低的场景下;而最终一致性方案,则适合系统对性能的要求较高,实时性要求比较叫低的场景。

强一致性的场景下,通常会引入一个事务协调者,监控所有分支事务的运行状态。通常,所有分支事务会想进行”彩排“,彩排的目的是提高事务的成功概率,如果彩排成功,就会对所有分支事务提交,否则会通知所有的分支事务回滚。

而最终一致性方案因为对实时性要求比较低,解决方案就相对比较多。

方案名称一致性描述

XA规范

强一致性

2PC协议

强一致性

3PC协议

强一致性

TCC

最终一致性

先进行资源预留,成功提交,失败取消资源预留

Saga

最终一致性

提交时从前往后提交,回滚时从后往前做补偿操作

事件驱动

最终一致性

前一个事务提交成功后会发送事件给下一个事务,依次驱动到最后一个事务

事件表+定时任务+消息队列

最终一致性

事件驱动的优化,存储分支事务事件到事件表,并通过定时任务发送事件到其他服务处理分支事务

本地消息表

最终一致性

在主事务中记录分支事务的消息状态,然后向分支事务发送消息,根据所有消息的状态可对事务进行完成或回滚的操作

最大努力通知方案

最终一致性

事务处理失败重复尝试知道成功为止、事务处理失败通过其他查询接口对其数据(支付对账)

分布式事务的XA规范

根据分布式事务的解决思路,就衍生出了XA规范。XA规范定义了事务协调者与数据库之间的规范,事务协调者用于通知数据库的开始、结束以及提交、回滚等。

上图中,TM就是协调者,RM是数据库,AP是应用程序。XA接口函数由数据库厂商提供。

分布式集群情况下,一般增加代理层来充当TM的角色,实现对事物的支持,由XA规范,引申出了:

  • 2PC,两阶段提交

  • 3PC,三阶段提交(几乎没有人用)

什么是两阶段提交协议?

两阶段提交分为两个阶段:

  1. 第一阶段为投票阶段,事务协调者开启事务,让参与事务的各方进行执行演练,并收集执行结果

  2. 第二阶段为提交/回滚阶段,如果有所有方都投票成功,就会通知所有方进行提交。如果有任何一方投票失败,或者连接不通,就会通知所有方进行回滚。

两阶段提交可以尽可能的保证数据的一致性。

两阶段提交有什么缺点?如何解决这些缺点?

  • 单点故障

    • 协调者发生故障

    • 解决方案:协调者改为集群部署

  • 阻塞资源

    • 占用数据库连接,性能低

    • 解决方案:在一阶段的时候,直接提交事务,并记录提交前的数据状态。等到二阶段,如果成功,那就不用通知了。如果失败,将数据回滚到记录的上个状态中(Alibaba seata AT模式 就是这么做的,他通过undolog来完成的)。

  • 数据不一致仍有可能发生

    • 当演练成功后,通知所有的服务,这个阶段有可能通讯失败,就造成了数据不一致的情况。

    • 解决方案:使用脚本检查异常情况,自动回滚

什么是三阶段提交协议?

有了两阶段提交,为什么还需要三阶段提交协议?

  1. 事务参与者如果本身就不符合执行条件,如果继续开启事务执行sql,会占用数据库连接,造成性能损失。故在三阶段提交中,增加can commit阶段,用于校验参与者是否可以提交,如果不可以,直接不开启事务。

  2. 为参与者提供超时机制,也就是在do commit阶段,做更好的容错处理,提高事务成功的概率。

三阶段提交分为三个阶段,分别是:

  1. can commit:校验所有参与者是否可进行提交操作

    1. 如果反馈结果为成功,进行pre commit阶段

    2. 如果有反馈结果为失败,执行 abort commit,事务直接会被取消。can commit返回失败的情况包含:

      1. 参与者返回no

      2. 协调者等待超时(包含参与者没有收到指令的情况)

  2. pre commit:同二阶段第一个相同,执行sql

    1. 如果反馈结果为成功,进行do commit阶段

    2. 如果有反馈结果为失败,执行 abort commit,并会进行数据回滚

  3. do commit:提交事务

参与者视角,可能发生超时的情况:

  1. can commit指令阶段,参与者不存咋超时情况

  2. 参与者在收到can commit指令后并回复,但是没有收到pre commit指令或abort commit指令,这时会执行回滚操作

  3. 参与者在收到pre commit指令后并回复,但是没有收到do commit指令或abort commit指令,这时候会直接执行提交操作(这是一个概率问题,前两个阶段的校验已经通过,后续大概率是会成功的,所以直接提交事务较为合适)。

什么是TCC(Try Confirm/Cancel)解决方案?

TCC可以解决两个数据方的数据同步问题,比如Mysql和Redis的数据同步问题:

  1. Try阶段,张三转账100元,将余额字段减去100,但是冻结字段增加100。而李四的冻结字段会加上100。

  2. 如果Try阶段成功,会执行Confirm,这时张三的冻结字段减100,李四的冻结字段减100,但是余额增加一百。

  3. 如果Try阶段失败,会执行Cancel,这时张三的冻结字段减100,余额增加100,恢复到了之前的状态,而李四的冻结字段减去了100,也恢复到了之前的状态。

所谓TCC就是给整个流程执行的过程中,增加一个中间的冻结态,如果操作失败,都可以根据中间台进行逆操作,从而恢复之前的状态。

使用事件表+定时任务+消息队列来解决分布式事务的问题

优点和缺点:

  1. 系统的可用性高,异步处理,接口的响应速度快

  2. 相应的,所有事件的处理是通过消息传递的,有可能发生数据一致性的问题,只能尽可能保证最终一致性

最大努力通知方案

支付回调,有可能发生回调失败的情况,可以增加重试次数。 并且,支付接口通常会提供查询接口,查询出已提交的支付信息的支付状态:

  1. 重复通知机制

  2. 消息校对机制

最后更新于