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..

分类标签

jekyll3   编码1   windows1   bootstrap1   git3   删除1   命令3   python11   ide1   学习笔记3   实例分析1   mp3-tag1   github1   gravatar1   goagent1   翻墙1   C#4   找茬工具1   微博自动评论1   电脑监控1   备份1   云搜索1   wxPython1   py2exe1   yaml1   Eric1   PyQt1   Django1   设计模式5   翻译4   单例1   工厂1   抽象工厂1   生成器1   原型1   适配器1   桥接1   组合1   装饰1   外观1   享元1   代理1   MVC1   观察者1   状态1   策略1   模板1   访问者1   职责链1   解释器1   迭代器1   中介者1   备忘录1   js1   resize bar1   geohash1   口琴1   rpm安装gitlab1   CentOs1   WordPress1   数据库1   读脏数据1   丢失的修改1   不可重复读1   幻影读1   1   隔离1   思维导图1   事务1   笔记迁移1   note1   issue1  

最新博文

最新评论

Feed订阅


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