数据库事务与spring

更新时间:2023-07-24 20:01:01 阅读量: 实用文档 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

全面的介绍了数据库事务的概念,并发引起的问题,隔离级别和传播行为,乐观锁和悲观锁

事务的ACID特性

1.原子性(Atomicity)

表示组成事务的多个数据库操作是一个不可分割的工作单元,只有所有的操作都执行成功,整个事务才提交,事务中任何一个数据库操作(SQL语句)失败,已经成功执行的任何操作都必须撤销,让数据库返回到事务执行前的状态。

2.一致性(Consistency)

数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性。事务操作成功后,数据库所处的状态和它的业务规则是一致的,即数据一致性不会被破坏。例如从A账户转账100元到B账户,不管操作成功与否,A和B的存款总额是不变的。

也就是说数据库要一直处于一致的状态,事务开始前是一个一致状态,事务结束后是另一个一致状态,事务将数据库从一个一致状态转移到另一个一致状态

3.独立性(Isolation)

所谓的独立性就是指并发的事务之间不会互相影响,如果一个事务要访问的数据正在被另外一个事务修改,只要另外一个事务还未提交,它所访问的数据就不受未提交事务的影响。换句话说,一个事务

全面的介绍了数据库事务的概念,并发引起的问题,隔离级别和传播行为,乐观锁和悲观锁

的影响在该事务提交前对其它事务是不可见的。一个事务都感觉不到系统中有其他事务在并发执行。

4.持久性(Durability)

一旦事务提交成功后,事务中所有的数据操作都必须被持久化到数据库中。即使提交事务后,数据库系统崩溃,也必须保证能够通过某种机制恢复事务已经提交的数据。

事务并发引起的问题

下面例子来自孙卫琴的《精通Hibernate》:

在并发的环境之中,一个数据库系统会同时为各种各样的客户端提供服务,对于同时运行的多个事务,当这些事务访问数据库中相同的数据时如果没有采取必要的隔离机制,就会出现各种并发问题,可以把这些问题归纳为一下五种:

假设现在一所银行有一个取款事务和一个支票转账事务操作同一个银行账户的情形,首先我们假设两个事务顺序执行,而不是并发执行,那么整个的步骤如下:

---- 银行客户在银行前台请求取款100元,出纳员先查询账户信息,得知存款余额为1000元。

---- 出纳员判断存款余额大于取款额,支付给客户100元,并将账户上的存款余额改为900元。

---- 出纳员处理一张转账支票,该支票向此账户汇入100元,出纳员

全面的介绍了数据库事务的概念,并发引起的问题,隔离级别和传播行为,乐观锁和悲观锁

先查询账户信息,得知存款余额为900元。 ---- 出纳员将存款余额改为1000元。

可以很清楚的看到,如果顺序的执行这两个事务,不会出现任何问题,可是现实之中并非如此简单,如果两个事务分别由两个出纳员同时执行那麽可能就会出现并发的问题,下面我们分别来介绍:

1.第一类丢失更新

这样的话,用户损失了100元,数据库中的数据出现了不一致。 A事务撤销的时候,把已经提交的B事务所做的数据更新覆盖了。

2.第二类丢失更新

全面的介绍了数据库事务的概念,并发引起的问题,隔离级别和传播行为,乐观锁和悲观锁

这样的话,银行就损失了100元。

B事务提交的时候,覆盖了A事务已经提交的数据,导致A事务所做的操作丢失。

3.脏读

全面的介绍了数据库事务的概念,并发引起的问题,隔离级别和传播行为,乐观锁和悲观锁

事务B读取了事务A已经修改但是还没有提交的数据,并且基于了脏数据进行操作。如果A事务回滚,那么B事务读取的数据其实是不合法的。

4.不可重复读

查询却返回了不同数据。这是由于查询时系统中其他事务修改的提交而引起的。比如事务T1读取某一数据,事务T2读取并修改了该数据,T1为了对读取值进行检验而再次读取该数据,便得到了不同的结果。

