美文网首页
10分钟学会Spring的事务管理机制

10分钟学会Spring的事务管理机制

作者: 谦卑王生 | 来源:发表于2019-01-06 20:21 被阅读0次

    前言

    在互联网数据库的使用中,对于那些电商和金融网站,最关注的内容毫无疑问就是数据库事务,因为对于人们商品的交易和库存以及金融产品的金额,是不允许发生错误的。但是它们面临的问题是,热门产品或金融产品上线销售瞬间可能面对的高并发的场景。那么在Spring中采用哪些事务机制处理这些高并发场景的呢?

    Spring事务管理

    常见的Spring事务管理有以下两种:

    1. 编程式事务管理
    • 编程式事务管理需要显示的调用beginTransaction()、rollback()、commit()等相关事务的处理方法,操作比较复杂,不推荐。
    2.声明式事务管理
    • 基于AOP技术实现的声明式事务管理,实质就是:在方法执行前后进行拦截,然后在目标方法执行前创建并加入事务,在目标方法执行后根据结果进行事务的回滚或者提交。
    • 声明式事务管理的实现有两种:
    1. 基于XML配置文件的实现;
      2.在业务方法上加上@Transactional注解,将事务规则应用到业务逻辑中。

    对于声明式事务,是使用@Transactional进行标注的,这个注解可以标注在类或方法上,当它标注在类上时,代表这个类的所有公共的(public)非静态的方法都将启动事务功能。在@Transactional中,还允许配置许多的属性,如事务的传播行为和隔离级别,以及异常类型,从而确定方法发生什么异常下回滚事务什么异常下不回滚事务等。这些配置内容,是在Spring IoC容器在加载时就会将这些配置信息解析出来,然后把这些信息存到事务定义器(TransactionDefinition接口的实现类)里,记录哪些类或者方法需要启动事务功能,采取什么策略去执行事务。在这个过程中,我们需要做的就是给需要事务的类或方法中加上@Transactional注解和配置其属性而已。
    有了@Transactional的配置,Spring就会知道在哪里启动事务机制,其约定流程如下图所示:


    image.png
    3.隔离级别

    当前互联网应用时刻面临着高并发的环境,如商品库存,时刻都是多个线程共享的数据,这样就会在多线程的环境中扣减商品库存。对于数据库而言,就会出现多个事务同时操作同一记录的情况,这样会引起数据出现不一致的情况,便是数据的丢失更新(Lost Update)问题。

    数据库事务的知识

    数据库事务具有以下4个基本特性,也就是著名的ACID。

    • Atomic(原子性):事务中包含的操作被看做一个整体的业务单元,这个业务单元的操作要么全部成功,要么全部失败,不会出现部分成功,部分失败的现象。
    • Consistency(一致性):事务在完成时,必须使所有的数据都保持一致状态,在数据库中所有的修改都基于事务,保证了数据的完整性,
    • Isolation(隔离性):可能多个应用程序线程同时访问同一数据,这样数据库同样的数据在各个不同的事务中被访问,这样会产生丢失更新,为了压制丢失更新的产生,数据库定义了隔离级别的概念,通过它的选择,可以在不同程度上压制数据丢失更新的产生,因为互联网的应用常常面对高并发的场景,所以隔离性是需要掌握的重点内容。
    • Durability(持久性):事务结束后,所有的数据都会固定到一个地方,如保存到磁盘中,即使断电重启后也可以提供给应用程序访问。
    第一类丢失更新
    时刻 事 务 1 事 务 2
    T1 初始库存100 初始库存100
    T2 扣减库存,余99 ——
    T3 —— 扣减库存,余99
    T4 提交事务,库存变为99
    T5 回滚事务,库存100

    可以看到,T5时刻事务回滚,导致原本库存为99的变为了100,显然事务2的结果就丢失了,这就是一个错误的值,类似的,对于这样一个事务提交而引发的数据不一致的情况,我们称为第一类丢失更新

    第二类丢失
    时刻 事 务 1 事 务 2
    T1 初始库存100 初始库存100
    T2 扣减库存,余99 ——
    T3 —— 扣减库存,余99
    T4 —— 提交事务,库存为99
    T5 提交事务,库存变为99 ——

    注意在T5时刻提交的事务。因为在事务1中,无法感知事务2 的操作,这样它就不知道事务2已经做了修改,因此它依旧认为这只是发生了一笔业务,所以库存更新变成了99,而这个结果又是一个错误的结果。这样T5时刻事务1提交的事务,就会引发事务2提交结果的丢失,我们把这样的多个事务的提交引发丢失更新的称为第二类丢失更新

    为处理第二类丢失更新引发的错误,提出了事务的隔离级别,常见的隔离级别有以下4种:

    • 未提交读
    • 读写提交
    • 可重读
    • 串行化
      下面我们来一一细说这四个隔离级别的作用
    1.未提交读

    未提交读(read uncommitted)是最低的得力级别,其含义是允许一个事务读取另一个事务没有提交的数据。未提交读是一种危险的隔离级别。所以我们一般在实际的开发中应用不广,但是它的优点在于并发能力高,适合那些对数据一致性没有要求而追求高并发的场景,它的最大坏处是出现脏读。

    脏读现象
    时刻 事 务 1 事 务 2 备 注
    T0 ....... ........ 商品库存初始化为2
    T1 读取库存为2
    T2 扣减库存 库存为1
    T3 扣减库存 库存为0,读取事务1为提交的数据
    T4 提交事务 库存保存为0
    T5 回滚事务 因为第一类丢失更新已经克服,所以不会回滚为2,库存为0,结果错误

    因为采用未提交读,所以事务2可以读取事务1为提交的库存数据为1,这里当它扣减库存后则数据为0,然后它提交了事务,库存就变成了0,而事务1在T5时刻回滚事务,因为第一类丢失更新已经被克服,所以它不会将库存回滚到2,那么最后的结果就变成了0,所以就出现了这样的错误,脏读一般是比较危险的隔离级别,在我们实际应用中采用的不多。

    未待完续,请持续关注~

    相关文章

      网友评论

          本文标题:10分钟学会Spring的事务管理机制

          本文链接:https://www.haomeiwen.com/subject/ipvpfqtx.html