数据库的隔离级别:数据库事务的隔离级别



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

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

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

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

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

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

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

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

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

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

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

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

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

l 数据库隔离级别

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

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

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

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

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

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

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

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

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

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

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

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

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



SQL99有关事务

要想让多个用户共享同个数据库首先要解决问题是用户的间相互隔离即每个用户在访问数据库时都感觉自己是在独占数据库
这有点象我们在操作系统中说“分时”在SQL99中为了实现事务完全隔离定义了 3种必须避免现象:
1、读“脏”数据(dirty read):即事务在运行中读到了其它事务未提交数据
2、不可重复读(unrepeatable read):即事务在运行中再次读取同数据时发现其它事务更新
3、“幻象”读(phantom read):即事务在运行中再次执行同查询时发现其它事务更新

只有避免上面 3种现象才能实现真正意义上隔离或者称是可串行但是此时事务的间冲突而等待机会非常高系统性能
可能难以达到预期目标

为了实现隔离性和系统性能的间平衡SQL99定义了 4种隔离级别允许应用根据实际需要选择合适隔离级别这 4种隔离级别含义如下:读“脏”数据 不可重复读 “幻象”读
未提交读 N N N
提交读 Y N N
可重复读 Y Y N
可串行 Y Y Y
其中“N”表示在这种隔离级别下对应现象不可避免而“Y”则
表示可以避免
当然不管怎样必须保证事务是可恢复所以SQL99还规定在未提交读这种隔离级别下事务必须是只读

在DB2中隔离级别是面向应用用户只有在应用被预编译(PREP)或联编(BIND)时才能设置隔离级别;而ORACLE隔离级别则是面向用户用户可以在每次事务开始时重新设置隔离级别由于后者更为灵活SQL99最终采用了这种方式

相比的下DB2并发控制显得“火候不足”仅仅是最基本 2阶段锁协议并发度的低可以预料;按照我们做数据库想法最起码也得做个多版本什么这样至少可以实现只读事务不等待(在实际数据库系统中只读事务出现概率要远远高于更新事务)真不明白IBM是如何想

而ORACLE并发控制则感觉是“做过了头”通过实现读致性不仅只读事务不必等待连只读操作都不必等待当然由此而付出代价是在可串行这种隔离级别(默认隔离级别)下更新操作冲突而使得整个事务被滚回几率大大增加搞不清楚为什么到现在还没人提意见大概是等到他们发现这个问题时已经无路可走了

不过话又说回来想我们这种无名小辈又有什么资格对这些大公司说 3道 4呢?人家纵然有千般不是毕竟还是个实实在在可用东西而中国人搞研究最大缺点就是:老是觉得些事情没有理论研究价值而不愿去做些具体工作
Tags:  什么是数据库事务 数据库事务处理 数据库事务 数据库的隔离级别

延伸阅读

最新评论

发表评论