数据库是一个并发操作的环境,就像多线程一样,这样在高并发的情况下回出现一些问题。
假设我们有一张表Account,表结构和数据如下所示
AccountName | Balance |
jo | 100 |
fo | 200 |
假设我们有两个事事务(T1,T2)
脏读:当一个事务允许读取另外一个事务修改但未提交的数据时,就可能发生脏读(dirty reads)。
(1)T1更新jo的余额:
UPDATE Account SET Balance=500 WHERE AccountName='jo'
(2)T2选出jo的余额:
SELECT Balance FROM Account WHERE AccountName='jo'
(3)T1回滚(rollback)。
此时在(2)中已经选出了500这个值,尽管最后T1的回滚让jo的余额保持100没有改变,这种情况就是脏读。
不可重复读:在一次事务中,当一行数据获取两遍得到不同的结果表示发生了"不可重复读(non-repeatable read)"。
(1)T2选出jo的余额:
SELECT Balance FROM Account WHERE AccountName='jo'
(2)T1中更新jo的余额并且提交(commit)。
UPDATE Account SET Balance=500 WHERE AccountName='jo'
Commit Tran
(3)T2再次选出jo的余额
SELECT Balance FROM Account WHERE AccountName='jo'
此时在(1)中选出的余额是100,在(3)中选出的余额是500,在同一个事务T2中两个Select得到了不一样的数据。
幻读:在事务执行过程中,当两个完全相同的查询语句执行得到不同的结果集。这种现象称为“幻读(phantom read)”
(1)T2选择金额大于100的账户余额
SELECT Balance FROM Account WHERE Balance>100
(2)T1插入新的余额大于100的账户
INSERT INTO Account (AccountName,Balance) VALUES ('ho',300)
(3)T2中再次选择金额大于100的账户
SELECT Balance FROM Account WHERE Balance>100
此时(1)读出了一条数据,(2)读出了两条数据,同一个查询的在同一个事务中的两次读取得到了不同的数量的结果集,这种现象就是幻读。
为了处理以上并发问题,有了并发控制的概念。简单来说就是一个事务在执行的时候,通过对正在访问的资源加锁来阻塞其他事务对资源的操作。
不同的加锁方式对应不同的隔离级别,用来针对不同的并发问题。
READ UNCOMMITTED
事务执行期间,对任何操作不加锁。意味着可以读取其他事务未提交的更新。这个隔离级别下以上的并发问题都有可能发生。
READ COMMITTED
事务执行期间,对写操作加锁。意味着当前将要写入的数据加了写锁,阻塞其他事务读取或者更改加锁数据,直到事务结束。
这个隔离级别可以防止其他的事务脏读,因为其他的事务对加锁数据的读写操作都被当前事务阻塞了。不过由于当前事务没有加读锁,所以当前事务会发生不可重复读。
REPEATABLE READ
事务执行期间,对读,写操作加锁。意味着当前事务写入和读取的数据其他的事务都无法访问,知道当前事务结束(回滚,或者提交),当前事务不会发生不可重复读,不过由于当前读取的数据范围没有加锁,其他的事务可以插入满足当前事务读取范围过滤条件的数据;或者更新当前事务读取范围外的数据,已使其满足当前事务的读取范围过滤条件。这样就会造成幻读。
SERIALIZABLE
可序列化或者叫可串行化,按照字面意思,是事务只能一个接着一个的执行。
实际的实现方式是通过加读锁,写锁和范围锁来保证事务执行的串行化。意味着在当前事务的执行过程中,会阻塞其他的涉及当前事务相关数据及数据范围的事务。
本文只是一个简单的介绍,要完全的理解隔离级别,并且推断出事务在不同隔离级别下会有什么样的不同就要深入的了解SQL Server在不同隔离级别下对不同对象的锁定,以及不同的锁之间是如何交互的,这些方面的问题推荐下面这篇博客:
http://www.cnblogs.com/RicCC/archive/2010/03/05/transaction-lock-isolation-level.html