首页 > 数据库 >sqlserver 事务隔离级别与脏读、不一致读、幻读

sqlserver 事务隔离级别与脏读、不一致读、幻读

时间:2023-10-24 14:14:33浏览次数:30  
标签:事务 幻读 sqlserver somx 脏读 read -- where select

sqlserver查询窗口的默认事务隔离级别为:read committed,可以通过下面SQL语句查看:

SELECT CASE transaction_isolation_level
       WHEN 0
         THEN 'Unspecified'
       WHEN 1
         THEN 'ReadUncommitted'
       WHEN 2
         THEN 'ReadCommitted'
       WHEN 3
         THEN 'Repeatable'
       WHEN 4
         THEN 'Serializable'
       WHEN 5
         THEN 'Snapshot' END AS TRANSACTION_ISOLATION_LEVEL
FROM sys.dm_exec_sessions
WHERE session_id = @@SPID

 

 

 

 

read uncommitted: 这是事务最低粒度的隔离级别,它允许在操作过程中不会锁,从而让当前事务读取到其他事务的数据。(存在脏读、不一致读、幻读)

 复现脏读(sqlserver2016取消了read uncommitted),set transaction isolation level Read unCommitted

在sqlserver新建两个查询窗口,分别执行下面的事务(先执行事务1)

--事务1
--不用在会话中设置事务的隔离级别,事务不要写commit或rollback,确保当前事务没有提交或者被取消 begin tran update jserp.somx set somx_wlid='888888' where somx_soid='60548712' and somx_soxh='99910'
--事务2
--设置会话事务级别 set transaction isolation level Read unCommitted select * from jserp.somx where somx_soid='60548712' and somx_soxh='99910' --或在查询语句中使用with(nolock) select * from jserp.somx with(nolock) where somx_soid='60548712' and somx_soxh='99910'

 此时,事务1被更新的行加了X锁(排它锁),但事务2仍读取到了事务1修改尚未提交的数据,导致脏读(dirty read)的问题。因为事务2的隔离级别为Read unCommitted,事务1的X锁不会阻止事务2读取。

 

read committed:资源(行)被当前事务更新锁定,但是没有commit或rollback,此时其他事务无法访问被锁定的行。(避免脏读)

避免脏读:修改前一个例子的事务2,设置隔离级别为:set transaction isolation level Read Committed,其他执行操作不变,直接查看两个事务的锁情况

--查看当前会话的隔离级别   select transaction_isolation_level,* from sys.dm_exec_sessions where session_id=@@spid 
--select request_session_id spid,object_name(resource_associated_entity_id) table_name from sys.dm_tran_locks where resource_type='object'
select request_session_id,* from sys.dm_tran_locks where resource_type<>'database' order by 1

 观察发现,事务2给要获取的资源(行)加了S锁,但状态是wait,也就是说事务2处于等待状态。

 

 read committed:只能避免脏读,不能避免不一致读

 复现不一致读:在sqlserver新建两个查询窗口,分别执行下面的事务(先执行事务1,在20s内执行事务2),发现事务1两次select查询的结果不同。

--事务1
set transaction isolation level Read Committed
begin tran
    select * from jserp.somx where somx_soid='60548712' and somx_soxh='99910'
    waitfor delay '00:00:20'
    select * from jserp.somx where somx_soid='60548712' and somx_soxh='99910'
    commit
--事务2
update jserp.somx set somx_wlid='222222' where somx_soid='60548712' and somx_soxh='99910'

可能有人会想不通,为什么事务1的第一个select给要查询的行加了S锁,事务2仍然可以更新改行,我也想了好久。事务1中第一条select语句会给要查询的行加一个共享锁(S锁),它确实会阻止其他事务对该行数据进行排他性操作,如更新或删除。

但是需要注意的是,共享锁(S锁)是一种短暂的锁,当事务读取完毕后会立即释放。waitfor delay '00:00:20' 该语句只会使当前事务暂停执行指定的时间,然后再继续执行后续的语句,但第一个select加的S锁已释放。这意味着在 Read Committed 隔离级别下,其他事务仍有机会在事务1查询某行数据时对该行进行更新。

 

