数据库事务中的隔离级别和锁+spring Transactional注解

数据库事务中的隔离级别和锁

数据库事务在后端开发中占非常重要的地位,如何确保数据读取的正确性、安全性也是我们需要研究的问题。

ACID

首先总结一下数据库事务正确执行的四个要素(ACID):

原子性(Atomicity):即事务是不可分割的最小工作单元,事务内的操作要么全做,要么全不做,不能只做一部分;
一致性(Consistency):在事务执行前数据库的数据处于正确的状态,而事务执行完成后数据库的数据还是处于正确的状态,即数据完整性约束没有被破坏;比如我们做银行转账的相关业务,A转账给B,要求A转的钱B一定要收到。如果A转了钱而B没有收到,那么数据库数据的一致性就得不到保障,在做高并发业务时要注意合理的设计。
隔离性(Isolation):并发事务执行之间无影响,在一个事务内部的操作对其他事务是不产生影响,这需要事务隔离级别来指定隔离性;
持久性(Durability):事务一旦执行成功,它对数据库的数据的改变必须是永久的,不会因各种异常导致数据不一致或丢失。

事务隔离级别

大部分数据库事务操作都是并发执行的,这就可能遇到下面的几种问题:

丢失更新:两个事务同时更新一行数据,最后一个事务的更新会覆盖掉第一个事务的更新,从而导致第一个事务更新的数据丢失,后果比较严重。一般是由于没加锁的原因造成的。
脏读(Dirty reads):一个事务A读取到了另一个事务B还没有提交的数据,并在此基础上进行操作。如果B事务rollback,那么A事务所读取到的数据就是不正确的,会带来问题。
不可重复读(Non-repeatable reads):在同一事务范围内读取两次相同的数据,所返回的结果不同。比如事务B第一次读数据后,事务A更新数据并commit,那么事务B第二次读取的数据就与第一次是不一样的。
幻读(Phantom reads):一个事务A读取到了另一个事务B新提交的数据。比如,事务A对一个表中所有行的数据按照某规则进行修改(整表操作),同时,事务B向表中插入了一行原始数据,那么后面事务A再对表进行操作时,会发现表中居然还有一行数据没有被修改,就像发生了幻觉,飘飘欲仙一样。
注意:不可重复读和幻读的区别是,不可重复读对应的表的操作是更改(UPDATE),而幻读对应的表的操作是插入(INSERT),两种的应对策略不一样。对于不可重复读,只需要采用行级锁防止该记录被更新即可,而对于幻读必须加个表级锁,防止在表中插入数据。有关锁的问题,下面会讨论。

为了处理这几种问题,SQL定义了下面的4个等级的事务隔离级别:

未提交读(READ UNCOMMITTED ):最低隔离级别,一个事务能读取到别的事务未提交的更新数据,很不安全,可能出现丢失更新、脏读、不可重复读、幻读;
提交读(READ COMMITTED):一个事务能读取到别的事务提交的更新数据,不能看到未提交的更新数据,不会出现丢失更新、脏读,但可能出现不可重复读、幻读;
可重复读(REPEATABLE READ):保证同一事务中先后执行的多次查询将返回同一结果,不受其他事务影响,不可能出现丢失更新、脏读、不可重复读,但可能出现幻读;
序列化(SERIALIZABLE):最高隔离级别,不允许事务并发执行,而必须串行化执行,最安全,不可能出现更新、脏读、不可重复读、幻读,但是效率最低。
隔离级别越高,数据库事务并发执行性能越差,能处理的操作越少。所以一般地,推荐使用REPEATABLE READ级别保证数据的读一致性。对于幻读的问题,可以通过加锁来防止。
MySQL支持这四种事务等级,默认事务隔离级别是REPEATABLE READ。Oracle数据库支持READ COMMITTED 和 SERIALIZABLE这两种事务隔离级别,所以Oracle数据库不支持脏读。Oracle数据库默认的事务隔离级别是READ COMMITTED。

各种锁

下面总结一下MySQL中的锁,有好几种分类。其它RDBMS也差不多是这样。

