事务的隔离等级

          

       事务准备接受不一致数据的级别称为隔离级别。隔离级别是一个事务必须与其它事务进行隔离的程度。较低的隔离级别可以增加并发,但代价是降低数据的正确性。相反,较高的隔离级别可以确保数据的正确性,但可能对并发产生负面影响。应用程序要求的隔离级别确定了所使用的锁定行为:


数据库在被广大客户所共享访问的操作过程中很可能出现以下几种不确定情况 :

1. 更新丢失(Lost update):两个事务都同时更新一行数据但是第二个事务却中途失败退出导致对数据两个修改都失效了这是系统没有执行任何锁操作因此并发事务并没有被隔离开来

2. 脏读取(Dirty Reads):一个事务开始读取了某行数据但是另外一个事务已经更新了此数据但没有能够及时提交。这是相当危险很可能所有操作都被回滚

3. 不可重复读取(Non-repeatable Reads):一个事务对同一行数据重复读取两次但是却得到了不同结果。例如在两次读取中途有另外一个事务对该行数据进行了修改并提交

4. 两次更新问题(Second lost updates problem):无法重复读取特例,有两个并发事务同时读取同一行数据然后其中一个对它进行修改提交而另一个也进行了修改提交这就会造成第一次写操作失效

5. 虚读(Phantom Reads):也称为幻像(幻影)。事务在操作过程中进行两次查询,第二次查询结果包含了第一次查询中未出现的数据(这里并不要求两次查询SQL语句相同)这是因为在两次查询过程中有另外一个事务插入数据造成的

为了避免上面出现几种情况在标准SQL规范中定义了4个事务隔离级别,不同隔离级别对事务处理不同。

1.未授权读取(Read Uncommitted):也称未提交读。允许脏读取但不允许更新丢失,如果一个事务已经开始写数据则另外一个数据则不允许同时进行写操作但允许其他事务读此行数据。该隔离级别可以通过排他写锁实现 。事务隔离的最低级别,仅可保证不读取物理损坏的数据。与READ COMMITTED 隔离级相反,它允许读取已经被其它用户修改但尚未提交确定的数据。

2. 授权读取(Read Committed):也称提交读。允许不可重复读取但不允许脏读取。这可以通过瞬间共享读锁排他写锁实现,读取数据的事务允许其他事务继续访问该行数据,但是未提交写事务将会禁止其他事务访问该行 。SQL Server 默认的级别。在此隔离级下,SELECT 命令不会返回尚未提交(Committed) 的数据,也不能返回脏数据。

3. 可重复读取(Repeatable Read):禁止不可重复读取和脏读取。但是有时可能出现幻影数据,这可以通过共享读锁排他写锁实现,读取数据事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务 。在此隔离级下,用SELECT 命令读取的数据在整个命令执行过程中不会被更改。此选项会影响系统的效能,非必要情况最好不用此隔离级。

4. 序列化(Serializable):也称可串行读。提供严格的事务隔离,它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。如果仅仅通过行级锁是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作事务访问到。事务隔离的最高级别,事务之间完全隔离。如果事务在可串行读隔离级别上运行,则可以保证任何并发重叠事务均是串行的。

隔离级需要使用SET 命令来设定其语法如下:
SET TRANSACTION ISOLATION LEVEL
{READ COMMITTED
| READ UNCOMMITTED
| REPEATABLE READ
| SERIALIZABLE }


下面是四种隔离级别允许不同类型的行为。


隔离级别 脏读 不可重复读取 幻像(幻影)
未提交读                               
提交读                                  
可重复读                               
可串行读                               

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

锁(Lock) 是在多用户环境下对资源访问的一种限制。机制当对一个数据源加锁后,此数据源就有了一定的访问限制。我们就称对此数据源进行了锁定。在SQL Server中,可以对以下的对象进行锁定:

数据行(Row):数据页中的单行数据;

索引行(Key):索引页中的单行数据,即索引的键值;

页(Page):页是SQL Server 存取数据的基本单位,其大小为8KB

盘区(Extent):一个盘区由8 个连续的页组成;

表(Table);

数据库(Database)。


SQL Server 中,锁有两种分类方法。
1) 从数据库系统的角度来看

锁分为以下三种类型:


