从事务到分布式事务详解

一、事务概念

    事务(Transaction)是并发控制的基本单位。所谓的事务,它是一个操作序列,这些操作要么都执行,要么都不执行,是一个不可分割的工作单位。

    事务四大特性(ACID

    Atomic(原子性):事务中包含的操作被看做一个逻辑单元,这个逻辑单元中的操作要么全部成功,要么全部失败;

    Consistency(一致性):事务必须使数据库从一个一致性状态变换到另外一种一致性状态;

    Isolation(隔离性):事务允许多个用户对用一个数据进行并发访问,而不破坏数据的正确性和完整性。同事,并行事务的修改必须与其他并行事务的修改相互独立;

    Durability(持久性):一个事务一旦被提交,太对数据库中数据的改变就是永久性的,即使数据库发生故障也不应该对其有任何影响。

 

二、事务隔离

    隔离级别:

  • Serializable(串行化):可避免脏读、不可重复读、虚读情况的发生。

  • Repeatable read(可重复读):可避免脏读、不可重复读情况的发生。

  • Read committed(读已提交):可避免脏读情况发生。

  • Read uncommitted(读未提交):最低级别,以上情况均无法保证。

      如果事务不考虑隔离性,可能会引发如下问题:

  • 脏读,脏读是指一个事务读取了另外一个事务未提交的数据

  • 不可重复读,指在一个事务内读取表中的某一行数据,多次读取结果不同;

  • 虚读(幻读),是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致;

 

三、Spring应用事务

     开启数据库事务

            <!-- 配置spring的PlatformTransactionManager,名字为默认值 -->

            <bean id="dataOnlyReadTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

                    <property name="dataSource" ref="onlyReadDataSource"/>

            </bean>

            <!-- 开启事务控制的注解支持 -->

            <tx:annotation-driven transaction-manager="dataOnlyReadTransactionManager"/>

    开启注解之后,可通过@Transactional注解进行事务标示,常用参数如下:

参 数 名 称

功 能 描 述

readOnly

该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。例如:@Transactional(readOnly=true)

rollbackFor

该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。例如:

指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)

指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})

rollbackForClassName

该属性用于设置需要进行回滚的异常类名称数组, 当方法中抛出指定异常名称数组中的异常时, 则进行事务回滚。例如:

指定单一异常类名称:@Transactional(rollbackForClassName=”RuntimeException”)

指定多个异常类名称:@Transactional(rollbackForClassName={“RuntimeException”,”Exception”})

noRollbackFor

该属性用于设置不需要进行回滚的异常类数组, 当方法中抛出指定异常数组中的异常时,不进行事务回滚。 例如:

指定单一异常类:@Transactional(noRollbackFor=RuntimeException.class)

指定多个异常类:@Transactional(noRollbackFor={RuntimeException.class, Exception.class})

noRollbackForClassName

该属性用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。例如:

指定单一异常类名称:@Transactional(noRollbackForClassName=”RuntimeException”)

指定多个异常类名称:

@Transactional(noRollbackForClassName={“RuntimeException”,”Exception”})

propagation

该属性用于设置事务的传播行为,具体取值可参考表6-7。

例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)

isolation

该属性用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,基本不需要进行设置

timeout

该属性用于设置事务的超时秒数,默认值为-1表示永不超时

需要注意:

  • @Transactional 只能被应用到public方法上, 对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能

  • 要想所有异常都回滚,要加上 @Transactional( rollbackFor={Exception.class,其它异常})

  • 在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上

 

