事务管理( 一 )——事务管理基本概念

概述 
   事务控制是保证系统数据完整一致性的基础。也是企业应用系统必须解决的一个技术问题。不单是企业审计上需要,更重要是企业及时有效的通过数据监控企业当前状态的需要。所以对于一个企业级的应用数据的完整一致性是必须的。那么如何简单快速,而又可以从全局上充分的对系统的事务进行有效的控制就显得至关重要了。这里简述系统事务控制的基本概念(数据库的数据一致性问题,数据库的锁机制,数据库的隔离级别的实现),JDBC,JTA的事务控制的实现方法,Spring的事务控制这三方面进行叙述,充分的了解事务控制的细节。 

事务的基本概念 
  数据库事务有严格的定义,它必须同时满足四个特性:原子性(Atomic)、一致性(Consistency)、隔离性(Isolation)和持久性(Durabiliy),简称为ACID。 
  原子性:表示组成一个事务的多个数据库操作是一个不可分隔的原子单元,只有所有的操作执行成功,整个事务才提交,事务中任何一个数据库操作失败,已经执行的任何操作都必须撤销,让数据库返回到初始状态。 
  一致性:事务操作成功后,数据库所处的状态和它的业务规则是一致的,即数据不会被破坏。如从A账户转账100元到B账户,不管操作成功与否,A和B的存款总额是不变的。 
  隔离性:在并发数据操作时,不同的事务拥有各自数据空间,它们的操作不会对对方产生干扰。准确的说,并非要求做到完全无干扰,数据库规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性越好,但并发性越弱。 
  持久性:一旦事务提交成功后,事务中所有的数据操作都必须被持久化到数据库中,即使提交事务后,数据库马上崩溃,在数据库重启时,也必须能保证能够通过某种机制恢复数据。 
  在这些事务特性中,数据“一致性”是最终目标,其它的特性都是为达到这个目标的措施、要求或手段。 数据库管理系统一般采用重执行日志保证原子性、一致性和持久性。重执行日志记录了数据库变化的每一个动作,数据库在一个事务中执行一部分操作后发生错误退出,数据库即可以根据重执行日志撤销已经执 行的操作。此外,对于已经提交的事务,即使数据库崩溃,在重启数据库时也能够根据日志对尚未持久化的数据进行相应的重执行操作。和Java程序采用对象锁机制进行线程同步类似,数据库管理系统采用数据库锁机制保证事务的隔离性。当多个事务试图对相同的数据进行操作时,只有持有锁的事务才能操作数据,直到前一个事务完成后,后面的事务才有机会对数据进行操作。 

多个事务同时访问系统的并发问题。 
1)脏读 
A读取了B尚未提交的数据,而致使账户损失了500元。 
时间 转账事务A 取款事务B 
T1 开始事务 
T2 开始事务
T3       查询账户余额为1000元     
T4          取出500元把余额改为500元 
T5 查询账户余额为500元
T6 撤销事务余额恢复为1000元 
T7 汇入100元把余额改为600元
T8 提交事务

2)不可重复读 
不可重复读是指A事务读取了B事务已经提交的更改数据。假设A在取款事务的过程中,B往该账户转账100元,A两次读取账户的余额发生不一致。 
时间 取款事务A 转账事务B 
T1 开始事务 
T2 开始事务                            
T3                               查询账户余额为1000元      
T4 查询账户余额为1000元                            
T5                    取出100元把余额改为900元 
T6 提交事务                   
T7 查询账户余额为900元(和T4读取的不一致)

3)幻影读 
A事务读取B事务提交的新增数据,这时A事务将出现幻象读的问题。幻象读一般发生在计算统计数据的事务中,举一个例子,假设银行系统在同一个事务中,两次 统计存款账户的总金额,在两次统计过程中,刚好新增了一个存款账户,并存入100元,这时,两次统计的总金额将不一致。 
时间 统计金额事务A 转账事务B 
T1 开始事务 
T2 开始事务               
T3 统计总存款数为10000元               
T4 新增一个存款账户,存款为100元 
T5 提交事务      
T6 再次统计总存款数为10100元(幻象读)

