数据库事务基础

1、概述

1.事务概念

在数据库中,事务是指一组逻辑工做单元执行的一系列动做,要么都执行,要么都不执行。 html

2.事务ACID特性

原子性(Atomic)

原子性是指组成一个事务的多个数据库操做是一个不可分割的原子单元,事务中的操做要么都发生,要么都不发生。

一致性(Consistency)

事务必须使数据库从一个一致性状态变换到另一个一致性状态 (数据不被破坏) 。如从A帐户转帐100元到B帐户,无论操做成功与否,A和B的存款总额是不变的。

隔离性(Isolation)

在并发数据操做时,不一样的事务拥有各自的数据空间,它们的操做不会对对方产生干扰。准确地说,并不是要求作到彻底无干扰,数据库规定了多种事务隔离级别,不一样隔离级别对应不一样的干扰程度,隔离级别越高,数据一致性越好,但并发性越弱。

持久性(Durabiliy)

一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其余操做和数据库故障不该该对其有任何影响。 java

在这些事务特性中,数据“一致性”是最终目标,其余的特性都是为达到这个目标的措施、要求或手段。  sql

数据库管理系统采用日志来保证事务的原子性、一致性和持久性。日志记录了事务对数据库所作的更新,若是某个事务在执行过程当中发生错误,就能够根据日志,撤销事务对数据库已作的更新,使数据库退回到执行事务前的初始状态。此外,对于已经提交的事务,即便数据库崩溃,在重启数据库时也可以根据日志对还没有持久化的数据进行相应的重执行操做。 数据库

数据库管理系统采用锁机制来实现事务的隔离性。当多个事务同时更新数据库中相同的数据时,只容许持有锁的事务能更新该数据,其余事务必须等待,直到前一个事务释放了锁,其余事务才有机会更新该数据。Oracle数据库还使用了数据版本的机制,在回滚段为数据的每一个变化都保存一个版本,使数据的更改不影响数据的读取。 并发

2、数据并发的问题

一个数据库可能拥有多个访问客户端,这些客户端均可以并发方式访问数据库。数据库中的相同数据可能同时被多个事务访问,若是没有采起必要的隔离措施,就会致使各类并发问题,破坏数据的完整性。这些问题能够归结为5类,包括3类数据读问题(脏读、不可重复读和幻象读)以及2类数据更新问题(第一类丢失更新和第二类丢失更新)。下面,咱们分别经过实例讲解引起问题的场景。  性能

脏读(dirty read) 

A事务读取B事务还没有提交的更改数据,并在这个数据的基础上操做。若是恰巧B事务回滚,那么A事务读到的数据根本是不被认可的。来看取款事务和转帐事务并发时引起的脏读场景: spa

在这个场景中,B但愿取款500元然后又撤销了动做,而A往相同的帐户中转帐100元,就由于A事务读取了B事务还没有提交的数据,于是形成帐户白白丢失了500元。在Oracle数据库中,不会发生脏读的状况。  设计

不可重复读(unrepeatable read) 

不可重复读是指A事务读取了B事务已经提交的更改数据。假设A在取款事务的过程当中,B往该帐户转帐100元,A两次读取帐户的余额发生不一致:

在同一事务中,T4时间点和T7时间点读取帐户存款余额不同。  日志

幻象读(phantom read) 

A事务读取B事务提交的新增数据,这时A事务将出现幻象读的问题。幻象读通常发生在计算统计数据的事务中,举一个例子,假设银行系统在同一个事务中,两次统计存款帐户的总金额,在两次统计过程当中,恰好新增了一个存款帐户,并存入100元,这时,两次统计的总金额将不一致:

若是新增数据恰好知足事务的查询条件,这个新数据就进入了事务的视野,于是产生了两个统计不一致的状况。 code

幻象读和不可重复读是两个容易混淆的概念,前者是指读到了其余已经提交事务的新增数据,然后者是指读到了已经提交事务的更改数据(更改或删除),为了不这两种状况,采起的对策是不一样的,防止读取到更改数据,只须要对操做的数据添加行级锁,阻止操做中的数据发生变化,而防止读取到新增数据,则每每须要添加表级锁——将整个表锁定,防止新增数据(Oracle使用多版本数据的方式实现)。 

第一类丢失更新 

A事务撤销时,把已经提交的B事务的更新数据覆盖了。这种错误可能形成很严重的问题,经过下面的帐户取款转帐就能够看出来:

A事务在撤销时,“不当心”将B事务已经转入帐户的金额给抹去了。 

第二类丢失更新 

A事务覆盖B事务已经提交的数据,形成B事务所作操做丢失:

上面的例子里因为支票转帐事务覆盖了取款事务对存款余额所作的更新,致使银行最后损失了100元,相反若是转帐事务先提交,那么用户帐户将损失100元。

3、数据库锁机制

数据并发会引起不少问题,在一些场合下有些问题是容许的,但在另一些场合下可能倒是致命的。数据库经过锁的机制解决并发访问的问题,虽然不一样的数据库在实现细节上存在差异,但原理基本上是同样的。

按锁定的对象的不一样,通常能够分为表锁定和行锁定,前者对整个表进行锁定,然后者对表中特定行进行锁定。从并发事务锁定的关系上看,能够分为共享锁定和独占锁定。共享锁定会防止独占锁定,但容许其余的共享锁定。而独占锁定既防止其余的独占锁定,也防止其余的共享锁定。为了更改数据,数据库必须在进行更改的行上施加行独占锁定,INSERT、UPDATE、DELETE和SELECT FOR UPDATE语句都会隐式采用必要的行锁定。下面咱们介绍一下Oracle数据库经常使用的5种锁定。

行共享锁定

通常经过SELECT FOR UPDATE语句隐式得到行共享锁定,在Oracle中用户也能够经过LOCK TABLE IN ROW SHARE MODE语句显式得到行共享锁定。行共享锁定并不防止对数据行进行更改的操做,可是能够防止其余会话获取独占性数据表锁定。容许进行多个并发的行共享和行独占性锁定,还容许进行数据表的共享或者采用共享行独占锁定。

行独占锁定

经过一条INSERT、UPDATE或DELETE语句隐式获取,或者经过一条LOCK TABLE IN ROW EXCLUSIVE MODE语句显式获取。这个锁定能够防止其余会话获取一个共享锁定、共享行独占锁定或独占锁定。

表共享锁定

经过LOCK TABLE IN SHARE MODE语句显式得到。这种锁定能够防止其余会话获取行独占锁定(INSERT、UPDATE或DELETE),或者防止其余表共享行独占锁定或表独占锁定,它容许在表中拥有多个行共享和表共享锁定。该锁定可让会话具备对表事务级一致性访问,由于其余会话在用户提交或者回溯该事务并释放对该表的锁定以前不能更改这个被锁定的表。

表共享行独占

经过LOCK TABLE IN SHARE ROW EXCLUSIVE MODE语句显式得到。这种锁定能够防止其余会话获取一个表共享、行独占或者表独占锁定,它容许其余行共享锁定。这种锁定相似于表共享锁定,只是一次只能对一个表放置一个表共享行独占锁定。若是A会话拥有该锁定,则B会话能够执行SELECT FOR UPDATE操做,但若是B会话试图更新选择的行,则须要等待。

表独占

经过LOCK TABLE IN EXCLUSIVE MODE显式得到。这个锁定防止其余会话对该表的任何其余锁定。

乐观锁与悲观锁

所谓悲观锁就是基于数据库机制实现的。好比在在使用select子句的时候加上for update,那么直到改子句的事务结束为止,任何应用都没法修改select出来的记录。

所谓乐观锁是基于应用的版本机制来实现的。通常会在表里面设计一个版本字段version(我通常会把这个字段设为长整形或者timestamp)。通常的update场景是这样:

第一步:

select a, version from tb where id=1;
假设获得数据是:['xxx', 100]

第二步:

update tb set a='yyy', version=100+1 where version=100; 
//注意, version通常不会在业务操做的时候修改

这要求每一次update操做都变动版本字段,不然仍是要进程间的数据 仍是会被相互覆盖。

乐观锁没法锁定其余应用对数据的操做。乐观锁机制避免了长事务中的数据库加锁开销(两个事务,事务A 和事务B操做过程当中,都没有对数据库数据加锁),大大提高了大并发量下的系统总体性能表现。 须要注意的是,乐观锁机制每每基于系统中的数据存储逻辑,所以也具有必定的局限性,因为乐观锁机制通常是在咱们的系统中实现,来自外部系统的用户余额更新操做不受咱们系统的控制,所以可能会形成脏数据被更新到数据库中。在系统设计阶段,咱们应该充分考虑到这些状况出现的可能性,并进行相应调整(如将乐观锁策略在数据库存储过程当中实现,对外只开放基于此存储过程的数据更新途 径,而不是将数据库表直接对外公开)。

