1 什么是事务
数据库事务(简称:事务,Transaction)是指数据库执⾏过程中的⼀个逻辑单位,⼀个事务会有多个业务操作构成。
connection.setAutoCommit(false); //开启事务
业务操作A:扣减库存
业务操作B:创建订单
业务操作C:扣款
业务操作D:增加⽤户积分
connection.commit(); //提交事务
connection.rollback(); //有异常回滚事务
ACID
事务拥有以下四个特性,习惯上被称为ACID特性:
原⼦性(Atomicity):事务作为⼀个整体被执⾏,包含在其中的对数据库的操作要么全部被执⾏,要么都不执⾏。
⼀致性(Consistency):事务应确保数据库的状态从⼀个⼀致状态转变为另⼀个⼀致状态。⼀致状态是指数据库中的数据应满⾜完整性约束。除此之外,⼀致性还有另外⼀层语义,就是事务的中间状态不能被观察到(这层语义也有说应该属于原⼦性)。
隔离性(Isolation):多个事务并发执⾏时,⼀个事务的执⾏不应影响其他事务的执⾏,如同只有这⼀个操作在被数据库所执⾏⼀样。
持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中。在事务结束时,此操作将不可逆转。
什么是分布式事务
分布式事务指事务的参与者、⽀持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上,且属于不同的应⽤,分布式事务需要保证这些操作要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同数据库的数据⼀致性。
CAP定理
CAP定理不可能同时满足以下三点
⼀致性(Consistency)
这⾥指的是强⼀致性,⽽最终⼀致性
在写操作完成后开始的任何读操作都必须返回该值,或者后续写操作的结果
也就是说,在⼀致性系统中,⼀旦客户端将值写⼊任何⼀台服务器并获得响应,那么之后client从其他任何服务器读取的都是刚写⼊的数据
- 客户端向G1写⼊数据v1,并等待响应
- 此时,G1服务器的数据为v1,⽽G2服务器的数据为v0,两者不⼀致
- 接着,在返回响应给客户端之前,G2服务器会⾃动同步G1服务器的数据,使得G2服务器的数据也是v1
- ⼀致性保证了不管向哪台服务器(⽐如这边向G1)写⼊数据,其他的服务器(G2)能实时同步数据
- G2已经同步了G1的数据,会告诉G1,我已经同步了
- G1接收了所有同步服务器的已同步的报告,才将“写⼊成功”信息响应给client
- client再发起请求,读取G2的数据
- 此时得到的响应是v1,即使client从未写⼊数据到G2
可⽤性(Availability)
系统中⾮故障节点收到的每个请求都必须有响应
在可⽤系统中,如果我们的客户端向服务器发送请求,并且服务器未崩溃,则服务器必须最终响应客户端,不允许服务器忽略客户的请求
分区容错性(Partition tolerance)
允许⽹络丢失从⼀个节点发送到另⼀个节点的任意多条消息,即不同步
也就是说,G1和G2发送给对⽅的任何消息都是可以放弃的,也就是说G1和G2可能因为各种意外情况,导致⽆法成功进⾏同步,分布式系统要能容忍这种情况。
例1:ZK遵循的是CP原则,即⼀致性和分区容错性,牺牲了可⽤性,具体牺牲在哪⾥呢?
当Leader宕机以后,集群机器⻢上会进去到新的Leader选举中,但是选举时⻓在30s — 120s之间,这个选取Leader期间,是不提供服务的,不满⾜可⽤性,所以牺牲了可⽤性
经过上⾯的简单讲解,为什么选举时⻓会⻓达半分钟到2分钟呢?
当然是为了保证⼀致性,为了保证集群中各个节点数据的⼀致性,ZK做了两类数据同步,初始化同步和更新同步。
1:当新的Leader选举出来后,各个Follower需要将新的Leader的数据同步到⾃⼰的缓存
中,这就是初始化同步
2:当Leader数据被客户端修改后,其会向Follower发出⼴播,然后各个Follwer会⽵筒去同步更新的数据,这是更新同步⽆论是初始化同步还是更新同步,ZK集群为了保证数据的⼀致性,若发现超过半数的Follower同步超时,则其会再次进⾏同步,⽽这个过程中ZK集群同样出去不可⽤状态
Eureka保证AP
Eureka看明⽩了这⼀点,因此在设计时就优先保证可⽤性。Eureka各个节点都是平等的,⼏个节点挂掉不会影响正常节点的⼯作,剩余的节点依然可以提供注册和查询服务。⽽Eureka的客户端在向某个Eureka注册或时如果发现连接失败,则会⾃动切换⾄其它节点,只要有⼀台Eureka还在,就能保证注册服务可⽤(保证可⽤性),只不过查到的信息可能不是最新的(不保证强⼀致性)。除此之外,Eureka还有⼀种⾃我保护机制,如果在15分钟内超过85%的节点都没有正常的⼼跳,那么Eureka就认为客户端与注册中⼼出现了⽹络故障,此时会出现以下⼏种情况:
- Eureka不再从注册列表中移除因为⻓时间没收到⼼跳⽽应该过期的服务
- Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可⽤)
- 当⽹络稳定时,当前实例新的注册信息会被同步到其它节点中
因此, Eureka可以很好的应对因⽹络故障导致部分节点失去联系的情况,⽽不会像zookeeper那样使整个注册服务瘫痪。
分布式事务解决⽅案
1 两阶段提交(2PC) ----->要成功都成功 有一个失败全部回滚
⼆阶段事务提交⽅案:强⼀致性
事务的发起者称协调者,事务的执⾏者称参与者。
处理流程:
1、准备阶段
事务协调者,向所有事务参与者发送事务内容,询问是否可以提交事务,并等待参与者回复。
事务参与者收到事务内容,开始执⾏事务操作,将 undo 和 redo 信息记⼊事务⽇志中(但此时并不提交事务)。
如果参与者执⾏成功,给协调者回复yes,表示可以进⾏事务提交。如果执⾏失败,给协调者回复no,表示不可提交。
2、提交阶段
如果协调者收到了参与者的失败信息或超时信息,直接给所有参与者发送回滚(rollback)信息进⾏事务回滚,否则,发送提交(commit)信息。
参与者根据协调者的指令执⾏提交或者回滚操作,释放所有事务处理过程中使⽤的锁资源。(注意:必须在最后阶段释放锁资源) 接下来分两种情况分别讨论提交阶段的过程。
简单⼀点理解,可以把协调者节点⽐喻为带头⼤哥,参与者理解⽐喻为跟班⼩弟,带头⼤哥统⼀协调跟班⼩弟的任务执⾏。
情况 1,当所有参与者均反馈 yes,提交事务,如上图:
协调者向所有参与者发出正式提交事务的请求(即 commit 请求)。
参与者执⾏ commit 请求,并释放整个事务期间占⽤的资源。
各参与者向协调者反馈 ack(应答)完成的消息。
协调者收到所有参与者反馈的 ack 消息后,即完成事务提交。
情况 2,当任何阶段 1 ⼀个参与者反馈 no,中断事务,如上图:
协调者向所有参与者发出回滚请求(即 rollback 请求)。
参与者使⽤阶段 1 中的 undo 信息执⾏回滚操作,并释放整个事务期间占⽤的资源。
各参与者向协调者反馈 ack 完成的消息。
协调者收到所有参与者反馈的 ack 消息后,即完成事务中断。
⽅案总结
2PC ⽅案实现起来简单,主要因为以下问题:
性能问题:所有参与者在事务提交阶段处于同步阻塞状态,占⽤系统资源,容易导致性能瓶颈。
可靠性问题:如果协调者存在单点故障问题,如果协调者出现故障,参与者将⼀直处于锁定状态。
数据⼀致性问题:在阶段 2 中,如果发⽣局部⽹络问题,⼀部分事务参与者收到了提交消息,另⼀部分事务参与者没收到提交消息,那么就导致了节点之间数据的不⼀致。
1.2 补偿事务(TCC)
TCC 事务:最终⼀致性
⽅案简介
TCC 是服务化的⼆阶段编程模型,其 Try、Confirm、Cancel 3 个⽅法均由业务编码实现:
Try 操作作为⼀阶段,负责资源的检查和预留。
Confirm 操作作为⼆阶段提交操作,执⾏真正的业务。
Cancel 是预留资源的取消。
TCC 事务的 Try、Confirm、Cancel 可以理解为 SQL 事务中的 Lock、Commit、Rollback。
处理流程
为了⽅便理解,下⾯以电商下单为例进⾏⽅案解析,这⾥把整个过程简单分为扣减库存,订单创建 2 个步骤,库存服务和订单服务分别在不同的服务器节点上。
①Try 阶段
从执⾏阶段来看,与传统事务机制中业务逻辑相同。但从业务⻆度来看,却不⼀样。
TCC 机制中的 Try 仅是⼀个初步操作,它和后续的确认⼀起才能真正构成⼀个完整的业务逻辑,这个阶段主要完成:
完成所有业务检查( ⼀致性 ) 。
预留必须业务资源( 准隔离性 ) 。
Try 尝试执⾏业务。
TCC 事务机制以初步操作(Try)为中⼼的,确认操作(Confirm)和取消操作(Cancel)都是围绕初步操作(Try)⽽展开。
因此,Try 阶段中的操作,其保障性是最好的,即使失败,仍然有取消操作(Cancel)可以将其执⾏结果撤销。
假设商品库存为 100,购买数量为 2,这⾥检查和更新库存的同时,冻结⽤户购买数量的库存,同时创建订单,订单状态为待确认。
②Confirm / Cancel 阶段
根据 Try 阶段服务是否全部正常执⾏,继续执⾏确认操作(Confirm)或取消操作(Cancel)。
Confirm 和 Cancel 操作满⾜幂等性,如果 Confirm 或 Cancel 操作执⾏失败,将会不断重试直到执⾏完成。
Confirm:当 Try 阶段服务全部正常执⾏, 执⾏确认业务逻辑操作
幂等性:就是⽤户对于同⼀操作发起的⼀次请求或者多次请求的结果是⼀致的,不会因为⽤户多次执⾏产⽣不同结果。
这⾥使⽤的资源⼀定是 Try 阶段预留的业务资源。在 TCC 事务机制中认为,如果在 Try 阶段能正常的预留资源,那 Confirm ⼀定能完整正确的提交。
Confirm 阶段也可以看成是对 Try 阶段的⼀个补充,Try+Confirm ⼀起组成了⼀个完整的业务逻辑。
Cancel:当 Try 阶段存在服务执⾏失败, 进⼊ Cancel 阶段
Cancel 取消执⾏,释放 Try 阶段预留的业务资源,上⾯的例⼦中,Cancel 操作会把冻结的库存释放,并更新订单状态为取消
TCC 事务机制相对于传统事务机制(X/Open XA),TCC 事务机制相⽐于上⾯介绍的 XA 事务机制,有以下优点:
性能提升:具体业务来实现控制资源锁的粒度变⼩,不会锁定整个资源。
数据最终⼀致性:基于 Confirm 和 Cancel 的幂等性,保证事务最终完成确认或者取消,保证数据的⼀致性。
可靠性:解决了 XA 协议的协调者单点故障问题,由主业务⽅发起并控制整个业务活动,业务活动管理器也变成多点,引⼊集群。
缺点: TCC 的 Try、Confirm 和 Cancel 操作功能要按具体业务来实现,业务耦合度较⾼,提⾼了开发成本。