数据库的并发操作时,很可能会出现不一致的问题,包括丢失的修改,读脏数据,不可重复读,幻影读等,这些可以通过最原始的共享锁和排他锁解决,但是使用锁复杂繁琐,便产生了隔离级别,针对具体的业务流程使用不同的隔离级别,从而解决并发不一致的问题。
并发不一致问题
多个对象同时进行数据库操作时,由于先后顺序、读写操作、读写内容的各种组合,可能会出现丢失的修改、读脏数据、不可重复读、幻影读四种不一致的情况。以下描述中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