全面的介绍了数据库事务的概念,并发引起的问题,隔离级别和传播行为,乐观锁和悲观锁

一种更易理解的说法是:在一个事务内,多次读同一个数据。在这个事务还没有结束时,另一个事务也访问该同一数据。那么,在第一个事务的两次读数据之间。由于第二个事务的修改,那么第一个事务读到的数据可能不一样,这样就发生了在一个事务内两次读到的数据是不一样的,因此称为不可重复读,即原始读取不可重复。

5.幻读/虚读

不一致,这个时候事务B出现了幻象读。幻象读一般发生在统计事务中,如果另一个事务新增/删除的数据行,刚好满足当前统计事务的查询条件,就会导致多次统计结果不一致的情况。

全面的介绍了数据库事务的概念,并发引起的问题,隔离级别和传播行为,乐观锁和悲观锁

事务隔离级别

尽管数据库为用户提供了锁的DML操作方式,但直接使用锁管理是非常麻烦的,因此数据库为用户提供了自动锁机制。只要用户指定会话的事务隔离级别,数据库就会分析事务中的SQL语句,然后自动为事务操作的数据资源添加上适合的锁。此外数据库还会维护这些锁,当一个资源上的锁数目太多时,自动进行锁升级以提高系统的运行性能,而这一过程对用户来说完全是透明的。

ANSI/ISO SQL 92标准定义了4个等级的事务隔离级别,在相同数据环境下,使用相同的输入,执行相同的工作,根据不同的隔离级别,可以导致不同的结果。不同事务隔离级别能够解决的数据并发问题的能力是不同的。JDBC规范中,Java.sql.Connection接口定义了5个与事务隔离级别相关的常量。不同数据库支持的事务隔离级别可能不相同,标准的SQL规范定义了4种隔离级别。

TRANSACTION_NONE

不支持事务,会产生事务并发的所有问题。实际应用中,几乎是 不会使用这种数据库的。这个级别只是Connection中定义的常量,没有对应SQL标准。

TRANSACTION_READ_UNCOMMITTED

公司发工资了,领导把5000元打到singo的账号上,但是该事务并未提交,而singo正好去查看账户,发现工资已经到账,是5000

全面的介绍了数据库事务的概念,并发引起的问题,隔离级别和传播行为,乐观锁和悲观锁

元整,非常高兴。可是不幸的是,领导发现发给singo的工资金额不对,是2000元,于是迅速回滚了事务,修改金额后,将事务提交,最后singo实际的工资只有2000元,singo空欢喜一场。

出现上述情况,即我们所说的脏读。两个并发的事务,“事务A:领导给singo发工资”、“事务B:singo查询工资账户”,事务B读取了事务A尚未提交的数据。当事务隔离级别设置为Read uncommitted时,就可能出现脏读。

TRANSACTION_READ_COMMITTED

singo拿着工资卡去消费,系统读取到卡里有2000元,而此时她的老婆也正好在网上转账,把singo工资卡的2000元转到另一账户,并在singo之前提交了事务。当singo扣款时,系统检查到singo的工资卡已经没有钱,扣款失败,singo十分纳闷,明明卡里有钱,为何? 出现上述情况,即我们所说的不可重复读,两个并发的事务,“事务A:singo消费”、“事务B:singo的老婆网上转账”。事务A事先读取了数据,事务B紧接了更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变。

当隔离级别设置为Read committed时,可以避免脏读,而且具有较好的并发性能。大多数数据库的默认级别就是Read committed,如Oracle、Sql server。因为这个级别在数据库可靠性和性能之间取得均衡,基本能够满足大部分的应用。尽管它会导致不可重复读、虚读和第二类丢失更新,在可能出现这些问题的场合,可以由应用程序采用

全面的介绍了数据库事务的概念,并发引起的问题,隔离级别和传播行为,乐观锁和悲观锁

