本文主要是为了实践一下数据库的各个事务隔离级别下对应的相关问题,所采用的数据库是SQL Server 2012.
事务隔离级别和对应可能发生的错误如下表所示。
准备工作
第一步: 创建数据库表
表的字段如下
第二步
插入测试数据
开始工作
打开SQL Server的Management Studio, 用查询分析器运行下面的命令。
1.脏读 Dirty Read
1)刚开始Ada的账户里有100块钱。
2)笨笨打开了一个事务,然后把Ada的账户数目改成150块。此时笨笨并未提交事务,然后就跑出去喝咖啡了。
3)在笨笨跑出去喝咖啡的时候,聪聪开启了另一个事务,并把事务隔离级别设置成read uncommitted,然后查询Ada账户里面的数目。(结果返回150)
4)笨笨喝完咖啡后,把对Ada账户数目的改动的事务回滚了。
这个时候,Ada账户上的数目应该是100,但是聪聪不知道,还以为是150,这就形成了脏读。如果聪聪把事务隔离级别提高到read committed就可以解决脏读。
2. 不可重复读Non-Repeatable Read
1) 刚开始Cindy的账户上有300块钱。
2) 笨笨进来打开一个事务,并且把事务隔离级别设置成read committed,然后笨笨查了一下Cindy的账户数目。(结果返回300)
3) 然后笨笨并没有马上完成剩下的操作,也没有提交事务,就出去喝咖啡了。
4) 在笨笨跑出去喝咖啡的时候,聪聪开启了另一个事务,并把事务的隔离级别设置成read committed。然后聪聪把Cindy的账户数目更新成了50,并且提交了事务。
5) 笨笨喝完咖啡以后,接着干活。如果笨笨此时再查询一下Cindy的账户,结果会返回50块。
6) 笨笨认为此时把Cindy的账户上还是300块钱,并扣掉100块钱,然后笨笨提交了事务。
7) 最终执行完成以后,Cindy的账户变成200块。
而实际情况是,经过中间聪聪改过账户后,Cindy账户余额已经不足100。如果笨笨把数据库隔离级别设置成Repeatable Read就可以解决不可重复读。
3.幻读Phantom Read
1) 刚开始,Ada账户上有100块,Betty账户上有200块,Cindy账户上有300块。
2) 笨笨进来打开一个事务,并且把事务隔离级别设置成Repeatable Read,然后笨笨查了一下所有用户的账户总额。(结果返回600)
3) 然后笨笨并没有马上完成剩下的操作,也没有提交事务,就出去喝咖啡了。
4) 在笨笨跑出去喝咖啡的时候,聪聪开启了另一个事务,并且在Money表里添加加了一条记录。
5) 笨笨喝完咖啡以后,接着干活。然后笨笨又查询了一下所有用户的账户总额。(结果返回1600)
然后笨笨就觉得很奇怪,我喝咖啡前读到的600莫非是幻觉。如果笨笨把数据库隔离级别设置成Serialization就可以解决幻读。
更多
为了平衡正确性和效率,在标准SQL规范中,数据库事务定义了四种隔离级别,分别是Read Uncommitted, Read Committed, Repeatable Read, Serializable.
- Read Uncommitted – 允许当前事务读取其他事务尚未提交的数据
- Read Committed – 当前事务只有等到其它事务提交之后才能读到这个改变
- Repeatable Read – 同一个事务里面先后执行同一个查询语句的时候得到一样的结果
- Serializable - 这个事务执行的时候不允许别的事务并发执行
这四个级别可以逐步解决Dirty Read, Non-Repeatable Read, Phantom Read等问题
- Dirty Read - 当前事务读取了其它事务尚未提交的更改数据,并在这个数据基础上操作。如果其它事务回滚,那么当前事务读到的数据根本不是合法的,称为脏读。
- Non-Repeatable Read - 当前事务读取了其它事务已经提交的更改(或删除)数据。比如当前事务第一次读取数据,然后其它事务更改该数据并提交,当前事务再次读取数据,那么两次读取的数据不一样。
- Phantom Read - 当前事务读取了其它事务已经提交的新增数据。注意和不可重复读的区别,这里是新增,不可重复读是更改(或删除)。
SQL Server默认的数据库隔离级别是Read Committed.
锁粒度和层次结构
https://msdn.microsoft.com/zh-cn/library/ms189849(v=sql.105).aspx
锁兼容性(数据库引擎)