Yhzhtk's Blog

(热爱技术,高效Code)     归档  标签  源码  关于 


数据库并发不一致分析

2014-06-16    数据库  读脏数据  丢失的修改  不可重复读  幻影读    隔离 


数据库的并发操作时,很可能会出现不一致的问题,包括丢失的修改,读脏数据,不可重复读,幻影读等,这些可以通过最原始的共享锁和排他锁解决,但是使用锁复杂繁琐,便产生了隔离级别,针对具体的业务流程使用不同的隔离级别,从而解决并发不一致的问题。

并发不一致问题

多个对象同时进行数据库操作时,由于先后顺序、读写操作、读写内容的各种组合,可能会出现丢失的修改、读脏数据、不可重复读、幻影读四种不一致的情况。以下描述中A、B表示两个线程中的操作对象:

  • 丢失的修改:A、B读完后,都修改后写入。那么最终的结果是后一个修改的,而前一个修改被覆盖丢失了。
  • 读脏数据:A读取到了B未提交的数据,当B回滚撤销时,A读到的数据就是错误的脏数据了。
  • 不可重复读:A读数据后,B对数据进行了修改,A再读取时,就发现数据不一致了。
  • 幻影读:A读取数据后,B又新增了一条记录,A再读时,发现多了一条,好像出现了幻觉一样。

锁机制

最原始的解决并发不一致的办法就是“加锁”,“锁”有两种:

  • 共享锁(Exclusive locks 简称X锁):所有对象都可以读取,但是都不能修改,只有在没有任何一个拥有该锁时,才能修改,可同时有多个对象加该锁。
  • 排他锁(Share locks 简称S锁):加排他锁后,仅有拥有该锁的对象可以读取后修改,其他对象都不可以读取和修改,同一时间只能有一个对象加该锁。

封锁协议

在运用X锁和S锁这两种基本封锁,对数据对象加锁时,还需要约定一些规则,例如应何时申请X锁或S锁、持锁时间、何时释放等。我们称这些规则为封锁协议(Locking Protocol)。

  • 一级封锁协议 :事务T在修改数据R之前必须先对其加X锁,直到事务结束后释放。事务结束包括正常结束(COMMIT)和非正常结束(ROLLBACK)。1级封锁协议可防止丢失修改,并保证事务T是可恢复的。在1级封锁协议中,如果仅仅是读数据不对其进行修改,是不需要加锁的,所以它不能保证可重复读和不读”脏”数据。
  • 二级封锁协议是:一级封锁协议再加上事务T在读取数据R之前必须先对其加S锁,读完后释放。2级封锁协议除防止了丢失修改,还可进一步防止读”脏”数据。
  • 三级封锁协议是:一级封锁协议再加上事务T在读取数据R之前必须先对其加S锁,直到事务结束后才释放。3级封锁协议除防止了丢失修改和不读’脏’数据外,还进一步防止了不可重复读。

隔离级别

在开发中控制如何加锁以及加锁、解锁的时机显然是很困难的事情,需要考虑繁琐的过程,花费过多时间。绝大多数数据库以及开发工具都提供了事务隔离级别,让用户以一种更轻松的方式处理并发一致性问题 。隔离级别主要有以下四种:

  • ReadUnCommitted:无锁
  • ReadCommitted:使用二级封锁协议。在读取加共享锁时,不允许其他事务操作该数据,只能读取,修改时加排它,其他事务无权修改,所以防止读脏数据。但是在修改之前其他事务也可以读取,在修改完后,其他事务就会出现不可重复读的情况。
  • RepeatableRead:使用三级封锁协议。读取时候不允许其他事务修改该数据,不管数据在事务过程中读取多少次,数据都是一致的,避免了不可重复读问题。与ReadCommitted的区别是释放读锁的时机不一样。
  • Serializable:采用范围锁RangeS 机制,参见 范围锁

隔离级别和各个并发问题的出现情况汇总表:

隔离级别 丢失的修改(Lost Update) 脏读(Dirty Read) 不可重复读(NonRepeatable Read) 幻读(Phantom Read)
读未提交(Read uncommitted) 可能 可能 可能 可能
读已提交(Read committed) 不可能 不可能 可能 可能
可重复读(Repeatable read) 不可能 不可能 不可能 可能
可串行化(Serializable ) 不可能 不可能 不可能 不可能

开发注意

在使用不同级别的隔离模式时,我们也应当注意以下一些问题 :

  • 一般情况下ReadCommitted隔离级别就足够了。过高的隔离级别将会锁定过多的资源,影响数据的共享效率。
  • 你所选择的隔离级别依赖于你的系统和商务逻辑。
  • 尽量避免直接使用锁,除非在万不得已的情况下。
  • 我们可以通过控制WHERE短语中的字段实现不同的更新策略,防止出现丢失的修改问题 。但不必要的更新策略可能造成SQL命令执行效率低下。所以要慎用时间戳和过多的保护字段作为更新依据。

参考资料:
  • http://xiajs.iteye.com/blog/871441
  • http://www.cnblogs.com/kinghuhua/archive/2011/08/17/2142902.html
  • http://software.it168.com/manual/sqlserver/ac_8_con_7a_0n6v.htm




Load Disqus comments, wait a moment..

©2013 首页   关于     View me on GitHub Powered by Jekyll & Bootstrap 知识共享许可协议