数据库事务

Outline
1.ACID
2.CAP
3.BASE
4.一致性协议

一直都对数据库事务不甚清楚,趁着前段时间对数据库事务级别进行了总结,一起对分布式事务进行总结梳理

ACID

1
2
3
4
start transaction;
update account set money = money-100 where userId = 1;
update account set money = money+100 where userId = 2;
commit;

A(Atomicity),原子性,一个事务内的操作,要么全成功执行,要么全部不执行。
C(Consistency),一致性,事务执行的结果必须使将数据库从一种一致的状态变为另一种一致的状态,不可能出现中间状态,只有事务提交前状态和提交后状态,不存在提交中的状态。
I(Isolation),隔离性,每个读写事务的对象对其他事务的操作对象相互独立,即该事务提交前对其他事务都不可见。
D(Durability),持久性,一个事务一旦提交,它对数据库中对应数据的状态变更就是永久性的。

CAP

单机事务很容易实现支持ACID特性的事务处理系统,但是在分布式环境中问题就变得困难许多。
CAP理论告诉我们,一个分布式系统不可能同时满足一致性(C:Consistency)、可用性(A:Availability)、分区容错性(P:Partition tolerance)。最多只能同时满足其中两项。

抛弃P
这个最简单,只有一台机器提供服务,肯定能保证一致性和可用性。
看到这里可能有人会问了,单机怎么保证可用性?我们来看这段话,引自《NOSQL Distilled》
By the usual definition of “available,” this would mean alack of availability, but this is where CAP’s special usage of “availability” gets confusing. CAP defines “availability” to mean “every request received by a non-failing node in the system must result in a response”[Lynch and Gilbert]. So a failed, unresponsive node doesn’t infer alack of CAP availability.
上面那段主要说的是,CAP中的A指的是,每个被活着的节点收到的请求都能得到响应,所以如果一个节点宕了无法对请求进行响应并不代表不具备A。
这样就说得通了,单机肯定满足C和A,但是不具备P,意味着放弃了系统的可扩展性。

抛弃A(机器未宕机但不提供服务)
意味着分布式系统只有C和P,指当系统遇到网络分区等故障的时候,受影响的服务无法对外提供正常服务。当发生网络分区时,此时的措施是从系统中抛弃一部分分区,从而维持系统的一致性和分区容错性。

抛弃C
意味着分布式系统只有C和P,指当系统遇到网络分区等故障的时候,让分离开的几个部分同时对外提供服务,这样可以保证可用性和分区容错性,但是会导致一致性受影响。

Ps 这里还有另外一种理解方式,分布式系统(可以简单理解为满足P必然是分布式系统)情况下,如果满足C那么服务器间进行数据同步时要锁住服务必然不能提供服务;如果满足A那么服务器必然不能被锁住,而这样又无法兼顾一致性。

如今大多数公司的架构都是分布式,所以分区容错性可以说是系统的一个最基本的需求,所以讨论的焦点就在如何在C和A之间寻求平衡。

BASE

BASE:
1)Basically Available
遇到故障,服务降级,比如响应时间增加、非核心功能不可用等
2)Soft state
弱状态,指允许系统中的数据存在中间状态,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时
3)Eventually consistent
顾名思义,经过一段时间同步后,最终能够达到一个一致的状态

通过牺牲强一致性来获得可用性,并允许数据在一段时间内是不一致的,但最终达到一直状态。

一致性协议

2PC
1)阶段一:提交事务请求(投票阶段)
1.1事务询问
协调者向所有的参与者发送事务内容,询问是否可以执行事务提交操作,并开始等待各参与者响应
1.2执行事务
各参与者节点执行事务操作,将Undo和Redo信息写到事务日志中
1.3各参与者向协调者反馈事务询问的响应
如果参与者成功执行了事务操作,那么反馈给协调者yes,表示可以事务执行;反之,反馈给协调者no,表示事务不可以执行。