幻象读和不可重复读是两个容易混淆的概念,幻象读是指读到了其它已经提交事务的新增数据,而不可重复读是指读到了已经提交事务的更改数据(更改或删除)。为了避免 这两种情况,采取的对策是不同的,防止读取到更改数据,只需要对操作的数据添加行级锁,阻止操作中的数据发生变化,而防止读取到新增数据,则往往需要添加表级锁——将整个表锁定,防止新增数据。 

4)第一类丢失更新    
A事务撤销时,把已经提交的B事务的更新数据覆盖了。     
时间 取款事务A 转账事务B 
T1 开始事务
T2 开始事务 
T3 查询账户余额为1000元     
T4 查询账户余额为1000元 
T5 汇入100元把余额改为1100元 
T6 提交事务 
T7 取出100元把余额改为900元
T8 撤销事务
T9 余额恢复为1000元(丢失更新)

5)  第二类丢失更新 
A事务覆盖B事务已经提交的数据,造成B事务所做操作丢失 
时间 转账事务A 取款事务B 
T1 开始事务 
T2 开始事务                           
T3                 查询账户余额为1000元     
T4 查询账户余额为1000元                           
T5 取出100元把余额改为900元 
T6 提交事务            
T7 汇入100元
T8 提交事务
T9 把余额改为1100元(丢失更新)

1.2.2 数据库锁机制实现对于上述5类问题的控制 
  数据并发会引发很多问题,在一些场合下有些问题是允许的,但在另外一些场合下可能却是致命的。数据库通过锁的机制解决并发访问的问题,虽然不同的数据库在实现细节上存在差别,但原理基本上是一样的。 
  从并发事务锁定的关系上看,可以分为共享锁定和独占锁定。共享锁定会防止独占锁定,但允许其它的共享锁定。而独占锁定既防止其它的独占锁定,也防止其它的共享锁定。 
  锁就是保护指定的资源,不被其他事务操作。为了最小化锁的成本,自动地以与任务相应等级的锁来锁定资源对象。锁定比较小的对象,例如锁定行,可以提高并发性,但是却有较高的开支。如果锁定许多行, 那么需要占有许多锁对象。锁定比较大的对象,例如锁定表,会大大降低并发性,因为锁定整个表就限制了其他事务访问该表的其他部分,但是成本开支比较低,因为只需维护比较少的锁。        锁是定义到SQL语句上的,对数据进行操作的SQL就是:select,Insert,update ,delete。不同的事物隔离级别,在执行SQL的时候会向表上发送不同的锁。例如在SQLSERVER中的READ COMMITED隔离级别下,SELECT语句会对所有的满足条件的数据行加上共享锁,而UPDATE语句则会对被UPDATE的数据加上排他锁。 

以下是一种最为简单的方式通过对锁的应用实现事务间共享数据的隔离。 
1)防止第一类丢失更新问题(实现READ_UNCOMMIT隔离级别) 
事务在读取数据时,对行数据加共享锁。当且仅当数据没有任何的锁定时候,事务才可以对数据加上独占锁。并且在事务更新完后立即释放独占锁。 

2)防止脏读问题(实现READ_COMMIT隔离级别) 
事务在读取数据时,对行数据加共享锁。当且仅当数据没有任何的锁定时候,事务才可以对数据加上独占锁。并且当事务提交时候才释放独占锁。 

3)防止重复读和第二类丢失更新问题 
事务在读取数据的时候直接加独占行级锁,并且当事务结束的时候才释放锁。同一行数据无法支持并发的读操作。

4)防止幻影读问题 
事务在读取数据的时候对表加表级独占锁,并且在事务结束的时候释放   

