一、 数据库事务处理中出现的数据不一致的情况
1. 丢失更新
事务a和事务b都要更新数据库一个字段
事务A 事务B
读取X=100 读取X=100
写入x=X+100 写入x=X+200
事务结束x=200 事务结束x=300
最后x==300
这种情况下,a事务的更新就被覆盖掉了.说明数据库写操作的时候可能会出现的问题
2. 脏读
事务a读取到事务b还未来得及提交的记录
如:
事务A 事务B
读取X=100 写入x=X+100
读取X=200 事务回滚x=100
读取X=100 事务结束x=100
3. 不可重复读
事务a在没有更新数据库数据的情况下,同一个查询操作执行两次或多次应该是一致的,如果不一致,就说明为不可重复读
事务A 事务B
读取X=100 读取X=100
读取X=100 写入x=X+100
读取X=200 事务结束x=200
事务结束x=200
这种情况事务A多次读取x的结果出现了不一致,即为不可重复读.
4. 幻读
事务a读的时候读出了15条记录,事务b在a读的过程中删除(增加)了1条记录,事务a再读的时候就变成了16条或14条,这种情况下叫幻读.
这种情况下说明数据库读操作的时候可能会出现的问题
二、 务隔离级别通过锁的实现机制
1. x锁排他锁事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的封锁.获准排他锁的事务既能读数据,又能修改数据
2. s锁共享锁事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁.获准共享锁的事务只能读数据,不能修改数据
3. 在运用X锁和S锁对数据对象加锁时,还需要约定一些规则,例如何时申请X锁或S锁、持锁时间、何时释放等.称这些规则为封锁协议(Locking Protocol).对封锁方式规定不同的规则,就形成了各种不同的封锁协议.
三、 封锁协议
1. 一级封锁协议 (对应read uncommited)
一级封锁协议是:事务T在修改数据R之前必须先对其加X锁,直到事务结束才释放.事务结束包括正常结束(COMMIT)和非正常结束(ROLLBACK).
一级封锁协议可以防止丢失修改,并保证事务T是可恢复的.使用一级封锁协议可以解决丢失修改问题.
在一级封锁协议中,如果仅仅是读数据不对其进行修改,是不需要加锁的,它不能保证可重复读和不读“脏”数据.
2. 二级封锁协议(对应read commited)
二级封锁协议是:一级封锁协议加上事务T在读取数据R之前必须先对其加S锁,读完后方可释放S锁
二级封锁协议除防止了丢失修改,还可以进一步防止读“脏”数据.但在二级封锁协议中,由于读完数据后即可释放S锁,所以它不能保证可重复读.
3. 三级封锁协议(对应reapetable read )
三级封锁协议是:一级封锁协议加上事务T在读取数据R之前必须先对其加S锁,直到事务结束才释放.
三级封锁协议除防止了丢失修改和不读“脏”数据外,还进一步防止了不可重复读.
四、 标准SQL规范中,定义了4个事务隔离级别
1. 未授权读取(Read Uncommitted):允许脏读取,但不允许更新丢失.如果一个事务已经开始写数据,则另外一个数据则不允许同时进行写操作,但允许其他事务读此行数据.该隔离级别可以通过“排他写锁”实现.
2. 授权读取(Read Committed):允许不可重复读取,但不允许脏读取.这可以通过“瞬间共享读锁”和“排他写锁”实现.读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行.
3. 可重复读取(Repeatable Read):禁止不可重复读取和脏读取,但是有时可能出现幻影数据.这可以通过“共享读锁”和“排他写锁”实现.读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务.
4. 序列化(Serializable):提供严格的事务隔离.它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行.如果仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到.
总结:
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大.对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed,它能够避免脏读取,而且具有较好的并发性能.尽管它会导致不可重复读、虚读和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制.
五、 在选取数据库的隔离级别时,应该注意原则
1. 必须排除“未授权读取”,因为在多个事务之间使用它将会是非常危险的.事务的回滚操作或失败将会影响到其他并发事务.第一个事务的回滚将会完全将其他事务的操作清除,甚至使数据库处在一个不一致的状态.很可能一个已回滚为结束的事务对数据的修改最后却修改提交了,因为“未授权读取”允许其他事务读取数据,最后整个错误状态在其他事务之间传播开来.
2. 绝大部分应用都无须使用“序列化”隔离(一般来说,读取幻影数据并不是一个问题),此隔离级别也难以测量.目前使用序列化隔离的应用中,一般都使用悲观锁,这样强行使所有事务都序列化执行.
3. 剩下的也就是在“授权读取”和“可重复读取”之间选择
“可重复读取”为数据库查询提供了更好的效率(仅对那些长时间的数据库事务),但是由于幻影读取依然存在,因此没必要使用它(对于Web应用来说,一般也很少在一个数据库事务中对同一个表查询两次).所以如果需要可以考虑授权读取.