【转贴】数据库事务的四大特性、隔离级别、传播行为、锁机制

数据库事务

概念

数据库事务( transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位 

作用

一个数据库事务通常包含了一个序列的对数据库的读/写操作。它的存在包含有以下两个目的:

  1. 为数据库操作序列提供了一个从失败中恢复到正常状态的方法,同时提供了数据库即使在异常状态下仍能保持一致性的方法。
  2. 当多个应用程序在并发访问数据库时,可以在这些应用程序之间提供一个隔离方法,以防止彼此的操作互相干扰。

当事务被提交给了DBMS(数据库管理系统),则DBMS(数据库管理系统)需要确保该事务中的所有操作都成功完成且其结果被永久保存在数据库中,如果事务中有的操作没有成功完成,则事务中的所有操作都需要被回滚,回到事务执行前的状态;同时,该事务对数据库或者其他事务的执行无影响,所有的事务都好像在独立的运行。

但在现实情况下,失败的风险很高。在一个数据库事务的执行过程中,有可能会遇上事务操作失败、数据库系统/操作系统失败,甚至是存储介质失败等情况。这便需要DBMS对一个执行失败的事务执行恢复操作,将其数据库状态恢复到一致状态(数据的一致性得到保证的状态)。为了实现将数据库状态恢复到一致状态的功能,DBMS通常需要维护事务日志以追踪事务中所有影响数据库数据的操作。

事务(Transaction)ACID属性

事务是由一组SQL语句组成的逻辑处理单元,事务具有4属性,通常称为事务的ACID属性。

  • 原子性(Actomicity):事务是一个原子操作单元,其对数据的修改,要么全都执行,要么全都不执行。
  • 一致性(Consistent):
    • 几个并行执行的事务,其执行结果必须与按某一顺序串行执行的结果相一致 (百度百科)
    • 一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。

如:转账,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。

  • 隔离性(Isolation):
    • 数据库系统提供一定的隔离机制,保证事务在不受外部并发操作影响的“独立”环境执行。这意味着事务处理过程中的中间状态对外部是不可见的,反之亦然。
    • 隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
    • 即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。
  • 持久性(Durable):事务完成之后,它对于数据的修改是永久性的,即使出现系统故障(数据库系统、操作系统和计算机硬件故障)也能够保持。

并发事务带来的问题

相对于串行处理来说,并发事务处理能大大增加数据库资源的利用率,提高数据库系统的事务吞吐量,从而可以支持可以支持更多的用户。但并发事务处理也会带来一些问题,主要包括以下几种情况。:

  • 更新丢失(Lost update) :两个事务T1和T2读入同一个数据并修改,T2提交的结果覆盖了T1提交的结果,导致T1的修改被丢失。 
  • 脏读(Dirty Reads) :
    • 事务T1对数据进行修改,但是还没有提交时,事务T2读取数据进行修改,此时T2读取的是T1修改了的值。但是突然由于某种原因T1进行了回滚,这时候数据恢复了原来的值,而T2取得的数据依然是T1修改的值,这就导致了数据库中的值与事务获取的值不同的现象。也就是读到了不正确的值,这就叫脏读。
    • 比如张三工资为5000元,事务A将他的工资改为8000元,在事务A还未提交时,事务B正在读取张三的工资,读取的值为8000.这时候突然由于事务A发生了异常进行了回滚,张三的工资恢复成了5000元,而事务B读取的工资却是8000元,读取的值与数据库中的值不同,这就是脏读。
  • 非重复读(Non-repeatable Reads) 
    •  是指在一个事务内,多次读取同一数据。在这个事务还没结束时,另外一个事务也访问该同一数据,并对该数据进行了修改。此时第一个事务再去读此数据时读到的结果与之前的结果不同。这就导致了在一个事务内两次相同的查询读到的数据是不一样的,因此称为是不可重复读。
    •  比如:在事务A中读到的张三的工资是5000元,操作还没有完成,事务还没提交。与此同时,事务B把张三的工资改为了8000,并提交了事务。随后,在事务A中,再次读取张三的工资,此时工资变为8000元。在A事务这一个事务内前后两次读取的结果不一致,导致了不可重复读。
  • 幻读(Phantom Reads) 
    •  例如:目前工资为5000元的员工有10个人,事务A读取所有工资为5000元的员工的人数为10人。此时事务B插入一条工资也为5000的记录。这时,事务A再次读取工资为5000元的员工,记录为11人。此时产生了幻读。

脏读和幻读的区别

  • 不可重复读和幻读的情况很类似,但是不可重复读的重点是修改:同样条件下,你读取过的数据,再次读取出来发现值不一样了。
  • 幻读的重点在于新增或者删除:同样条件下,第一次和第二次读出来的记录数不一样。

“脏读”、“不可重复读”和“幻读”,其实都是数据库读一致性问题,避免不一致的方法和技术就是进行并发控制。最常用的就是封锁技术,但封锁技术的缺点是会造成死锁和性能下降。为了兼顾并发效率与异常控制,定义了4中隔离级别,接下来将介绍隔离级别。

数据库事务的隔离级别

在并发事务处理带来的问题中,“更新丢失”通常应该是完全避免的。但防止更新丢失,并不能单靠数据库事务控制器来解决,需要应用程序对要更新的数据加必要的锁来解决,因此,防止更新丢失应该是应用的责任。

隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。

大多数数据库的默认级别就是Read committed,比如Sql Server , Oracle。

Mysql的默认隔离级别就是可重复读取。

1、Read uncommitted(读未提交数据):

       如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。

        避免了更新丢失,却可能出现脏读、不可重复读、幻读的情况。

2、Read committed(读已提交数据):

       读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。

       该隔离级别避免了脏读,但是却可能出现不可重复读、幻读的情况。

3、Repeatable read(可重复读):

       读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。

       避免了不可重复读取和脏读,但是有时可能出现幻读。这中隔离级别可以通过“共享读锁”和“排他写锁”实现。

4、Serializable(可串行化):

       提供严格的事务隔离。它要求事务串行化执行,事务只能一个接着一个地执行,但不能并发执行。如果仅仅通过“行级锁”是无法实现事务串行化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。

       串行化是最高的事务隔离级别,同时代价也花费最高,性能很低,一般很少使用,在该级别下,事务顺序执行,不仅可以避免脏读、不可重复读,还避免了幻像读。

对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed。它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、幻读和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。

传播行为

  在我们用SSH开发项目的时候,我们一般都是将事务设置在Service层,那么当我们调用Service层的一个方法的时候它能够保证我们的这个方法中执行的所有的对数据库的更新操作保持在一个事务中,在事务层里面调用的这些方法要么全部成功,要么全部失败。那么事务的传播特性也是从这里说起的。 
  如果你在你的Service层的这个方法中,除了调用了Dao层的方法之外,还调用了本类的其他的Service方法,那么在调用其他的Service方法的时候,这个事务是怎么规定的呢,我必须保证我在我方法里掉用的这个方法与我本身的方法处在同一个事务中,否则如果保证事物的一致性。事务的传播特性就是解决这个问题的,“事务是会传播的”在Spring中有针对传播特性的多种配置我们大多数情况下只用其中的一种:PROPGATION_REQUIRED:这个配置项的意思是说当我调用service层的方法的时候开启一个事务(具体调用那一层的方法开始创建事务,要看你的aop的配置),那么在调用这个service层里面的其他的方法的时候,如果当前方法产生了事务就用当前方法产生的事务,否则就创建一个新的事务。

  • PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
  • PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。‘
  • PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
  • PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。
  • PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
  • PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。



https://www.cnblogs.com/zhumengke/articles/9480580.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值