事务的原理

1. 什么是事务

事务就是一个包含多个步骤的事情,这些步骤要么都做好,要么都别做。

2. ACID

事务都跟ACID相关,注意这里说的是“相关”,而不是一定都“满足”。全都严格满足,是“刚性事务”,部分满足或一定程度满足,是“柔性事务”。 ACID展开来说是:

  • A(Atomic)原子性:事务的每一步要么都做好,要么都别做。
  • C(Consistency)一致性:事务从一个状态变到另一个状态,不能停止在中间状态。比如你只能在家、或在公司,不能停在路上(可以停一段时间,但不能成为你的终态)。
  • I(Isolation)隔离性:多个事务执行过程中的互相影响的情况。比如你和你同事分别去公司,路上碰见可能聊几句话(脏读、幻读、不可重复读),也可能碰到了装作不认识。
  • D(Durability)持久性:事务一但成功,就被永久保留下来。比如你到公司了,只要没有其他事情发生:同事来看,你在;老板来看,你也在;老板上午来看,你在;老板晚上10点来看,你也在。

3. CAP

  • C:一致性
  • A:可用性
  • P:分区容错

分布式场景下,P必须满足,否则就是单机系统了。

分布式下,A和C只能满足一个。

  • CP:追求强一致性,一般像配置中心,是CP模式,如zookeeper、etcd。
  • AP:最终一致性,追求高可用,BASE,一般像注册中心。

4. BASE

在互联网服务中,一般对可用性要求很高,比如5个9的SLA。在这些场景的分布式事务中,对AP进行了延伸,保障高可用,容忍一定时间的中间状态,只要能最终一致性就可以,这就是BASE。

5. 刚性事务和柔性事务

5.1. 刚性事务

严格满足ACID。

5.2. 柔性事务

  • A:严格满足
  • C:部分满足
  • I:部分满足
  • D:严格满足

6. XA协议

数据库的一种接口协议,一般是用于数据库事务的。

它所定义的3种角色,在其他分布式事务中也可以参考使用。

  • AP:应用服务。
  • TM:事务管理器,有些地方也叫事务协调者。
  • RM:资源管理器。

7. 分布式事务的方案

7.1. 两阶段提交(2PC)

  • 1阶段prepare:事务参与方开启事务(如数据库做为参与方,这一阶段会开启数据库事务,记录undo、redo信息),但不提交。
  • 2阶段commit:1阶段都准备好的话,这个阶段就提交,否则这个阶段就回滚。

2b37f71c028546b78b7b19f558f03198.jpg

2PC的问题主要是同步阻塞事务时间长。每个RM在第一阶段都要开启事务(就像DB里start了一个事务,但没有commit,就阻塞在这里了,并且每个RM都要等所有其他RM全部开启好事务),在第二阶段才会提交或回滚事务。如果有一个RM开启事务很慢,其他RM就要开启着事务等着它,平白无故在那里等它。

比如有个人在深山老林,网络很差,跟其他人一起协商做个事,其他人要等他半天。

7.2. 三阶段提交(3PC)

  • 1阶段canCommit:RM上不开启事务,只是问问每个RM,能不能行。
  • 2阶段prepare:对应2PC的1阶段。
  • 3阶段commit:对应2PC的2阶段。

5a756590ffbd42c4a3f373aae8f6cd88.jpg3PC对2PC做了个优化,先询问一下每个RM基本资源是不是准备好了,并不开启事务,这会大大降低由于下一阶段由于部分RM异常(包括RM本身异常和RM对应的网络异常)所导的其他RM白白开启事务的成本。

比如有个人在深山老林,网络很差,跟其他人一起协商做个事,其他人发现他网不好,这个事情就先不做了(回滚),大家也不会干耗在那里等他半天,才决定不做。

虽然3PC对2PC做了优化,但实际用得最多的还是2PC,因为3PC有点复杂。

7.3. TCC

TCC是Try-Confirm-Cancel的简称,属于两阶段提交:Try是一阶段,Confirm/Cancel是二阶段。

TCC的思路是减小RM上事务影响范围。

  • Try:TCC只预留部分资源,不像2PC,第一阶段在RM上就把一个大事务给开启了。比如交易的时候TCC先“预扣款”,即冻结部分钱,这样你的其他钱还能被其他事务使用;而2PC的第一阶段上来就把你整个银行卡冻住,你其他事情也干不了了。
  • Confirm:执行真正的业务提交,它是基于Try阶段预留的资源进行的。比如在上述交易场景中,Confirm阶段会将冻结的钱正式扣除,并进行其他相关业务操作,如生成订单等。
  • Cancel:在Try或者Confirm阶段出现问题,都会执行Cancel阶段,将资源状态恢复到事务执行前。比如上述交易场景出现问题,Cancel阶段就把冻结的钱再“还”回去。这也可以算作一种“补偿”机制。
  1. Try和Confirm阶段分别要提交哪些事务,都是根据业务自己来决定的,而且因为提交了事务,在Cancel阶段要回滚就只能业务上自己处理了,所以TCC一般是业务上自己实现的,没法靠数据库帮你回滚。
  2. TCC对业务入侵较多,因为TCC一般是业务上自己实现的。

7.4. AT模式

automatic transaction,自动事务。也是一种2PC的变形。

7.4.1. 数据准备阶段(1阶段)