注意以上的控制方法关键在于锁定类型和锁的释放时间上进行控制,从而实现不同的隔离级别和数据库的并发访问性。这些方法只是个人的推测,而非数据库的实现。 

1.2.3 数据库的隔离级别 
  尽管数据库为用户提供了锁的DML操作方式,但直接使用锁管理是非常麻烦的,因此数据库为用户提供了自动锁机制。只要用户指定会话的事务隔离级别,数据库 就会分析事务中的SQL语句,然后自动为事务操作的数据资源添加上适合的锁。此外数据库还会维护这些锁,当一个资源上的锁数目太多时,自动进行锁升级以提 高系统的运行性能,而这一过程对用户来说完全是透明的。 
  ANSI/ISO SQL 92标准定义了4个等级的事务隔离级别,在相同数据环境下,使用相同的输入,执行相同的工作,根据不同的隔离级别,可以导致不同的结果。不同事务隔离级别能够解决的数据并发问题的能力是不同的。数据库一般的默认隔离离级别是“READ COMMITTED”,它能避免脏读,而且有较好的并发性能。尽管它会导致不可重复读、虚读和第二类更新丢失等问题,在可能出现这类问题的个别场合可以由应用程序釆用悲观锁或乐观锁来控制。
  SQLSERVER可以使用SET TRANSACTION ISOLATION LEVEL来设置事务的隔离级别。指定隔离级别后,sql会话中所有语句的锁定行为都运行于该隔离级别上,并一直保持有效直到会话终止或者将隔离级别设置为另一个级别。 
事务隔离级别对并发问题的解决情况 
隔离级别 脏读 不可 
重复读 幻象读 第一类丢失更新 第二类丢失更新 
READ UNCOMMITED 允许 允许 允许 不允许 允许 
READ COMMITTED 不允许 允许 允许 不允许 允许 
REPEATABLE READ 不允许 不允许 允许 不允许 不允许 
SERIALIZABLE 不允许 不允许 不允许 不允许 不允许 概述 
   事务控制是保证系统数据完整一致性的基础。也是企业应用系统必须解决的一个技术问题。不单是企业审计上需要,更重要是企业及时有效的通过数据监控企业当前状态的需要。所以对于一个企业级的应用数据的完整一致性是必须的。那么如何简单快速,而又可以从全局上充分的对系统的事务进行有效的控制就显得至关重要了。这里简述系统事务控制的基本概念(数据库的数据一致性问题,数据库的锁机制,数据库的隔离级别的实现),JDBC,JTA的事务控制的实现方法,Spring的事务控制这三方面进行叙述,充分的了解事务控制的细节。 

事务的基本概念 
  数据库事务有严格的定义,它必须同时满足四个特性:原子性(Atomic)、一致性(Consistency)、隔离性(Isolation)和持久性(Durabiliy),简称为ACID。 
  原子性:表示组成一个事务的多个数据库操作是一个不可分隔的原子单元,只有所有的操作执行成功,整个事务才提交,事务中任何一个数据库操作失败,已经执行的任何操作都必须撤销,让数据库返回到初始状态。 
  一致性:事务操作成功后,数据库所处的状态和它的业务规则是一致的,即数据不会被破坏。如从A账户转账100元到B账户,不管操作成功与否,A和B的存款总额是不变的。 
  隔离性:在并发数据操作时,不同的事务拥有各自数据空间,它们的操作不会对对方产生干扰。准确的说,并非要求做到完全无干扰,数据库规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性越好,但并发性越弱。 
  持久性:一旦事务提交成功后,事务中所有的数据操作都必须被持久化到数据库中,即使提交事务后,数据库马上崩溃,在数据库重启时,也必须能保证能够通过某种机制恢复数据。 
  在这些事务特性中,数据“一致性”是最终目标,其它的特性都是为达到这个目标的措施、要求或手段。 数据库管理系统一般采用重执行日志保证原子性、一致性和持久性。重执行日志记录了数据库变化的每一个动作,数据库在一个事务中执行一部分操作后发生错误退出,数据库即可以根据重执行日志撤销已经执 行的操作。此外,对于已经提交的事务,即使数据库崩溃,在重启数据库时也能够根据日志对尚未持久化的数据进行相应的重执行操作。和Java程序采用对象锁机制进行线程同步类似,数据库管理系统采用数据库锁机制保证事务的隔离性。当多个事务试图对相同的数据进行操作时,只有持有锁的事务才能操作数据,直到前一个事务完成后,后面的事务才有机会对数据进行操作。 