首先最重要的分类就是乐观锁(Optimistic Lock)和悲观锁(Pessimistic Lock),这实际上是两种锁策略。
乐观锁,顾名思义就是非常乐观,非常相信真善美,每次去读数据都认为其它事务没有在写数据,所以就不上锁,快乐的读取数据,而只在提交数据的时候判断其它事务是否搞过这个数据了,如果搞过就rollback。乐观锁相当于一种检测冲突的手段,可通过为记录添加版本或添加时间戳来实现。
悲观锁,对其它事务抱有保守的态度,每次去读数据都认为其它事务想要作祟,所以每次读数据的时候都会上锁,直到取出数据。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性,但随之而来的是各种开销。悲观锁相当于一种避免冲突的手段。
选择标准:如果并发量不大,或数据冲突的后果不严重,则可以使用乐观锁;而如果并发量大或数据冲突后果比较严重(对用户不友好),那么就使用悲观锁。

从读写角度,分共享锁(S锁,Shared Lock)和排他锁(X锁,Exclusive Lock),也叫读锁(Read Lock)和写锁(Write Lock)
理解:

持有S锁的事务只读不可写。如果事务A对数据D加上S锁后,其它事务只能对D加上S锁而不能加X锁。
持有X锁的事务可读可写。如果事务A对数据D加上X锁后,其它事务不能再对D加锁,直到A对D的锁解除。

从锁的粒度角度,主要分为表级锁(Table Lock)和行级锁(Row Lock)。
表级锁将整个表加锁,性能开销最小。用户可以同时进行读操作。当一个用户对表进行写操作时,用户可以获得一个写锁,写锁禁止其他的用户读写操作。写锁比读锁的优先级更高,即使有读操作已排在队列中,一个被申请的写锁仍可以排在所队列的前列。
行级锁仅对指定的记录进行加锁,这样其它进程可以对同一个表中的其它记录进行读写操作。行级锁粒度最小,开销大,能够支持高并发,可能会出现死锁。

MySQL的MyISAM引擎使用表级锁,而InnoDB支持表级锁和行级锁,默认是行级锁。
还有BDB引擎使用页级锁,即一次锁定一组记录,并发性介于行级锁和表级锁之间。

三级锁协议

三级加锁协议是为了保证正确的事务并发操作,事务在读、写数据库对象是需要遵循的加锁规则。

一级封锁协议:事务T在修改数据R之前必须对它加X锁,直到事务结束方可释放。而若事务T只是读数据,不进行修改,则不需加锁,因此一级加锁协议下可能会出现脏读和不可重复读。
二级加锁协议:在一级加锁协议的基础上,加上这样一条规则——事务T在读取数据R之前必须对它加S锁,直到读取完毕以后释放。二级加锁协议下可能会出现不可重复读。
三级加锁协议:在一级加锁协议的基础上,加上这样一条规则——事务T在读取数据R之前必须对它加S锁,直到事务结束方可释放。三级加锁协议避免了脏读和不可重复读的问题。

spring @Transactional注解参数详解

事物注解方式: @Transactional

当标于类前时, 标示类中所有方法都进行事物处理 , 例子:

1 @Transactional public class TestServiceBean implements TestService {}
当类中某些方法不需要事物时:

复制代码
 @Transactional 
 public class TestServiceBean implements TestService { 
 private TestDao dao; 
 public void setDao(TestDao dao) { 
 this.dao = dao; 
 } 
 @Transactional(propagation =Propagation.NOT_SUPPORTED)
 public List getAll() { 
 return null; 
 } 
 }
复制代码

事物传播行为介绍:

  @Transactional(propagation=Propagation.REQUIRED) :如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)
  @Transactional(propagation=Propagation.NOT_SUPPORTED) :容器不为这个方法开启事务
  @Transactional(propagation=Propagation.REQUIRES_NEW) :不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
  @Transactional(propagation=Propagation.MANDATORY) :必须在一个已有的事务中执行,否则抛出异常
  @Transactional(propagation=Propagation.NEVER) :必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
  @Transactional(propagation=Propagation.SUPPORTS) :如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.