四、分布式事务

       随着微服务架构的普及,一个大型业务系统往往由若干个子系统构成,这些子系统又拥有各自独立的数据库。往往一个业务流程需要由多个子系统共同完成,而且这些操作可能需要在一个事务中完成。在微服务系统中,这些业务场景是普遍存在的。此时,我们就需要在数据库之上通过某种手段,实现支持跨数据库的事务支持,这也就是大家常说的“分布式事务”。

       理论基础:

       CAP定理

  • Consistency (一致性):,即更新操作成功并返回客户端后,所有节点在同一时间的数据完全一致,这就是分布式的一致性。

  • Availability (可用性):可用性指“Reads and writes always succeed”,即服务一直可用,而且是正常响应时间。好的可用性主要是指系统能够很好的为用户服务,不出现用户操作失败或者访问超时等用户体验不好的情况。

  • Partition Tolerance (分区容错性):即分布式系统在遇到某节点或网络分区故障的时候,仍然能够对外提供满足一致性或可用性的服务。分区容错性要求能够使应用虽然是一个分布式系统,而看上去却好像是在一个可以运转正常的整体。比如现在的分布式系统中有某一个或者几个机器宕掉了,其他剩下的机器还能够正常运转满足系统需求,对于用户而言并没有什么体验上的影响。

      总结

        CAP三个特性只能满足其中两个,现如今,对于多数大型互联网应用的场景,主机众多、部署分散,而且现在的集群规模越来越大,节点只会越来越多,所以节点故障、网络故障是常态,因此分区容错性也就成为了一个分布式系统必然要面对的问题。那么就只能在C和A之间进行取舍。但对于传统的项目就可能有所不同,拿银行的转账系统来说,涉及到金钱的对于数据一致性不能做出一丝的让步,C必须保证,出现网络故障的话,宁可停止服务,可以在A和P之间做取舍。

     BASE理论

     BA:Basic Available 基本可用

    整个系统在某些不可抗力的情况下,仍然能够保证“可用性”,即一定时间内仍然能够返回一个明确的结果。只不过“基本可用”和“高可用”的区别是:

  • “一定时间”可以适当延长

  • 当举行大促时,响应时间可以适当延长

  • 给部分用户返回一个降级页面

  • 给部分用户直接返回一个降级页面,从而缓解服务器压力。但要注意,返回降级页面仍然是返回明确结果。

    S:Soft State:柔性状态。同一数据的不同副本的状态,可以不需要实时一致。

    E:Eventual Consisstency:最终一致性。同一数据的不同副本的状态,可以不需要实时一致,但一定要保证经过一定时间后仍然是一致的。

 

    典型场景

  • 跨库事务。一个应用某个功能需要操作多个库,不同的库中存储不同的业务数据。

  • 分库分表。 通常一个库数据量比较大或者预期未来的数据量比较大,都会进行水平拆分,也就是分库分表。

  • 服务化(SOA)。微服务架构是目前一个比较一个比较火的概念。某个应用同时操作了9个库,这样的应用业务逻辑必然非常复杂,对于开发人员是极大的挑战,应该拆分成不同的独立服务,以简化业务逻辑。拆分后,独立服务之间通过RPC框架来进行远程调用,实现彼此的通信。

 

四、解决方案

       基于XA协议的两阶段提交。XA是一个分布式事务协议,由Tuxedo提出。XA中大致分为两部分:事务管理器和本地资源管理器。其中本地资源管理器往往由数据库实现,比如Oracle、DB2这些商业数据库都实现了XA接口,而事务管理器作为全局的调度者,负责各个本地资源的提交和回滚。XA协议比较简单,而且一旦商业数据库实现了XA协议,使用分布式事务的成本也比较低。但是,XA也有致命的缺点,那就是性能不理想,特别是在交易下单链路,往往并发量很高,XA无法满足高并发场景。XA目前在商业数据库支持的比较理想,在mysql数据库中支持的不太理想,mysql的XA实现,没有记录prepare阶段日志,主备切换回导致主库与备库数据不一致。许多nosql也没有支持XA,这让XA的应用场景变得非常狭隘。

        消息事务+最终一致性。所谓的消息事务就是基于消息中间件的两阶段提交,本质上是对消息中间件的一种特殊利用,它是将本地事务和发消息放在了一个分布式事务里,保证要么本地操作成功成功并且对外发消息成功,要么两者都失败,开源的RocketMQ就支持这一特性。

  • A系统向消息中间件发送一条预备消息

  • 消息中间件保存预备消息并返回成功

  • A执行本地事务

  • A发送提交消息给消息中间件

       分析:

  • 步骤一出错,则整个事务失败,不会执行A的本地操作

  • 步骤二出错,则整个事务失败,不会执行A的本地操作

  • 步骤三出错,这时候需要回滚预备消息,怎么回滚?答案是A系统实现一个消息中间件的回调接口,消息中间件会去不断执行回调接口,检查A事务执行是否执行成功,如果失败则回滚预备消息

  • 步骤四出错,这时候A的本地事务是成功的,那么消息中间件要回滚A吗?答案是不需要,其实通过回调接口,消息中间件能够检查到A执行成功了,这时候其实不需要A发提交消息了,消息中间件可以自己对消息进行提交,从而完成整个消息事务

       TCC编程模式

        所谓的TCC编程模式,也是两阶段提交的一个变种,TCC方案在电商、金融领域落地较多。。TCC提供了一个编程框架,将整个业务逻辑分为三块:Try、Confirm和Cancel三个操作。以在线下单为例,Try阶段会去扣库存,Confirm阶段则是去更新订单状态,如果更新订单失败,则进入Cancel阶段,会去恢复库        存。总之,TCC就是通过代码人为实现了两阶段提交,不同的业务场景所写的代码都不一样,复杂度也不一样,因此,这种模式并不能很好地被复用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值