多个事务同时访问系统的并发问题。 
1)脏读 
A读取了B尚未提交的数据,而致使账户损失了500元。 
时间 转账事务A 取款事务B 
T1 开始事务 
T2 开始事务
T3       查询账户余额为1000元     
T4          取出500元把余额改为500元 
T5 查询账户余额为500元
T6 撤销事务余额恢复为1000元 
T7 汇入100元把余额改为600元
T8 提交事务

2)不可重复读 
不可重复读是指A事务读取了B事务已经提交的更改数据。假设A在取款事务的过程中,B往该账户转账100元,A两次读取账户的余额发生不一致。 
时间 取款事务A 转账事务B 
T1 开始事务 
T2 开始事务                            
T3                               查询账户余额为1000元      
T4 查询账户余额为1000元                            
T5                    取出100元把余额改为900元 
T6 提交事务                   
T7 查询账户余额为900元(和T4读取的不一致)

3)幻影读 
A事务读取B事务提交的新增数据,这时A事务将出现幻象读的问题。幻象读一般发生在计算统计数据的事务中,举一个例子,假设银行系统在同一个事务中,两次 统计存款账户的总金额,在两次统计过程中,刚好新增了一个存款账户,并存入100元,这时,两次统计的总金额将不一致。 
时间 统计金额事务A 转账事务B 
T1 开始事务 
T2 开始事务               
T3 统计总存款数为10000元               
T4 新增一个存款账户,存款为100元 
T5 提交事务      
T6 再次统计总存款数为10100元(幻象读)

幻象读和不可重复读是两个容易混淆的概念,幻象读是指读到了其它已经提交事务的新增数据,而不可重复读是指读到了已经提交事务的更改数据(更改或删除)。为了避免 这两种情况,采取的对策是不同的,防止读取到更改数据,只需要对操作的数据添加行级锁,阻止操作中的数据发生变化,而防止读取到新增数据,则往往需要添加表级锁——将整个表锁定,防止新增数据。 

4)第一类丢失更新    
A事务撤销时,把已经提交的B事务的更新数据覆盖了。     
时间 取款事务A 转账事务B 
T1 开始事务
T2 开始事务 
T3 查询账户余额为1000元     
T4 查询账户余额为1000元 
T5 汇入100元把余额改为1100元 
T6 提交事务 
T7 取出100元把余额改为900元
T8 撤销事务
T9 余额恢复为1000元(丢失更新)

5)  第二类丢失更新 
A事务覆盖B事务已经提交的数据,造成B事务所做操作丢失 
时间 转账事务A 取款事务B 
T1 开始事务 
T2 开始事务                           
T3                 查询账户余额为1000元     
T4 查询账户余额为1000元                           
T5 取出100元把余额改为900元 
T6 提交事务            
T7 汇入100元
T8 提交事务
T9 把余额改为1100元(丢失更新)

