文章目录
- 一、隔离级别
- 1.1 几种数据读取现象
- 1.2 隔离级别介绍
- 1.3 隔离级别相关操作
- 1、隔离级别相关信息与设置
- 2、RC隔离级别
- 3、RCS隔离级别
- 4、RR隔离级别
- 5、SNAPSHOT隔离级别
一、隔离级别
1.1 几种数据读取现象
1)脏读
脏读就是指读取到了未提交的事务数据
2)不可重复读
在同一事务中,两次读取的数据记录内容不一致,更偏向与数据的更改。
3)幻读
主要指在一个事务中查看或者更新某一范围记录,由于其他事务对该范围内写入了新数据,导致事务一没有返回或更新新插入的数据记录,让人感觉出现了幻读的感觉。
幻读与不可重复读类似,但是幻读更偏向是由于insert导致事务中的一个范围查询两次结果不一致。
1.2 隔离级别介绍
隔离级别 | SQL Server中的特点 | 并发控制模型 | MySQL中的特点 |
Read Uncommitted | 允许脏读、不可重复读、幻读;并发性最好 | 悲观 | 与SQL Server一致 |
Read Committed | SQL Server的默认隔离级别;解决了脏读,存在不可重复读、幻读;事务内语句运行完后便会释放共享锁;并发性差 | 悲观 | 阿里云RDS默认隔离级别,写不阻塞读(MVCC) |
Repeatable Read | 解决了脏读、不可重复读,存在幻读;事务提交/回滚后才可释放共享锁(不可U/D、可以I);并发性差 | 悲观 | MySQL8之前的默认隔离级别;解决了脏读、不可重复读、幻读 |
Serializable | 解决了脏读、不可重复读、幻读,事务之间完全隔离,并发性最差 | 悲观 | 与SQL Server一致 |
Read Committed SNAPSHOT | 解决了脏读,存在不可重复读、幻读;并发性良好 | 乐观 | 无 |
snapshot | 不存在脏读、不可重复读、幻读的现象 ;并发性良好 | 乐观 | 无 |
1.3 隔离级别相关操作
1、隔离级别相关信息与设置
-- 查看当前隔离级别
DBCC Useroptions
-- 查看数据库并发性支持
select name,user_access,user_access_desc,
snapshot_isolation_state,snapshot_isolation_state_desc,
is_read_committed_snapshot_on
from sys.databases
-- 提升数据库并发行参数修改
ALTER DATABASE dbname SET SINGLE_USER WITH ROLLBACK IMMEDIATE
ALTER DATABASE dbname SET ALLOW_SNAPSHOT_ISOLATION ON
ALTER DATABASE db1 SET READ_COMMITTED_SNAPSHOT ON
ALTER DATABASE dbname SET MULTI_USER
2、RC隔离级别
1)隔离级别设置:SET TRANSACTION ISOLATION LEVEL read committed
2)同一事务下不可重复读
RC隔离级别下,同一事务会读取到当前状态下已提交的最新数据记录,所以会造成多次读取相同的记录行却获取的结果不一致的情况。
Session A | Session B | 描述 |
begin tran; | - | - |
select view_count from deadlock_test where id=1 //view_count=97 | - | Session A 或许共享锁,语句执行完毕后就会释放 |
- | update deadlock_test set view_count=view_count+1 where id=1; //success | SessionB获取到具体的X锁进行更新 |
select view_count from deadlock_test where id=1 //view_count=98 | - | SessionA 可以读取到已提交事务的数据,同一事物内存在不可重复读 |
commite tran; | - |
3)写事务未及时提交阻塞读
RC隔离级别下并没有实现多版本控制,当其他事务的X锁未提交释放时,就会导致被X锁锁住的记录行在读取(S锁)的时候被阻塞。也正是该原因,该隔离级别下的并发性并没有较好的提升。
session A | Session B | 描述 |
begin tran; | begin tran; | |
select view_count from deadlock_test where id=1; //view_count=98 | select view_count from deadlock_test where id=1; //view_count=98 | |
update deadlock_test set view_count=view_count+1 where id=1; //succuee | - | |
select view_count from deadlock_test where id=1; //view_count=99 | select view_count from deadlock_test where id=1 //被阻塞 | SessionB被阻塞 |
commit tran; | //view_count=99 | SessionB直到SessionA提交事务后才返回结果 |
其参数锁等待相关信息:
3、RCS隔离级别
1)设置数据库可使用RCS
-- 查看是否支持RCS
select name,user_access,user_access_desc,
snapshot_isolation_state,snapshot_isolation_state_desc,
is_read_committed_snapshot_on
from sys.databases
-- 设置数据库支持RCS
ALTER DATABASE dbname SET READ_COMMITTED_SNAPSHOT ON;
设置完成后显示如下:
2) 同一事务下的不可重复读
在RCS的隔离级别下,与RC一样同一个事务下会读取到该记录的最新版本信息,所以就会导致同一事务下多次查询统一记录行却显示不同结果的情况。
Session A | Session B | 描述 |
begin tran; | - | |
select age from t1 where id=1; //age=24 | - | |
- | update t1 set age=age+1 where id=1 //succes | |
select age from t1 where id=1; //age=25 | - | |
commit tran; | - |
3) 写事务未提交下的快照读(可重复读)
该现象就是RCS与RC隔离级别的不同,RCS使用行版本控制来保证同一事务下每次读取相同记录行的数据一致性。
Session A | Session B | 描述 |
begin tran; | begin tran; | |
select age from t1 where id=1; //age=25 | select age from t1 where id=1; //age=25 | |
update t1 set age=age+1 where id=1 //success | - | |
select age from t1 where id=1; //age=26 | select age from t1 where id=1; //age=25 | Session A未提交时,SessionB不会被阻塞,进行快照读 |
commit tran; | select age from t1 where id=1; //age=26 | |
- | commit tran; |
期间Session所持有资源情况:
4、RR隔离级别
1)通过锁实现了可重复读
在RR隔离级别下,只有当事务提交或者回滚的情况下才会释放共享S锁,不像RC隔离级别,当语句执行完毕后就会释放共享S锁。所以该模式下实现的可重复读,是通过锁来达到的。
Sesseion A | Session B | 描述 |
begin tran; | - | |
select age from t1 where id=1; //age=27 | - | |
- | update t1 set age=age+1 where id=1 //被阻塞 | SessionB被阻塞 |
commit tran; | - | Session B的update操作执行成功 |
select age from t1 where id=1; //age=28 | select age from t1 where id=1; //age=28 |
该情况下各个会话的锁持有与锁等待信息:
2)幻读
在RR隔离级别下,无法解决幻读的现象。这是由于锁一定程度上无法对各行之间的间隙进行控制,就会导致其他事务可以对该间隙进行insert,从而导致同一事务下两次查询的范围记录好像多了,出现了一种幻读的现象。
Session A | Session B | 描述 |
begin tran; | - | |
select count(*) as cnt from t1 where name=‘cbs’; //cnt=3 | - | |
- | insert into t1 values(9,‘cbs’,22,0) //success | |
select count(*) as cnt from t1 where name=‘cbs’; //cnt=4 | - | |
commit tran; | - |
3)写未提交阻塞读操作
正是由于该隔离级别下只有等事务提交或者回滚后才会释放共享S锁,所以在事务未提交的情况下,若该事务中持有部分X锁,会导致对这部分X锁记录读取被阻塞。
Session A | Session B | 描述 |
begin tran | begin tran; | |
select count(*) as cnt from t1 where name=‘cbs’; //cnt=4 | select count(*) as cnt from t1 where name=‘cbs’; //cnt=4 | |
insert into t1 values(8,‘cbs’,22,0) //success | - | |
select count(*) as cnt from t1 where name=‘cbs’; //cnt=5 | select count(*) as cnt from t1 where name=‘cbs’; //被阻塞 | |
commit tran; | - //cnt=6 | Session B返回结果 |
5、SNAPSHOT隔离级别
1)设置允许snapshot隔离级别
ALTER DATABASE db2 SET ALLOW_SNAPSHOT_ISOLATION ON;
SET TRANSACTION ISOLATION LEVEL SNAPSHOT
2)可重复读
Sesseion A | Session B | 描述 |
begin tran; | - | |
select count(*) as cnt from t1 where name=‘cbs’; //cnt=7 | select count(*) as cnt from t1 where name=‘cbs’; //cnt=7 | |
update t1 set age=age+1 where id=1 //success | - | |
select count(*) as cnt from t1 where name=‘cbs’; //cnt=8 | select count(*) as cnt from t1 where name=‘cbs’; //cnt=7 | |
commit tran; | - | |
select count(*) as cnt from t1 where name=‘cbs’; //cnt=8 | select count(*) as cnt from t1 where name=‘cbs’; //cnt=7 | 只要该事务未提交,读取的记录永远是当前快照版本 |
- | commit tran; | |
- | select count(*) as cnt from t1 where name=‘cbs’; //cnt=8 | 事务提交后,读取记录为最新提交版本下的记录 |
2)无幻读
在RR隔离级别下,无法解决幻读的现象。这是由于锁一定程度上无法对各行之间的间隙进行控制,就会导致其他事务可以对该间隙进行insert,从而导致同一事务下两次查询的范围记录好像多了,出现了一种幻读的现象。
Session A | Session B | 描述 |
begin tran; | begin tran; | |
select count(*) as cnt from t1 where name=‘cbs’; //cnt=7 | - | |
- | insert into t1 values(9,‘cbs’,22,0) //success | |
select count(*) as cnt from t1 where name=‘cbs’; //cnt=4 | - | |
commit tran; | - |