首页 > 数据库 >Sql Server 数据库事务与锁,同一事务更新又查询锁?期望大家来解惑

Sql Server 数据库事务与锁,同一事务更新又查询锁?期望大家来解惑

时间:2023-04-28 09:36:11浏览次数:39  
标签:事务 隔离 数据库 Sql Server SQL 读取

我有一个People表,有三行数据:
image

如果我们没详细了解数据库事务执行加锁的过程中,会不会有这样一个疑问:如下的这段 SQL 开启了事务,并且在事务中进行了更新和查询操作。

BEGIN TRAN 
	update People set Name='张三' where id=1;
	
	select * from People where id=1;
commit tran

我们知道sql server数据库的默认事务级别是READ COMMITTED(已提交的读取),我们再看一下已提交读事务隔离级别描述:

允许事务读取另一个事务以前读取(未修改)的数据,而不必等待第一个事务完成。 SQL Server数据库引擎将保留 (对所选数据) 获取的写入锁,直到事务结束,但读取锁将在执行 SELECT 操作后立即释放。 这是SQL Server数据库引擎默认级别。

那么我们在READ COMMITTED 隔离级别下更新People表数据库,按照这个逻辑在id=1的数据行上添加排它锁(X锁)并等到事务提交后才会释放锁。
但是事务继续执行查询,在READ COMMITTED隔离级别下 Select 会对查询数据施加共享锁(S锁)。因为有排它锁,所以查询无法获得共享锁需要等待排它锁释放,如果按照这个逻辑的话这个事务自身就死锁无法执行了。

但这个事务还是会正常执行完成,针对这个疑问,那么我们看下数据库的事务和锁:

数据库引擎隔离级别

ISO 标准定义了以下隔离级别,SQL Server数据库引擎支持所有这些隔离级别:

隔离级别 定义
未提交的读取 隔离事务的最低级别,只能保证不读取物理上损坏的数据。 在此级别上,允许脏读,因此一个事务可能看见其他事务所做的尚未提交的更改。
已提交的读取 允许事务读取另一个事务以前读取(未修改)的数据,而不必等待第一个事务完成。 SQL Server数据库引擎将保留 (对所选数据) 获取的写入锁,直到事务结束,但读取锁将在执行 SELECT 操作后立即释放。 这是SQL Server数据库引擎默认级别。
可重复的读取 SQL Server数据库引擎会保留对所选数据获取的读取和写入锁定,直到事务结束。 但是,因为不管理范围锁,可能发生虚拟读取。
可序列化 隔离事务的最高级别,事务之间完全隔离。 SQL Server数据库引擎保留对所选数据获取的读取和写入锁定,这些锁将在事务结束时释放。 SELECT 操作使用分范围的 WHERE 子句时获取范围锁,主要为了避免虚拟读取。 注意: 请求可序列化隔离级别时,复制的表上的 DDL 操作和事务可能失败。 这是因为复制查询使用的提示可能与可序列化隔离级别不兼容。

image
SQL Server数据库引擎使用不同的锁模式锁定资源,这些模式确定并发事务如何访问资源。

T-SQL 设置事务隔离级别,只对当前会话连接一直有效

SET TRANSACTION ISOLATION LEVEL
    { READ UNCOMMITTED
    | READ COMMITTED
    | REPEATABLE READ
    | SNAPSHOT
    | SERIALIZABLE
    }

锁模式

下表显示了SQL Server数据库引擎使用的资源锁模式。

锁模式 说明
共享 (S) 用于不更改或不更新数据的读取操作,如 SELECT 语句。
更新 (U) 用于可更新的资源中。 防止当多个会话在读取、锁定以及随后可能进行的资源更新时发生常见形式的死锁。
排他 (X) 用于数据修改操作,例如 INSERT、UPDATE 或 DELETE。 确保不会同时对同一资源进行多重更新。
意向 用于建立锁的层次结构。 意向锁包含三种类型:意向共享 (IS)、意向排他 (IX) 和意向排他共享 (SIX)。
架构 在执行依赖于表架构的操作时使用。 架构锁包含两种类型:架构修改 (Sch-M) 和架构稳定性 (Sch-S)。
大容量更新 (BU) 在将数据大容量复制到表中且指定了 TABLOCK 提示时使用。
键范围 当使用可序列化事务隔离级别时保护查询读取的行的范围。 确保再次运行查询时其他事务无法插入符合可序列化事务的查询的行。