事物超时设置:

  @Transactional(timeout=30) //默认是30秒

事务隔离级别:

  @Transactional(isolation = Isolation.READ_UNCOMMITTED):读取未提交数据(会出现脏读, 不可重复读) 基本不使用
  @Transactional(isolation = Isolation.READ_COMMITTED):读取已提交数据(会出现不可重复读和幻读)
  @Transactional(isolation = Isolation.REPEATABLE_READ):可重复读(会出现幻读)
  @Transactional(isolation = Isolation.SERIALIZABLE):串行化

  MYSQL: 默认为REPEATABLE_READ级别
  SQLSERVER: 默认为READ_COMMITTED

脏读 : 一个事务读取到另一事务未提交的更新数据

不可重复读 : 在同一事务中, 多次读取同一数据返回的结果有所不同, 换句话说, 后续读取可以读到另一事务已提交的更新数据. 相反, "可重复读"在同一事务中多次读取数据时, 能够保证所读数据一样, 也就是后续读取不能读到另一事务已提交的更新数据

幻读 : 一个事务读到另一个事务已提交的insert数据

@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表示永不超时

注意的几点:
  1、@Transactional 只能被应用到public方法上, 对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能.
  2、用 spring 事务管理器,由spring来负责数据库的打开,提交,回滚.默认遇到运行期例外(throw new RuntimeException("注释");)会回滚,即遇到不受检查(unchecked)的例外时回滚;而遇到需要捕获的例外(throw new Exception("注释");)不会回滚,即遇到受检查的例外(就是非运行时抛出的异常,编译器会检查到的异常叫受检查例外或说受检查异常)时,需我们指定方式来让事务回滚要想所有异常都回滚,要加上 @Transactional( rollbackFor={Exception.class,其它异常}) .如果让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)
如下:

复制代码
@Transactional(rollbackFor=Exception.class) //指定回滚,遇到异常Exception时回滚
public void methodName() {
   throw new Exception("注释");
}
@Transactional(noRollbackFor=Exception.class)//指定不回滚,遇到运行期例外(throw new RuntimeException("注释");)会回滚
public ItimDaoImpl getItemDaoImpl() {
   throw new RuntimeException("注释");
}
复制代码
3、@Transactional 注解应该只被应用到 public 可见度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 但是这个被注解的方法将不会展示已配置的事务设置。
  4、@Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上。然而,请注意仅仅 @Transactional 注解的出现不足于开启事务行为,它仅仅 是一种元数据,能够被可以识别 @Transactional 注解和上述的配置适当的具有事务行为的beans所使用。上面的例子中,其实正是 元素的出现 开启 了事务行为。
  5、Spring团队的建议是你在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。你当然可以在接口上使用 @Transactional 注解,但是这将只能当你设置了基于接口的代理时它才生效。因为注解是不能继承的,这就意味着如果你正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装(将被确认为严重的)。因此,请接受Spring团队的建议并且在具体的类上使用 @Transactional 注解。

原文地址:https://www.cnblogs.com/AnXinliang/p/9850876.html

时间: 2024-10-13 01:11:14

数据库事务中的隔离级别和锁+spring Transactional注解的相关文章

数据库事务特性和隔离级别

事务 一组业务操作,要么全部成功,要么全部不成功. ----------------事务的特性-------------- 原子性:一个事务是一个整体,不可分割,事务中的操作要么都成功,要么都失败. 一致性:事务必须是使数据库从一个一致性状态变到另一个一致性状态.事务执行前后,数据库的状态是一致的. 例如:A有1000,元,B有1000元,加起来是2000,中间执行转账的操作,转账后两个人的钱加起来还是2000. 隔离性:两个或多个事务并发操作,之间会互相影响.数据库提供了隔离级别来消除这些影响

MYSQL数据库事务4种隔离级别及7种传播行为

