事务的隔离级别(Isolation Level)

如果不对事务进行隔离可能会产生的后果:

1.更新丢失: 两个事务同时更新一条数据,一个事务的失败导致两次修改均失败。

这是因为没有锁操作,可以同时操作数据。并发事务没隔离

2.脏读:已经修改了一条数据而没有及时提交,但该数据被并发读取,如果此时回滚数据,那并发读取的就是错误数据。那么获取的数据不一致。

3.不可重复读:一个事务范围内多次查询而返回不同值,原因是被另外的事务修改。

 

【脏读和不可重复读的区别】

脏读的主事务是在修改,没提交状态被读取。而不可重复读的主事务是在读取,多次读取被其他事务修改。

 

【注意Warning】

Oracle和Sql Server的默认隔离级别是RC提交读取。但是Mysql的默认隔离级别是RR可重复读取

 

需要对事务进行隔离。

 

隔离级别如下:

查看当前数据库的隔离级别

--查看当前的事务隔离级别,默认是read Committed 读已提交--
DBCC UserOptions

 

【未提交读取 Read UnCommitted。简称RU】 (相当于with(nolock))  适用于对数据完整性要求不高 或者 不经常修改的数据

允许脏读,但不允许更新丢失

如果一个事务开始写数据,那么其他事务不能写该数据,但是可以读。通过“排他写锁”实现。

由于不可同时写,所以不存在更新丢失,但是允许读,所以数据的修改前后都可以被读取,因此存在脏读和不可重复读。

此时此操作不申请锁,

复制代码
--设置事务的隔离级别--
set Transaction Isolation level Read UnCommitted
--第一个事务还未提交--
begin transaction
select * from Commodity where Id=7;    --此时结果为efcore test


--第二个事务修改--
update Commodity set Title='isolation test' where Id=7;   --修改之后,查询为 isolation test


--此时将第一个事务进行回滚--
rollback transaction      --回滚之后再次查询则为efcore test
复制代码

 总结:写时禁写可读

适用于单用户系统,不存在并发

 

【提交读取 Read Committed】  Sql Server/Oracle数据库的默认隔离级别

允许不可重复读,但不允许脏读。通过“瞬间共享读锁”和“排他写锁”实现。

读取数据的事务允许其他事务继续访问该数据,但未提交的数据则不允许其他事务访问

也就是说在修改时,不仅不允许其他事务进行写操作,甚至未提交也不允许读,直到本事务的写操作完成。强调了写操作的安全,所以避免了脏读。

但是并没有读操作的安全,依然会有不可重复读的风险。

总结:写时禁写禁未提交读

 

【可重复读取  Repeatable Read】  Mysql数据库的默认隔离级别

不允许脏读和不可重复读,但是可以幻读。通过“共享读锁”和“排他写锁”实现。

读取事务禁止写数据,但允许读数据,而写事务则禁止任何其他操作。

保证一个事务中的两个读操作之间,其他事务不能修改该数据。该级别在获取数据之前必先获得共享锁,并一直保持共享锁至事务结束

实例:

首先一个查询事务

复制代码
--查看隔离级别--
DBCC  UserOptions

--设置隔离级别为可重复读--
set transaction isolation level Repeatable read;

--开启事务--
begin transaction
select * from Commodity  where Id=4;



--回滚事务--
rollback transaction
复制代码

另外一个事务进行修改

复制代码
--开启事务,进行修改--

--当第一个事务还没有回滚的时候,这条语句没有执行完成,因为读时禁写。直到回滚之后,得到了共享锁,这里便完成了修改--
--而且必须是这里提交事务之后,那边才能读取,因为写时禁止一切操作--
begin transaction

update Commodity set Title='Repeatable Read test' where Id=4;

commit  transaction
复制代码

 这里当第一个事务回滚后进行查询的时候会发现 Title属性变成了Repeatable Read test。这是由于第一个事务还没结束的时候持有共享锁,

此时第二个事务想修改数据请求共享锁没有成功一直等待。当第一个事务回滚,结束事务的时候,第二个事务得到了共享锁,所以修改成功。

 

总结:写时禁其他,读时禁写

 

【幻读】

上面可重复读,执行事务的时候只能锁定第一次运行所获取的数据资源。比如上面的Id为4 的数据,但是无法锁定结果之外的行。即原本不存在于数据库中的数据。

如果两次查询之间,插入了满足第一次查询条件的数据,那么第二次查询将和第一次有所不同,这种读操作叫幻读。

 

第一个事务进行读取

--开启事务--
begin transaction
select * from Commodity  where Title='efcore test';   --第一次查询一共有2条--


select * from Commodity  where Title='efcore test';   --第二次查询一共有3条,包含了新增的一条--

第二个事务进行插入

--开启事务,进行插入--

--由于插入的记录满足Title为efcore test的情况。由于这是新增数据,并没有操作查询事务里面Title为efcore test的数据
--所以是可以执行的
--注意:是不允许对第一个事务查询的数据进行写操作,因为第一个事务获得了共享锁,而不是数据库不能进行写操作,可以正常插入!
begin transaction
insert into Commodity(CategoryId,Title,Price,Url,ImgUrl) values(1,'efcore test',123,'44.com','dddd.jpg')
commit  transaction

 

【序列化(Serializable)】严格的事务隔离

事务只能一个一个执行。不过效率较低,一般不使用。所以不会出现上诉的幻读,因为读取事务和插入新数据的事务也是要一次进行的。

 

 

一般使用的隔离级别是Read Committed提交读取。尽管会带来不可重复读和幻读。但可以使用悲观锁和乐观锁来解决。

 

 

【已提交读快照  Read Committed Snapshot】

和前面的已提交读机制相同,隔离级别是 读操作之前的已提交版本。

和RC一样,不可避免  不可重复读/幻读。 但是比RC多了不需要共享锁来获取数据。比如在RC隔离级别下,当前数据被事务所修改但还未提交,那么此时读取就会堵塞。

但是已提交读快照并不会堵塞

开启RC快照,并读取

--设置隔离级别为已提交读快照--
alter database Products set Read_Committed_Snapshot on;
go

select * from Commodity where id=4;
begin transaction

开启修改事务

复制代码
--此时开启修改事务--
--并没有提交,由于第一个事务开启了提交读取快照,所以第一个读取事务依然能够查询--
--但是修改的结果在第一个事务查不到,依然是旧结果--
begin transaction
update Commodity set Title='Repeatable Read test01' where id=4;
select * from Commodity where id=4;



Commit transaction    --此时提交事务,在第一个读取事务里面就可以查到修改结果了
复制代码

在修改事务还未提交的过程中,查询事务依然能够查询,但查询的是旧数据,直到修改事务提交。

 

但是在RC隔离级别中,在修改事务未提交时时查询不到数据的,处于堵塞状态

关闭RC快照的语句

--设置隔离级别为已提交读快照(必须关闭除了当前连接之外的其他连接)--
ALTER DATABASE [Products] SET SINGLE_USER WITH ROLLBACK IMMEDIATE ;
ALTER DATABASE [Products] SET READ_COMMITTED_SNAPSHOT OFF;
ALTER DATABASE [Products] SET MULTI_USER;
Go

 

posted @   RookieCoderAdu  阅读(330)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示