1.2.2 数据库锁机制实现对于上述5类问题的控制 
  数据并发会引发很多问题,在一些场合下有些问题是允许的,但在另外一些场合下可能却是致命的。数据库通过锁的机制解决并发访问的问题,虽然不同的数据库在实现细节上存在差别,但原理基本上是一样的。 
  从并发事务锁定的关系上看,可以分为共享锁定和独占锁定。共享锁定会防止独占锁定,但允许其它的共享锁定。而独占锁定既防止其它的独占锁定,也防止其它的共享锁定。 
  锁就是保护指定的资源,不被其他事务操作。为了最小化锁的成本,自动地以与任务相应等级的锁来锁定资源对象。锁定比较小的对象,例如锁定行,可以提高并发性,但是却有较高的开支。如果锁定许多行, 那么需要占有许多锁对象。锁定比较大的对象,例如锁定表,会大大降低并发性,因为锁定整个表就限制了其他事务访问该表的其他部分,但是成本开支比较低,因为只需维护比较少的锁。        锁是定义到SQL语句上的,对数据进行操作的SQL就是:select,Insert,update ,delete。不同的事物隔离级别,在执行SQL的时候会向表上发送不同的锁。例如在SQLSERVER中的READ COMMITED隔离级别下,SELECT语句会对所有的满足条件的数据行加上共享锁,而UPDATE语句则会对被UPDATE的数据加上排他锁。 

以下是一种最为简单的方式通过对锁的应用实现事务间共享数据的隔离。 
1)防止第一类丢失更新问题(实现READ_UNCOMMIT隔离级别) 
事务在读取数据时,对行数据加共享锁。当且仅当数据没有任何的锁定时候,事务才可以对数据加上独占锁。并且在事务更新完后立即释放独占锁。 

2)防止脏读问题(实现READ_COMMIT隔离级别) 
事务在读取数据时,对行数据加共享锁。当且仅当数据没有任何的锁定时候,事务才可以对数据加上独占锁。并且当事务提交时候才释放独占锁。 

3)防止重复读和第二类丢失更新问题 
事务在读取数据的时候直接加独占行级锁,并且当事务结束的时候才释放锁。同一行数据无法支持并发的读操作。

4)防止幻影读问题 
事务在读取数据的时候对表加表级独占锁,并且在事务结束的时候释放   

注意以上的控制方法关键在于锁定类型和锁的释放时间上进行控制,从而实现不同的隔离级别和数据库的并发访问性。这些方法只是个人的推测,而非数据库的实现。 

1.2.3 数据库的隔离级别 
  尽管数据库为用户提供了锁的DML操作方式,但直接使用锁管理是非常麻烦的,因此数据库为用户提供了自动锁机制。只要用户指定会话的事务隔离级别,数据库 就会分析事务中的SQL语句,然后自动为事务操作的数据资源添加上适合的锁。此外数据库还会维护这些锁,当一个资源上的锁数目太多时,自动进行锁升级以提 高系统的运行性能,而这一过程对用户来说完全是透明的。 
  ANSI/ISO SQL 92标准定义了4个等级的事务隔离级别,在相同数据环境下,使用相同的输入,执行相同的工作,根据不同的隔离级别,可以导致不同的结果。不同事务隔离级别能够解决的数据并发问题的能力是不同的。数据库一般的默认隔离离级别是“READ COMMITTED”,它能避免脏读,而且有较好的并发性能。尽管它会导致不可重复读、虚读和第二类更新丢失等问题,在可能出现这类问题的个别场合可以由应用程序釆用悲观锁或乐观锁来控制。
  SQLSERVER可以使用SET TRANSACTION ISOLATION LEVEL来设置事务的隔离级别。指定隔离级别后,sql会话中所有语句的锁定行为都运行于该隔离级别上,并一直保持有效直到会话终止或者将隔离级别设置为另一个级别。 
事务隔离级别对并发问题的解决情况 
隔离级别 脏读 不可 
重复读 幻象读 第一类丢失更新 第二类丢失更新 
READ UNCOMMITED 允许 允许 允许 不允许 允许 
READ COMMITTED 不允许 允许 允许 不允许 允许 
REPEATABLE READ 不允许 不允许 允许 不允许 不允许 
SERIALIZABLE 不允许 不允许 不允许 不允许 不允许 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值