锁兼容性

锁兼容性控制多个事务能否同时获取同一资源上的锁。 如果资源已被另一事务锁定,则仅当请求锁的模式与现有锁的模式相兼容时,才会授予新的锁请求。 如果请求锁的模式与现有锁的模式不兼容,则请求新锁的事务将等待释放现有锁或等待锁超时间隔过期。 例如,没有与排他锁兼容的锁模式。 如果具有排他锁(X 锁),则在释放排他锁(X 锁)之前,其他事务均无法获取该资源的任何类型(共享、更新或排他)的锁。 另一种情况是,如果共享锁(S 锁)已应用到资源,则即使第一个事务尚未完成,其他事务也可以获取该项的共享锁或更新锁(U 锁)。 但是,在释放共享锁之前,其他事务无法获取排他锁。

下表显示了最常见的锁模式的兼容性。
image

查看执行时锁的情况

通过锁的兼容性模式我们知道在id=1的行上添加了排它锁,那么它就无法再接收任何锁,那我们调试这个事务看看锁的情况。
image

我们调试到第3行,这个时候看下锁的情况,此时事务添加了key(行)排它锁X锁,page(页)和object(表)添加了意向排它锁IX锁

SELECT 
resource_type,
resource_database_id,
resource_description,
request_mode,
request_type,
request_session_id,
request_owner_type,
request_owner_id,
lock_owner_address
FROM sys.dm_tran_locks where request_owner_type='transaction'

image

然后我们再继续调试到第4行,此时还没提交事务,排它锁X依然存在,但是没有S锁。

image

我们知道在读提交事务隔离级别下,S锁是使用完了就释放的,所以我们用SQL Server Profiler来监视下锁的情况,设置监控的项为lock,然后设置筛选条件。

image
image

上面我已经将 张三1 改为了 张三,我们再将 张三 改回 张三1,并启动监控。

BEGIN TRAN 
	update People set Name='张三1' where id=1;
	select * from People where id=1;
commit tran

image

可以看到事务 transactionid=30010685 的锁监控 :

  • 首先申请IX更新意向锁(object,page) 准备更新,然后获得行上的X排它锁进行更新,更新后释放了行锁和page锁(EventClass= Lock:released,Mode=0-null)。
  • 等查询时申请page页IS意向读取锁,并获得行S锁读取数据后释放行锁和page页锁。
  • 最后还有几个顺序释放,依次是key、page、Object,这里恰好和上面调试还没提交事务时查询sys.dm_tran_locks的锁情况一样,也就是说事务提交后依次又进行了一遍释放。

通过上面我们得出结论,事务里面并不是取得了X锁要等事务结束后才释放,在事务执行过程中也是有释放的,只是事务还保持着对于资源锁的逻辑标识,防止其它事务并发(这里是我推断的,没找到相关文献,望大佬指正)。

所以事务是在锁上更宏观的逻辑隔离,事务隔离级别只是在业务上保证数据符合隔离级别预期,至于事务中如何控制锁是基于数据库内在设计,而不能通过事务的描述去推断锁过程。

我查阅网上很多博文和官方资料都是讲事务和锁概念,有时候结合两种也是模棱两可看不出什么强联系,没有讲事务执行过程中锁是如何变化的,不知道我这篇推论是否正确,欢迎指正。

再次验证

我将事务隔离级别设置为REPEATABLE READ(可重复读),然后调试到commit行还没提交,我们看跟踪的锁和事务锁表dm_tran_locks查询的结果,按照REPEATABLE READ描述,select查询的S锁会在事务提交后释放,我们看看截图情况

image

开启了SQL Server Profiler结果,查询id=3后S锁已经释放。

image

再查dm_tran_locks表,表中依然显示事务获取了S锁,并且 resource_description=98ec012aa510 资源描述和上面跟踪是对应的。
image