repeatable read:保障在一个事务内重复读取时,始终能够读取到相同的内容。(避免不一致读 

避免不一致读:修改前一个例子的事务1,设置隔离级别为:set transaction isolation level Repeatable Read,其他执行操作不变,直接查看两个事务的锁情况

通过查询发现,事务1给行加了一个S锁,事务2给行加的X锁(wait)。等事务1执行完毕,才会执行事务2的更新操作

 

 上述的三个隔离级别,都是针对行数据进行加锁的。

 

serializable:事务的最高隔离级别(避免幻读)
幻读:一个事务先后两次在同一个范围内查询数据,并且在两次查询间隔内,另一个事务这个范围内插入一条或多条新的数据,导致前一个事务两次读取到的数据不一致。

 下图,在 Transaction 2 操作过程中,会对 Range 进行加锁,此时其他事务无法操作其中的数据,只能等待或者放弃

--事务1
set transaction isolation level serializable
begin tran
    select * from jserp.somx where somx_soid='60548712' and somx_soxh between '99910' and '999100'
    waitfor delay '00:00:20'
    select * from jserp.somx where somx_soid='60548712' and somx_soxh between '99910' and '999100'
    commit
 --事务2
 insert into jserp.somx ...99920

查询发现当前范围内的所有行都加上了RangeS-S范围共享锁(Range Shared Lock)。另一个事务要在这个范围内插入两条记录,并加了一个RangeI-N
范围间隙锁(Range In-Range Gap Lock),此时范围间隙锁处于wait状态,确保事务1执行完毕。
设置事务隔离级别serializable会对where条件范围内的记录全部加锁。

 

总结:事务的隔离级别是通过锁权限和锁生命周期实现的。

1、在read uncommitted下,S锁可以访问X锁锁定的行;

2、在read committed下,S锁访问X锁锁定的行时,处于wait状态,被阻塞。S锁在事务生命周期内短暂存在,当事务读取完毕后会立即释放,事务继续执行后面的语句;(加S锁在读取某一行的那一刻,X锁需要等待)

3、在repeatable read下,S锁在整个事务生命周期内保持,直到事务提交或者撤回,这期间会阻塞X锁。

4、在serializable下,RangeS-S范围共享锁与repeatable read情况相似,不同的是不再作用数据库某一行,而是一个范围内的行都将被加上RangeS-S锁,如果其他事务insert一条语句正好在前面这个范围,将被阻塞。


参考资料: 

https://www.jianshu.com/p/ba8de5bc51e2

标签:事务,幻读,sqlserver,somx,脏读,read,--,where,select
From: https://www.cnblogs.com/pandora2050/p/17777526.html

相关文章

  • sqlserver 服务器主体 无法在当前安全上下文下访问数据库
    SELECTname,database_id,is_trustworthy_onFROMsys.databasesALTERDATABASEbole_dataSETTRUSTWORTHYONALTERAUTHORIZATIONONDATABASE::bole_dataTObole 今天使用sqlserver,发现了一个问题,就是使用insertinto数据库名.dbo.表名(字段)values(值)这样语句的......
  • mysql,sqlserver,oracle各自的存在更新不存在添加写法
    mysql,sqlserver,oracle各自的存在更新不存在添加写法在向表中插入数据的时候,经常遇到这样的情况:首先判断数据是否存在;如果不存在,则插入:如果存在,则更新。SQLserver脚本先查询,没有数据再进行数据插入,有数据就走更新ifnotexists(select1fromtwhereid=1)ins......
  • sqlserver在设计表结构时,如何选择字段的数据类型
    在设计表结构时,选择适当的字段数据类型是非常重要的,它会直接影响数据库的性能、存储空间和数据的完整性。以下是在SQLServer中选择字段数据类型时的一些建议和理由:1.整数类型:在SQLServer中,整数类型包括INT、BIGINT、SMALLINT和TINYINT。根据数值范围和数据需求,选择合适......
  • jpa 连接sqlserver 发布tomcat报错 SunJSSE
    报错信息:java.ext.dirs:/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.332.b09-1.el7_9.x86_64/jre/lib/ext:/usr/java/packages/lib/ext20-Oct-202316:37:28.074信息[com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread-#0]com.microsoft.sqlserver.jdbc.TDSChanne......
  • SQLServer编辑SQL查询的数据
    设置:视图》工具栏》勾选视图设计器正常右击表,编辑前200行点击1,会显示编辑前200行SQL,添加where条件,点击2执行,即可进行编辑数据  修改编辑前n行(默认编辑前200)工具》选项》sqlserver对象资源管理器》命令》编辑前N行 ......
  • 通过SQLserver系统存储过程分离和附加数据库的简单使用
    sp_detach_db 数据库分离sp_detach_db 是SQLServer中的一个系统存储过程,用于卸载数据库。使用这个存储过程可以使得数据库不再与当前SQLServer实例关联。以下是 sp_detach_db 的基本语法:sp_detach_db'database_name';其中 'database_name' 是要卸载的数据库的名......
  • SqlServer的执行计划如何分析?
    sqlserver的执行计划执行计划是SQLServer中的一个重要工具,用于分析和优化查询的性能。它提供了关于查询的详细信息,包括查询的执行顺序、使用的索引、连接类型、过滤条件等。What(什么):执行计划显示了查询的执行计划,即查询的逻辑操作和物理操作。它告诉你查询是如何执行的,包......
  • SQL_导出SQLserver数据库表和数据
    1.选择数据,右键。任务----生成脚本 2.选择具体的数据库对象 3.输入要保存的路径,点击高级。  4.在高级脚本编写选项中,选择架构和数据 ......
  • docker安装sqlserver2019
    1.拉取镜像dockerpullmcr.microsoft.com/mssql/server:2019-latest2.执行dockerrun命令: dockerrun--namesqlserver-wangmj--hostnamesqlserver-master\-p1434:1433\-e'ACCEPT_EULA=Y'\-e'SA_PASSWORD=sdfasdfasdfsdf?'......
  • sqlServer查询字段位数不够补0方法
    1.查询字段为字符串函数:RIGHT('0000'+字符串,n)即:从右侧截取字符串,n代表侧截取的位数实例:SELECTRIGHT('0000'+'66',3)//结果:066实例:SELECTRIGHT('0000'+'66',4)//结果:00662.查询字段非字符串......