一、引言
1、DBMS除了采用严格的两阶段封锁协议来保证并发事务的可串行化,实现事务的隔离性,也可允许用户选择一个可以保证应用程序正确执行并且能够使并发度最大的隔离性等级
2、通常用隔离级别来描述隔离性等级,以下将主要介绍ANSI 92标准定义的4个隔离级别
二、隔离级别中的锁协议
1、事务封锁的数据对象保持锁的时间不同,从而产生不同的封锁协议
(1)有些隔离级别要求把锁保留到事务提交的时候,这种锁称为“长期锁”
(2)而有的隔离级别只把锁保留到语句执行完毕就释放,这种锁称为“短期锁”
(3)在所有的隔离级别上写锁都是长期锁
(4)读锁在每个隔离级别上的处理方式不同
比如SQL Server多粒度封锁模式中的TABLOCK锁就是短期锁,HOLDLOCK保持锁是长期锁
实际的DBMS将不同的读锁和写锁进行组合运用,同时结合多粒度封锁,就形成了不同的封锁协议
2、ANSI用4个隔离级别来定义这些封锁协议并用每个级别满足的数据一致性来命名
三、隔离级别类型
1、对于READ UNCOMMITTED读未提交隔离级别
(1)运行在该隔离级别下的事务,没有获得读锁也可执行读操作
(2)即事务可以读取其他事务已经在其上加了写锁的数据
(3)因此,该事务可能会读取没有提交事务所写的脏数据
脏读有时是严重的,有时是无关紧要的,当某事务只想尽快地大概了解一下数据库的当前状态并不太关心数据的准确性,则该事务就可运行在该隔离级别下而不用为了不脏读而进行耗时的等待
2、对于READ COMMITTED读提交隔离级别
(1)运行在该隔离级别下的事务,读数据之前要获得数据对象上的读锁,若该数据对象上已经加写锁,则要等到该数据对象上的写锁释放
(2)因此,事务只会读取其他事务提交后的数据,不会读取脏数据
(3)但该事务的读锁是短期锁,读操作完成后就释放了读锁,若有另一个事务随后对该事务所读的数据进行了更新并提交,则该事务对同一数据对象进行的再次读取的结果与前一次是不一样的,会出现不可重复读问题
3、对于REPEATABLE READ可重复读隔离级别
(1)运行在该隔离级别下的事务,要获取SELECT语句读取的查询结果中每个元组上的长期读锁,即保持读锁直到事务提交
(2)因此,该事务对查询结果中元组的再次查询不存在不可重复读问题
(3)但在该事务执行时,可能有其他事务向数据库插入满足该SELECT语句查询条件的新元组,从而导致该事务中该SELECT语句的再次执行有可能检索到前次查询没有检索到的新元组,出现了幻影现象,新出现的元组称为幻影元组
4、对于SERIALIZABLE可串行化隔离级别,该隔离级别对应的就是严格的两阶段封锁协议
(1)运行在该隔离级别下的事务,在进行所有数据对象的读操作之前都要求获得长期读锁
(2)对关系表做查询,锁会加在关系表上,不会有幻影现象
(3)事务的执行是可串行化的
四、隔离级别的设置
1、SQL语言提供了在事务执行前对事务的隔离级别进行设置的功能,使得同一个应用程序的不同事、务,也可以在不同的隔离级别下执行
2、这里给出一种设置隔离级别的语句格式:
SET TRANSACTION ISOLATION LEVEL <隔离级别>
3、语句中的隔离级别可以是ANSI标准中4个隔离级别类型中的任何一个,遵循该标准的DBMS会有自己默认的隔离级别,通常为READ COMMITTED或REPEATABLE READ,利用该设置语句才能改变隔离级别,并可能影响随后定义的其他事务
五、隔离级别应用
1、需要说明的是,隔离级别是运行在该隔离级别下的事务遵循的封锁协议。事务加锁的类型、封锁粒度及保持锁的时间、产生的数据不一致问题等只与自身的隔离级别有关,与并发的其他事务的隔离级别无关,也不会影响到其他隔离级别下的事务对数据的读写方式。
但事务中的操作能否执行与并发事务在共享对象上的封锁情况有关
2、下面我们基于创建的学生选课数据库,通过两个并发事务的执行,来理解隔离级别对事务中数据操作的影响
3、首先来看一下两个事务在默认隔离级别,即READ COMMITTED读提交隔离级别下并发执行的情况
我们通过在SQL Server的对象资源管理器上打开两个查询窗口来执行两个并发事务,请在事务的并发执行过程中,认真观察并发调度中每一步的执行结果,分析操作的封锁情况以及事务的并发性和数据的一致性
(1)在查询窗口1开始执行事务T1,首先对关系表SC进行查询
BEGIN TRAN T1
SELECT * FROM SC;
(2)再在查询窗口2开始执行事务T2,修改学号为‘202218014030’的学生的成绩,操作可执行
BEGIN TRAN T2
UPDATE SC
SET GRADE=GRADE+50
WHERE SNO='202218014030';
SELECT * FROM SC;
查询可看到SC表中SNO='202218014030'的记录的GRADE值被更新,说明事务T1的读锁是短期锁,读锁只保持到语句执行完毕
(3)返回到查询窗口1,事务T1再次进行同样的查询,
BEGIN TRAN T1
SELECT * FROM SC;
SELECT * FROM SC;
可以看到查询不能执行,说明事务T2中“写锁”是长期锁,事务T2写完并没有释放锁,事务T1不能读未提交事务T2所写的数据,即不会读取脏数据
(4)返回到查询窗口2,提交事务T2
BEGIN TRAN T2
UPDATE SC
SET GRADE=GRADE+50
WHERE SNO='202218014030';
SELECT * FROM SC;
COMMIT TRAN T2
再返回到窗口1,发现事务T1可进行查询,但查询结果中SNO为‘202218014030’ 的元组的GRADE值与前一次查询不一致了,事务T1出现了不可重复读现象
这个例子说明默认隔离级别为READ COMMITTED,该隔离级别读锁是短期锁,只保持到语句执行完毕,事务不会读取“脏数据”,但会出现“不可重复读”现象
4、再来看一个在REPEATABLE READ可重复读隔离级别下的事务与两个在默认隔离级别,即READ COMITTED读提交隔离级别下的事务并发执行的情况
(1)在查询窗口1开始执行事务T1前,设置隔离级别为REPEATABLE READ可重复读,然后开始执行事务T1,首先对SC表中学号SNO='2022018014030'的元组进行查询,留意查询结果
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRAN T1
SELECT * FROM SC WHERE SNO='202218014030';
(2)在查询窗口2,在默认隔离级别下执行事务T2,首先修改学号SNO='202218014032'的学生的成绩,通过比较更新前后查询结果可看到更新可以实现
BEGIN TRAN T2
SELECT * FROM SC WHERE SNO='202218014032';
UPDATE SC SET GRADE=GRADE+2 WHERE SNO='202218014032';
SELECT * FROM SC WHERE SNO='202218014032';
(3)接着在修改学号SNO='202218014030'的学生的成绩,操作不能执行,说明事务T1的读锁是加在元组上的且读完并没有释放锁
BEGIN TRAN T2
SELECT * FROM SC WHERE SNO='202218014032';
UPDATE SC SET GRADE=GRADE+2 WHERE SNO='202218014032';
SELECT * FROM SC WHERE SNO='202218014032';
UPDATE SC SET GRADE=GRADE+2 WHERE SNO='202218014030';
(4)返回事务T1,重新对SC表中SNO='202218014030'的元组进行查询,发现T1可重复读取数据
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRAN T1
SELECT * FROM SC WHERE SNO='202218014030';
SELECT * FROM SC WHERE SNO='202218014030';
(5)在查询窗口3中执行单语句事务操作,向表SC中插入一个学号为‘202218014030’的学生的选课元组,会发现操作可以执行,不用等待
INSERT INTO SC VALUES('202218014030','004',70);
(6)返回事务T1,重新对SC表中SNO='202218014030'的元组进行查询,可见事务T1再次查询得到了与上一次不一致的结果,即产生幻影现象
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRAN T1
SELECT * FROM SC WHERE SNO='202218014030';
SELECT * FROM SC WHERE SNO='202218014030';
SELECT * FROM SC WHERE SNO='202218014030';
(7)然后提交事务T1
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRAN T1
SELECT * FROM SC WHERE SNO='202218014030';
SELECT * FROM SC WHERE SNO='202218014030';
SELECT * FROM SC WHERE SNO='202218014030';
COMMIT
(8)再返回事务T2,可见更新操作已可执行,查询后可看到更新结果
BEGIN TRAN T2
SELECT * FROM SC WHERE SNO='202218014032';
UPDATE SC SET GRADE=GRADE+2 WHERE SNO='202218014032';
SELECT * FROM SC WHERE SNO='202218014032';
UPDATE SC SET GRADE=GRADE+2 WHERE SNO='202218014030';
SELECT * FROM SC WHERE SNO='202218014030';
这个例子说明,并发事务可在不同的隔离级别下执行
六、小结
1、隔离级别表示在与其他事务并发执行时所能容忍的被其他事务干扰的程度
2、ANSI标准隔离级别中的4个隔离级别,从上到下隔离级别从低到高,隔离级别越高,隔离性能越好,相互干扰越小,数据不一致问题越少,但事务的并发程度越低