在RM上拦截业务SQL,保存SQL执行前后的数据镜像(分别叫“前镜像”和“后镜像”),这些操作是在事务范围内进行的,确保数据的一致性。然后把这些镜像和事务相关信息存到undo_log中,用于后续回滚。

7.4.2. 事务提交阶段(2阶段)

如果每个RM的事务都顺利提交,就可以删除undo_log中的记录。

7.4.3. 事务回滚阶段(2阶段)

如果某些(个)RM事务执行有问题,则根据undo_log中的“前镜像”“后镜像”等信息,对已执行操作进行逆向操作。

7.4.4. AT的特点总结

  • 锁定少:一阶段不强锁定,通过数据前后镜像来回滚,RM锁定粒度和时长与传统2PC不同。
  • 无侵入:对业务代码无侵入,开发者无需关心事务的具体实现细节,只需关注业务逻辑本身。
  • 高性能:可以把2阶段异步化,这样只处理了一阶段就返回响应,然后异步慢慢处理二阶段。

7.5. 事务消息

事务消息是一种分布式系统中保证事务一致性的消息处理机制。属于一种特殊的2PC。

7.5.1. 正常过程

  1. 第一步,发送半消息:生产者把消息发到消息中间件,此时消息中间件不会把此消息投给消费者。
  2. 第二步,成功,则提交消息:生产者执行本地事务,如果没问题会提交给消息中间件一个确认的消息,消息中间件保证(重试、死信队列)把消息投递给消费者。
  3. 第二步,失败,则回滚消息:本地事务执行失败,生产者会向消息中间件发送一个回滚消息,消息中间件会删除之前暂存的半事务消息,这样消费者就不会收到该消息。

“半消息”一般都是完整的消息,只是后面还要“提交”一下才能被消费者看到,所以被叫做“半消息”

7.5.2. 异常处理

  • 消息中间件一直未收到确认或回滚的消息:比如网络异常或者生产者突然挂了,不同的消息中间件的处理方式会有所不同。如:
    • 回滚该半消息:将其从消息存储中删除
    • 使用回调:消息中间件定期(或超时)来查询生产者上的一个回调函数,来觉得是回滚还是提交半消息。如果查询次数太多(相当于重试太多),会丢掉这个消息,或者把它记录到某个地方(比如死信队列)。
  • 消息中间件收到确认或回滚消息后,投递给消费者失败:这要靠消息中间件的机制来确保一定要投递出去了,一般消息中间件会重试。
  • 由于消息中间件会重试,所以消费者可能会收到多次相同的消息,所以消费者需要保证幂等性消费。

事务消息,要重点记住和理解“两次提交”消息和“回调查询”的机制

7.6. SAGA模式

  • 把事务拆分成多个本地事务,按一定顺序执行。
  • 每个本地事务,都要有一个对应的补偿事务。

比如有A、B、C三个事务,对应a、b、c三个补偿事务,正常执行顺序是ABC,当执行到C失败的时候,就要执行b、a,把事务回退。

SAGA模式的特点:

  • 灵活:可灵活定义事务拆分方式和补偿事务。它不依赖事务协调器,本地事务可独立开发部署。
  • 高性能:被拆分的这些本地事务可以异步执行,提高系统的并发和速度。
  • 对业务侵入性较强:与其他一些分布式事务解决方案相比,SAGA模式需要开发者手动编写每个本地事务及对应的补偿事务,对业务代码有一定的侵入性。

8. 分布式事务方案对比

a7f56629eba34a84b3fa0e85e3552dbf.png

9. 事务悬挂与空回滚

9.1. 事务悬挂

事务悬挂就是事务没有正常走到终态。

比如猴子要么在树上,要么在地上,是正常的终态,但它“悬挂”在树上,始终不是长久之计。

通常,事务悬挂是发生在某些事务参与方一阶段正常、二阶段不正常的情况下。

一般来说,成熟的事务框架会保证业务逻辑正常的时候不会发生悬挂,但业务逻辑有问题,就没法保证了。比如参与方在二阶段明明不正常,它还给TM(transaction manager)返回正常。

9.2. 空回滚

一般是RM上的一阶段还没处理完(或没处理,比如网络延时导致先收到二阶段的请求),二阶段的回滚请求就过来了,这样回滚的就是空的东西。

如事务消息中,生产者发送半消息后,由于网络原因,消息中间件没有收到该半事务消息,但消息中间件触发了回滚操作,这就是空回滚。

9.3. 空回滚容易导致事务悬挂

RM上先执行了二阶段的回滚(空回滚),才收到一阶段的请求,一阶段的请求再也不会被二阶段处理,就悬在那里了。

9.4 事务悬挂的解决方法

9.4.1. 方法1. 一阶段必须先执行完,才能执行二阶段

这要求一阶段有重试机制,其实就是把一二阶段串行起来了,必须先1,再2。

9.4.2. 方法2.双插方案

  • 一阶段开始前插入一个“事务id”且状态是“一阶段”的记录,然后锁住所有“事务id”的记录,看下有没有状态是“回滚”的,如果没有,正常执行一阶段,否则说明有些RM先回滚了,那就不执行一阶段了。
  • 二阶段回滚前插入一个“事务id”且状态是“回滚”的记录,然后锁住所有“事务id”的记录,再执行回滚。

本质就是让一二阶段感知到对方,且同一个事务不能并行跑一二阶段。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值