4、事务隔离级别

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

ANSI/ISO SQL 92标准定义了4个等级的事务隔离级别,在相同数据环境下,使用相同的输入,执行相同的工做,根据不一样的隔离级别,能够致使不一样的结果。不一样事务隔离级别可以解决的数据并发问题的能力是不一样的,以下表所示。

事务的隔离级别和数据库并发性是对立的,二者此增彼长。通常来讲,使用READ UNCOMMITED隔离级别的数据库拥有最高的并发性和吞吐量,而使用SERIALIZABLE隔离级别的数据库并发性最低。

SQL 92定义READ UNCOMMITED主要是为了提供非阻塞读的能力,Oracle虽然也支持READ UNCOMMITED,但它不支持脏读,由于Oracle使用多版本机制完全解决了在非阻塞读时读到脏数据的问题并保证读的一致性,因此,Oracle的READ COMMITTED隔离级别就已经知足了SQL 92标准的REPEATABLE READ隔离级别。

SQL 92推荐使用REPEATABLE READ以保证数据的读一致性,不过用户能够根据应用的须要选择适合的隔离等级。

5、JDBC对事务的支持

并非全部的数据库都支持事务,即便支持事务的数据库也并不是支持全部的事务隔离级别,用户能够经过Connection#getMetaData()方法获取DatabaseMetaData对象,并经过该对象的supportsTransactions()、supportsTransactionIsolationLevel(int level)方法查看底层数据库的事务支持状况。

Connection默认状况下是自动提交的,也即每条执行的SQL都对应一个事务,为了可以将多条SQL当成一个事务执行,必须先经过Connection#setAutoCommit(false)阻止Connection自动提交,并可经过Connection#setTransactionIsolation()设置事务的隔离级别,Connection中定义了对应SQL 92标准4个事务隔离级别的常量。经过Connection#commit()提交事务,经过Connection#rollback()回滚事务。下面是典型的JDBC事务数据操做的代码:

Connection conn ;
try{
     conn = DriverManager.getConnection();//①获取数据链接
     conn.setAutoCommit(false); //②关闭自动提交的机制
     conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); //③设置事务隔离级别

     Statement stmt = conn.createStatement(); 
    
     int rows = stmt.executeUpdate( "INSERT INTO t_topic VALUES(1,’tom’) " );
     rows = stmt.executeUpdate( "UPDATE t_user set topic_nums = topic_nums +1 "+
"WHERE user_id = 1"); 
     
     conn.commit();//④提交事务
}catch(Exception e){
     …
     conn.rollback();//⑤回滚事务
}finally{
   …
}
在JDBC 2.0中,事务最终只能有两个操做:提交和回滚。可是,有些应用可能须要对事务进行更多的控制,而不是简单地提交或回滚。JDBC 3.0(JDK 1.4及之后的版本)引入了一个全新的保存点特性,Savepoint 接口容许用户将事务分割为多个阶段,用户能够指定回滚到事务的特定保存点,而并不是像JDBC 2.0同样只回滚到开始事务的点,如图所示。

下面的代码使用了保存点的功能,在发生特定问题时,回滚到指定的保存点,而非回滚整个事务,代码以下所示:

…
Statement stmt = conn.createStatement(); 
int rows = stmt.executeUpdate( "INSERT INTO t_topic VALUES(1,’tom’)");

Savepoint svpt = conn.setSavepoint("savePoint1");//①设置一个保存点
rows = stmt.executeUpdate( "UPDATE t_user set topic_nums = topic_nums +1 "+
          "WHERE user_id = 1"); 
…
//②回滚到①处的savePoint1,①以前的SQL操做,在整个事务提交后依然提交,
//但①到②之间的SQL操做被撤销了
conn.rollback(svpt); 
…
conn.commit();//③提交事务

并不是全部数据库都支持保存点功能,用户能够经过DatabaseMetaData#supportsSavepoints()方法查看是否支持。





Spring事务机制详解参见:http://www.open-open.com/lib/view/open1350865116821.html

文中大量内容摘自:http://stamen.iteye.com/blog/1541720

相关文章
相关标签/搜索