2)阶段二:执行事务提交
2.1执行事务提交
如果参与者反馈都是Yes,那么执行事务提交
2.1.1发送提交请求
协调者向所有参与者节点发出Commit请求
2.1.2事务提交
参与者接收到Commit请求后,会正式执行事务commit操作,并在完成提交之后释放整个事务执行期间占用的事务资源
2.1.3反馈事务提交结果
参与者在完成事务提交后,向协调者发送Ack消息
2.1.4完成事务
协调者接收到所有参与者反馈的Ack消息后,完成事务
Or
2.2中断事务
如果任何一个参与者反馈为No,或者在协调者等待某个参与者响应超时后,那么就会中断事务。
2.2.1发送回滚请求
协调者向参与者节点发送Rollback请求
2.2.2事务回滚
参与者接收到Rollback请求后,利用Undo信息执行回滚操作,并在完成回滚后释放整个事务执行期间占用的资源。
2.2.3反馈事务回滚结果
参与者完成事务回滚后,向协调者发送Ack消息
2.2.4中断事务
协调者接收所有参与者反馈的Ack后,完成事务中断

缺点:
1)同步阻塞
在提交阶段,所有参与者都处于阻塞状态,需要等待协调者发送Commit(等待所有参与者都发送Yes到协调者)
2)单点问题
协调者如果出问题,整个二阶段将无法执行,参与者将一直阻塞
3)数据不一致
协调者向参与者发送commit请求之后,发生了局部网络异常or协调者在未发送完Commit请求前自身发生了崩溃,导致最终只有部分参与者收到了commit请求
4)缺少容错机制
协调者需要给每个参与者额外指定超时机制,超时后整个事务失败。(缺少容错机制)

3PC
3PC是2PC的改进版,将2PC提交事务请求阶段一分为二,由CanCommit,PreCommit,DoCommit三个阶段组成

1)阶段一:CanCommit
1.事务询问
协调者向所有参与者发送一个包含事务内容的canCommit请求,询问是否可以执行事务提交操作,并开始等待参与者响应
2.各参与者向协调者反馈事务询问的响应
参与者在接收到来自协调者的canCommit后,会反馈yes or no表示能否执行事务

2)阶段二:PreCommit
执行事务预提交(如果都是yes)
1.发送预提交请求
协调者向参与者发送preCommit请求
2.事务预提交
参与者接收到preCommit请求后,会执行事务操作,并将Undo和Redo信息记录到事务日志中
3.各参与者向协调者反馈事务执行的响应
如果参与者成功执行了事务操作,那么就反馈给协调者Ack响应,同时等待最终的指令:commit or abort
or
中断事务(不都是yes or 超时)
1.发送中断请求
协调者向参与者发送abort
2.中断事务
无论收到来自协调者的abort请求,还是在等待协调者请求过程中出现超时,参与者都会中断事务

3)阶段三:doCommit
执行提交
1.发送提交请求
协调者收到了所有参与者的Ack响应,向所有参与者发送doCommit请求
2.事务提交
参与者接收doCommit请求后,会正式执行事务的提交,并在完成提交之后释放在整个事务期间占用的事务资源
3.反馈事务提交结果
参与者在完成事务提交后,向协调者发送Ack消息
4.协调者接收到所有参与者反馈的Ack后,完成事务
or
中断事务(不都是yes or 超时)
1.发送中断请求
协调者向所有的参与者节点发送abort请求
2.事务回滚
参与者接收到abort后,会利用Undo信息来执行事务回滚操作。并在回滚后释放整个事务执行期间占用的资源。
3.反馈事务回滚结果
参与者在完成事务回滚后,向协调者发送Ack消息。
4.中断事务
协调者接收到所有参与者反馈的Ack消息后,中断事务

Ps:
一旦进入阶段三,无论是协调者出现问题,还是协调者和参与者之间的网络出现问题,都会导致参与者无法及时接收到来自协调者的doCommit or abort,此时,参与者会在超时后,继续进行事务提交

优点:
1.相比二阶段提交,阻塞范围变小了,如果事务不能被某个参与者执行会在阶段一便反馈给协调者
2.在出现单点故障后继续达成一致,阶段二协调者故障(发送preCommit前)-所有参与者会自动中断事务,阶段三协调者故障-所有参与者会提交事务
缺点:
1.阶段二在参与者都收到preCommit后,如果出现了网络分区,此时参与者会执行事务但未提交,进入阶段三,这个时候协调者没有收到所有的Ack,那么会通知能和它保持联系的参与者进行回滚,但是由于网络分区隔离出去的参与者们会继续提交事务,这会导致数据的不一致