事务的特性: 原子性:事务的不可分割,组成事务的各个逻辑单元不可分割. 一致性:事务执行的前后,数据完整性保持一致. 隔离性:事务执行不应该受到其他事务的干扰. 持久性:事务一旦结束,数据就持久化到数据库中. 查看/设置隔离级别 查看:SELECT @@tx_isolation  设置:set tx_isolation='xxx' 事务的隔离级别 如果不考虑隔离性,引发一些安全问题 隔离性:一个事务的执行,不应该受到其他事务的干扰. 脏读:一个事务读到了另一个事务未提交的数据,导致查询结果不一致

Spring事务中的隔离级别

TransactionDefinition接口中定义了五个表示隔离级别的常量: TransactionDefinition.ISOLATION_DEFAULT:使用后端数据库默认的隔离界别,MySQL默认采用的REPEATABLE_READ隔离级别,Oracle默认采用的READ_COMMITTED隔离级别. TransactionDefinition.ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取,允许读取尚未提交的的数据变更,可能会导致脏读.幻读或不可重复读.

MySQL数据库引擎、事务隔离级别、锁

MySQL数据库引擎.事务隔离级别.锁 数据库引擎InnoDB和MyISAM有什么区别 大体区别为: MyISAM类型不支持事务处理等高级处理,而InnoDB类型支持.MyISAM类型的表强调的是性能,其执行效率比InnoDB类型更快,但是不支持事务,而InnoDB提供事务支持以及外键等高级数据库功能. 具体实现的区别: InnoDB不支持FULLTEXT类型的索引 InnoDB中不保存表的具体行数,也就是说,执行查询SQL时,InnoDB要扫描一遍整个表来计算有多少行,而MyISAM只要简单的

数据库事务隔离级别与锁

一.事务的4个基本特征     所谓事务是用户定义的一个数据库操作序列,这些操作要么全做要么全不做,是一个不可分割的工作单位.例如,在关系数据库中,一个事务可以是一条SQL语句.一组SQL语句或整个程序. 事务ACID特性. ACID就是:原子性(Atomicity ).一致性( Consistency ).隔离性或独立性( Isolation)和持久性(Durabilily). 事务和程序是两个概念.一般地讲,一个程序中包含多个事务.事务的开始与结束可以由用户显式控制.如果用户没有显式地定义事

数据库的四种隔离级别和事务的四特性

数据库事务的四大特性: 事务(Transaction)是并发控制的基本单位.所谓事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位.例如,银行转帐工作:从一个帐号扣款并使另一个帐号增款,这两个操作要么都执行,要么都不执行. 1.数据库事务必须具备ACID特性,ACID是Atomic(原子性).Consistency(一致性).Isolation(隔离性)和Durability(持久性)的英文缩写. 1)原子性(Atomicity) 一个事务(transactio

数据库事务的四大特性以及事务的隔离级别-与-Spring事务传播机制&隔离级别

本篇讲诉数据库中事务的四大特性(ACID),并且将会详细地说明事务的隔离级别. 如果一个数据库声称支持事务的操作,那么该数据库必须要具备以下四个特性: ⑴ 原子性(Atomicity) 原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,这和前面两篇博客介绍事务的功能是一样的概念,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响. ⑵ 一致性(Consistency) 一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执

MySQL事务及实现、隔离级别及锁与优化

事务  事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消.事务是逻辑上的一组操作,要么都执行,要么都不执行. ACID简介 原子性(Atomicity).一致性(Correspondence).隔离性(Isolation).持久性(Durability). (1)原子性:整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节.事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一

事务的传播行为,隔离级别以及锁机制

脏读,不可重复读和幻读. 脏读:读到未提交(uncommited)的数据 不可重复读:多次查询中(提交插入数据的事务) 幻读:在修改事务后(提交了一个添加的事务),之后查询数据以为出现幻觉 事务的隔离级别: read_uncommitted 最低的隔离级别 有脏读,不可重复读和幻读. read_committed 看到已提交的数据 存在不可重复读和幻像读. 支持sqlsever repeatable_read 存在幻读 .支持mysql serializable 防止脏读,不可重复读外,还避免了