乐观锁或悲观所控制。

TRANSACTION_REPEATABLE_READ

当隔离级别设置为Repeatable read时,可以避免不可重复读。当singo拿着工资卡去消费时,一旦系统开始读取工资卡信息(即事务开始),singo的老婆就不可能对该记录进行修改,也就是singo的老婆不能在此时转账。虽然Repeatable read避免了不可重复读,但还有可能出现幻读。

假如singo的老婆工作在银行部门,她时常通过银行内部系统查看singo的信用卡消费记录。有一天,她正在查询到singo当月信用卡的总消费金额(select sum(amount) from transaction where month = 本月)为80元。而singo此时正好在外面吃完饭,在收银台买单,消费1000元,即新增了一条1000元的消费记录(insert into ... ),并提交了事务。随后singo的老婆将singo当月信用卡消费的明细打印到A4纸上,却发现消费总额为1080元,singo的老婆很诧异,以为出现了幻觉,幻读就这样产生了。

TRANSACTION_SERIALIZABLE

Serializable是最高的事务隔离级别,同时代价也花费最高,性能很 低,一般很少使用。在该级别下,事务顺序执行,不仅可以避免脏读、不可重复读,还避免了幻像读等所有事物并发可能导致的问题。

全面的介绍了数据库事务的概念,并发引起的问题,隔离级别和传播行为,乐观锁和悲观锁

不同的事务隔离级别能够解决的并发问题如下:

悲观所和乐观锁

为了得到最大的性能,一般数据库都有并发机制,不过带来的问题就是数据访问的冲突。为了解决这个问题,大多数数据库用的方法就是数据的锁定。There are mechanisms employed to manage the actions of multiple concurrent users on a database – the purpose is to prevent lost updates and dirty reads. The two types of locking are Pessimistic and Optimistic Locking.引用wiki的定义:

悲观锁:

悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度。在本事务的整个数据处理过程中,选定的数据一直处于锁定状态,直到该事务提交或回滚。

悲观锁是指假设并发更新冲突会发生,所以不管冲突是否真的

全面的介绍了数据库事务的概念,并发引起的问题,隔离级别和传播行为,乐观锁和悲观锁

发生,都会使用锁机制。悲观锁会完成以下功能:锁住读取的记录,防止其它事务读取和更新这些记录。其它事务会一直阻塞,直到这个事务结束。 悲观锁是在使用了数据库的事务隔离功能的基础上,独享占用的资源,以此保证读取数据一致性,避免修改丢失。

悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。如Oracle数据的 select * from table_name where id=100 for udate;就是悲观所。

乐观锁

乐观锁( Optimistic Locking )相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。而乐观锁机制在一定程度上解决了这个问题。

乐观锁,大多是基于数据版本( Version )记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。

全面的介绍了数据库事务的概念,并发引起的问题,隔离级别和传播行为,乐观锁和悲观锁

Spring事务传播行为

Spring事务传播行为说的就是当一个方法调用另一个方法时,事务应该如何操作。使用方式,就是使用@Transactional注解对方法进行标注,设定相应的属性,如

@Transactional(propagation=Propagation.MANDATORY) public void add(User user) { }

Propagation.MANDATORY

该方法必须运行在一个事务中。如果当前线程不存在事务,则抛出异常。

org.springframework.transaction.IllegalTransactionStateException:

No

existing transaction found for transaction marked with propagation 'mandatory'

Propagation.REQUIRED

该放在必须运行在一个事务中。如果已经有事务正在运行,该方法将运行在这个事务中;如果当前当前线程没有事务,则开启一个新的事务。

全面的介绍了数据库事务的概念,并发引起的问题,隔离级别和传播行为,乐观锁和悲观锁

Propagation.SUPPORTS

该方法可以不需要事务处理环境,但是如果已经有事务在运行的话,这个方法也在这个事务中运行。

本文来源:https://www.bwwdw.com/article/r3nm.html

Top