最后我们执行完调试,跟踪锁显示又按照顺序释放了一遍锁

image

标签:事务,隔离,数据库,Sql,Server,SQL,读取
From: https://www.cnblogs.com/SunSpring/p/17356694.html

相关文章

  • SQLSERVER 动态生成列、合计行
    /*test110'2023-04-26','2023-04-26','全部','901100569,901100570,901100571,901100572,901100573';*/ALTERPROCEDURE[dbo].[test110]@StartDatedatetime,@EndDatedatetime,@unameVARCHAR(50)='......
  • 当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!
    开心一刻中午和哥们一起喝茶哥们说道:晚上喝酒去啊我:不去,我女朋友过生日哥们瞪大眼睛看着我:你有病吧,充气的过什么生日我生气到:有特么生产日期的好吧需求背景系统对接了外部系统,调用外部系统的接口需要付费,一个接口一次调用付费0.03元同一个......
  • MySQL转移字符
    MySQL语言中的转义字符和各种编程语言基本相同,见下表形式含义\00(NUL)字符\n换行\r回车符\t制表符\b退格\'单引号\"双引号\\反斜线\%%符(用于区分模式匹配中的%)\__符(用于区分模式匹配中的_)举其中一个用的比较多的'\n'为例,执行以下......
  • SQLServer2005 AMD8450,3核CPU装不上sql 2005的解决办法
    中午12点开始,安装SQLServer2005,一直到晚上9点半,把网上的各个文章翻了个遍,依然没有安装上我的SQLServer2005,安装不上的症状跟网上其它人遇到的一样,可是为什么别人的就解决了,我的就不行呢```带着郁闷的心情睡觉了```夜里3点几分,起夜,想到数据库还......
  • Sql Server 2005 在建立与服务器的连接时出错。provider,error: 40
    在建立与服务器的连接时出错。在连接到SQLServer2005时,在默认的设置下SQLServer不允许进行远程连接可能会导致此失败。(provider:命名管道提供程序,error:40-无法打开到SQLServer的连接)(.NetSqlClientDataProvider) 网上找的解决办法对我的不适用下面上网......
  • upupw mysql数据库密码初始化
    1、启动upupw2、打开cmd窗口,进入到数据库目录/UPUPW/MariaDB/bin。3、执行命令:mysql -uroot-p;4、用系统提供的密码登录,默认的是:DRsXT5ZJ6Oi55LPQ5、进入mysql管理界面后,执行命令:updateusersetauthentication_string=password("root")whereuser='root'; 提示:Query......
  • mybatis定义sql语句标签之select 标签
    属性介绍:id:唯一的标识符.和Mapper接口定义方法名同名。parameterType:传给此语句的参数的全路径名或别名例:com.test.poso.User或user,目前很少用到。resultType:语句返回值类型或别名。注意,如果是集合,那么这里填写的是集合的泛型,而不是集合本身(resultType与resultMap不......
  • 找出SQLServer数据库I/O高的原因
    找出SQLServer数据库I/O高的原因影响SQLServer性能的因素有很多,比如CPU、I/O、内存、错误的执行计划、不恰当的索引或缺少索引等。当查询变慢时,我发现最常见的一件事是由于查询执行的I/O太大。当一个查询因为I/O而变慢时,可能是因为糟糕的硬件、糟糕的执行计划,但通常是糟糕的数据......
  • mybatis定义sql语句标签之insert标签
    这是非常核心的一个标签,CURD是mybatis的核心功能。insert元素相对于select元素要简单很多,mybatis会在插入之后返回一个整数,表示插入成功后插入的条数。真实项目中,使用最多的一个点要注意,在插入过程中返回一些自动主键。因为这个主键,在同一个事务中,还有其它用。例如级联。属性......
  • mybatis控制动态SQL拼接标签之foreach标签
    mybatis控制动态SQL拼接标签之foreach标签foreach标签主要用于构建in条件,可在sql中对集合进行迭代。也常用到批量删除、添加等操作中。这个标签在实际业务中非常常用,当然运维旧项目也会发现,有些坑,用java循环执行sql来表示批量插入。属性说明:collection:collection属性的值有三......