事务的隔离级别(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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构