1.
独占锁(Exclusive Lock

独占锁锁定的资源只允许进行锁定操作的程序使用,其它任何对它的操作均不会被接受。执行数据更新命令,即INSERT UPDATE DELETE 命令时,SQL Server 会自动使用独占锁。但当对象上有其它锁存在时,无法对其加独占锁。独占锁一直到事务结束才能被释放。

2.
共享锁(Shared Lock

共享锁锁定的资源可以被其它用户读取,但其它用户不能修改它。在SELECT 命令执行时,SQL Server 通常会对对象进行共享锁锁定。通常加共享锁的数据页被读取完毕后,共享锁就会立即被释放。

3.
更新锁(Update Lock

更新锁是为了防止死锁而设立的。当SQL Server 准备更新数据时,它首先对数据对象作更新锁锁定,这样数据将不能被修改,但可以读取。等到SQL Server 确定要进行更新数据操作时,它会自动将更新锁换为独占锁。但当对象上有其它锁存在时,无法对其作更新锁锁定。


2
)从程序员的角度看

锁分为以下两种类型:


1.
乐观锁(Optimistic Lock

乐观锁假定在处理数据时,不需要在应用程序的代码中做任何事情就可以直接在记录上加锁、即完全依靠数据库来管理锁的工作。一般情况下,当执行事务处理时SQL Server会自动对事务处理范围内更新到的表做锁定。

2.
悲观锁(Pessimistic Lock

悲观锁对数据库系统的自动管理不感冒,需要程序员直接管理数据或对象上的加锁处理,并负责获取、共享和放弃正在使用的数据上的任何锁。

锁定优化程序提示及其描述
优化程序提示 优化程序提示描述

holdlock
保持锁定直到事务结束

nolock
检索数据时不使用锁

paglock
使用页面锁

tablock
使用表锁

tablockx
使用独占表锁

updlock
使用更新锁  

如: SELECT *    FROM authors (paglock holdlock index=aunmind)  

死锁及其防止:

死锁(Deadlocking) 是在多用户或多进程状况下,为使用同一资源而产生的无法解决的争用状态,通俗地讲,就是两个用户各占用一个资源,两人都想使用对方的资源,但同时又不愿放弃自己的资源,就一直等待对方放弃资源,如果不进行外部干涉,就将一直耗下去。

死锁会造成资源的大量浪费,甚至会使系统崩溃。在SQL Server 中解决死锁的原则是牺牲一个比两个都死强,即挑出一个进程作为牺牲者,将其事务回滚,并向执行此进程的程序发送编号为1205 的错误信息。而防止死锁的途径就是不能让满足死锁条件的情况发生,为此,用户需要遵循以下原则:


尽量避免并发地执行涉及到修改数据的语句;

要求每个事务一次就将所有要使用的数据全部加锁,否则就不予执行;

预先规定一个封锁顺序所有的事务,都必须按这个顺序对数据执行封锁,例如,不同的过程在事务内部对对象的更新执行顺序应尽量保持一致;

每个事务的执行时间不可太长,对程序段长的事务可考虑将其分割为几个事务。

 

 

 

事务(Transaction)是并发控制的基本单位。所谓的事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。 例如,银行转账工作:从一个账号扣款并使另一个账号增款,这两个操作要么都执行,要么都不执行。所以,应该把它们看成一个事务。事务是数据库维护数据一致 性的单位,在每个事务结束时,都能保持数据一致性。

针对上面的描述可以看出,事务的提出主要是为了解决并发情况下保持数据一致性的问题。


事务具有以下4个基本特征。


l Atomic
(原子性):事务中包含的操作被看做一个逻辑单元,这个逻辑单元中的操作要么全部成功,要么全部失败。


l Consistency
(一致性):只有合法的数据可以被写入数据库,否则事务应该将其回滚到最初状态。


l Isolation
(隔离性):事务允许多个用户对同一个数据进行并发访问,而不破坏数据的正确性和完整性。同时,并行事务的修改必须与其他并行事务的修改相互独立。


l Durability
(持久性):事务结束后,事务处理的结果必须能够得到固化。


数据库肯定是要被广大客户所共享访问的,那么在数据库操作过程中很可能出现以下几种不确定情况。


l
更新丢失(Lost update):两个事务都同时更新一行数据,但是第二个事务却中途失败退出,导致对数据的两个修改都失效了。这是因为系统没有执行任何的锁操作,因此并发事务并没有被隔离开来。


l
脏读取(Dirty Reads):一个事务开始读取了某行数据,但是另外一个事务已经更新了此数据但没有能够及时提交。这是相当危险的,因为很可能所有的操作都被回滚。


l
不可重复读取(Non-repeatable Reads):一个事务对同一行数据重复读取两次,但是却得到了不同的结果。例如,在两次读取的中途,有另外一个事务对该行数据进行了修改,并提交。


l
两次更新问题(Second lost updates problem):无法重复读取的特例。有两个并发事务同时读取同一行数据,然后其中一个对它进行修改提交,而另一个也进行了修改提交。这就会造成第一次写操作失效。


l
虚读(Phantom Reads):事务在操作过程中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据(这里并不要求两次查询的SQL语句相同)。这是因为在两次查询过程中有另外一个事务插入数据造成的。


l
数据库的隔离级别


为了避免上面出现的几种情况,在标准SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同。


ü
未授权读取(Read Uncommitted):允许脏读取,但不允许更新丢失。如果一个事务已经开始写数据,则另外一个数据则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过排他写锁实现。


ü
授权读取(Read Committed):允许不可重复读取,但不允许脏读取。这可以通过瞬间共享读锁排他写锁实现。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。


ü
可重复读取(Repeatable Read):禁止不可重复读取和脏读取,但是有时可能出现幻影数据。这可以通过共享读锁排他写锁实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。


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


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


通过前面的介绍已经知道,通过选用不同的隔离等级就可以在不同程度上避免前面所提及的在事务处理中所面临的各种问题。所以,数据库隔离级别的选取就显得尤为重要,在选取数据库的隔离级别时,应该注意以下几个处理的原则:


先,必须排除未授权读取,因为在多个事务之间使用它将会是非常危险的。事务的回滚操作或失败将会影响到其他并发事务。第一个事务的回滚将会完全将其他 事务的操作清除,甚至使数据库处在一个不一致的状态。很可能一个已回滚为结束的事务对数据的修改最后却修改提交了,因为未授权读取允许其他事务读取数 据,最后整个错误状态在其他事务之间传播开来。


其次,绝大部分应用都无须使用序列化隔离(一般来说,读取幻影数据并不是一个问题),此隔离级别也难以测量。目前使用序列化隔离的应用中,一般都使用悲观锁,这样强行使所有事务都序列化执行。


下的也就是在授权读取可重复读取之间选择了。我们先考虑可重复读取。如果所有的数据访问都是在统一的原子数据库事务中,此隔离级别将消除一个事 务在另外一个并发事务过程中覆盖数据的可能性(第二个事务更新丢失问题)。这是一个非常重要的问题,但是使用可重复读取并不是解决问题的唯一途径。


设使用了版本数据Hibernate会自动使用版本数据。Hibernate的一级Session缓存和版本数据已经为你提供了可重复读取隔离绝大部分的特性。特别是,版本数据可以防止二次更新丢失的问题,一级Session缓存可以保证持久载入数据的状态与其他事务对数据的修改隔离开来,因此 如果使用对所有的数据库事务采用授权读取隔离和版本数据是行得通的。


可重复读取为数据库查询提供了更好的效率(仅对那些长时间的数据库事务),但是由于幻影读取依然存在,因此没必要使用它(对于Web应用来说,一般也很少在一个数据库事务中对同一个表查询两次)。


可以同时考虑选择使用Hibernate的二级缓存,它可以如同底层的数据库事务一样提供相同的事务隔离,但是它可能弱化隔离。假如在二级缓存大量使用缓 存并发策略,它并不提供重复读取语义(例如,后面章节中将要讨论的读写,特别是非严格读写),很容易可以选择默认的隔离级别:因为无论如何都无法实现 重复读取,因此就更没有必要拖慢数据库了。另一方面,可能对关键类不采用二级缓存,或者采用一个完全的事务缓存,提供可重复读取隔离。那么在业务中 需要使用到可重复读取吗?如果你喜欢,当然可以那样做,但更多的时候并没有必要花费这个代价。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值