了解下五种TCC分布式事务编程模型

774 阅读4分钟

大家好,我是小趴菜,在单机服务下,Spring自带的事务就可以满足我们的需求了,但是在分布式下,Spring的事务就没法保证我们的数据可靠性,这时候我们就需要引入分布式事务,TCC就是其中一种分布式事务解决方案

TCC共有三个阶段

. try:尝试阶段

. confirm:确认阶段

. cancel:回滚阶段

我们还是以最经典的转账案例,我们需要知道在整个转账过程到结束,TCC的每一个阶段都做了些什么事

TCC模型-1(错误)

image.png

在try阶段,两个人都不做操作,在confirm阶段进行扣钱和加钱操作,cancel回滚阶段也是什么都不做

首先这个模式是错误的,为什么呢?

try阶段什么都不做,那么在confirm阶段的时候就会出现问题,比如张三的余额不足20了,这时候肯定会就扣减失败,但是李四加20元是成功的。这时候即使会回滚,但是会发现,cancel阶段没有做任何操作,所以就会出现张三钱没扣,但是李四的账户多了20元

而之所以会出现这个问题,主要是没有提前判断章三的账户是否余额充足

TCC模型-2(错误)

image.png

针对模型一的问题,这时候在try阶段我们首先判断张三的金额是否充足,然后在confirm阶段进行扣减金额,这时候是不是就不会出现模型一的问题了。

首先这个模型还是错误的。

假设张三现在还有20元,但是这时候有二笔扣款操作,每笔20元。但是在try阶段只是判断金额是否大于等于20,所以二个操作在try阶段都会成功。

但是在confirm阶段,由于张三的金额只有20元,所以肯定会有一笔扣款操作会失败,但是李四加钱是会成功的。这时候又会出现模型一的问题,张三扣钱失败,但是李四加钱成功了。即使回滚,李四的钱也不会有变化,所以到最后,李四的钱就会莫名其妙多20

而之所以会出现这个问题是由于并发导致的,二个线程同时来执行扣款请求,这时候try阶段都会成功,所以才导致有一笔操作在confirm失败

TCC模型-3(错误)

image.png

针对模型二的并发问题,这时候我们在try阶段先扣减张三的20元,同时将李四的账户加20元,如果执行成功,confirm阶段就什么都不做,cancel回滚阶段就还张三20,扣减李四40

这时候我们好像是已经解决了在并发情况下,张三因为余额不足而导致的一次失败操作

但是此时我们在try阶段是直接将李四的金额加了20,此时还没有执行confirm阶段的操作,这时候正好有一个线程B来操作李四的账户,比如扣减操作,假设李四本来的金额是只有10块,这个时候线程B来扣减李四的账户20元,正常来说,我们给李四增加20元的这个TCC事务还没有完成,也就是还没有执行confirm阶段,但是线程B这时候来发现李四账户有30了,可以扣减20元

这时候问题来了,我们这个TCC事务执行了try阶段,李四的金额从10变成了30,线程B发现李四账户有30元,就扣减了20,这时候由于某些原因,导致这个TCC事务失败了,那么就会执行cancel阶段的操作,但是此时李四账户因为线程B扣了20,所以只剩10块。这时候难道要从10扣减成-10吗?

而之所以会出现这个问题就是由于脏读导致的,因为第一个线程还没有提交,导致其它线程读到了脏的数据

TCC模型-4(正确,标准的TCC模型)

image.png

针对模型三的脏读问题,我们在try阶段就不对李四账户做操作,此时也只是冻结张三的20元, 如果try阶段成功,那么confim阶段就会将对应张三账户扣减冻结20元,李四也会加20元,即使失败了,执行了cancel操作,也只是将张三冻结的20元返还而已

TCC模型-5(正确,高效的TCC模型)

image.png

为什么这个模型相较于模型四更高效呢,业务的操作百分之九十以上其实都会成功,也就是说大部分的请求都会执行confirm阶段,相对于模型四而言,confirm阶段多了一步扣减张三冻结的20元的数据库操作,模型五相对来说就减少了一步数据库的操作