博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
一次意外的X锁不阻塞问题
阅读量:5155 次
发布时间:2019-06-13

本文共 2458 字,大约阅读时间需要 8 分钟。

    最近有一个朋友问我一个关于给查询操作强制上X锁却不阻塞的问题。该查询写在一个存储过程中,代码如代码1所示:

1: create PROC [dbo].[GetCityOrders]
2:     @city NVARCHAR(10) ,
3:     @num INT
4: AS
5:     SET NOCOUNT ON
6: 
7:     BEGIN TRY
8: 
9:         BEGIN TRAN
10: 
11:         SELECT TOP ( @num )
12:                 id ,
13:                 number ,
14:                 price ,
15:                 mid ,
16:                 @city city
17:         INTO    #cityorders
18:         FROM    cmcc WITH ( XLOCK )
19:         WHERE   prov = 0
20:                 AND status = 0
21:                 AND city = @city
22: 
23:         UPDATE  cmcc
24:         SET     status = 100
25:         WHERE   id IN ( SELECT  id
26:                         FROM    #cityorders )
27: 
28:         SELECT  o.* ,
29:                 c.attach
30:         FROM    #cityorders o
31:                 LEFT JOIN cmcc_attach c ON o.id = c.id
32: 
33:         DROP TABLE #cityorders
34: 
35:         COMMIT TRAN
36: 
37:     END TRY
38:     BEGIN CATCH
39: 
40:         ROLLBACK
41: 
42:     END CATCH

代码1.

 

    该存储过程首先通过对查询操作加X锁,使得其他读取操作更新时不影响该部分加X锁的操作。乍一看没有任何问题,但是当业务上线后就发现,即使查询有了X锁,但实际上还是会有多个调用该存储过程的客户端同时读取到同一条数据的现象现象。

 

原因?

    为了验证原因,我们来做一个Demo测试,首先我们创建测试表,代码如代码2所示。

1: CREATE TABLE dbo.DemoX
2:     (
3:       [key] INT PRIMARY KEY ,
4:       [value] INT,
5:     );
6: GO
7: INSERT  INTO dbo.DemoX
8:         ( [key], value )
9: VALUES  ( 1, 100 );
10: GO

代码2.创建测试DEMO

 

    接下来,对该DemoX表进行Select操作,并查看锁。如代码3所示。

1: BEGIN TRAN
2: SELECT  [key],value
3: FROM    dbo.DemoX D WITH (XLOCK);
4:
5: SELECT  L.resource_type,
6:         L.request_mode,
7:         L.request_status,
8:         L.resource_description,
9:         L.resource_associated_entity_id
10: FROM    sys.dm_tran_current_transaction T
11: JOIN    sys.dm_tran_locks L
12:         ON  L.request_owner_id = T.transaction_id;

代码3.使用X锁提示查语句

 

    在代码3中显式指定了X锁,并查看上锁情况,可以看出X锁以及对应父对象上的意向锁都正常存在,如图1所示。

图1.

 

      我们再开另外一个窗口运行一个普通的Select,结果如图2所示。

图2.

 

为什么没有阻塞

    理论上来说,第二个查询应该会被阻塞,因为第二个查询所需加的S锁和第一个查询的X锁不兼容。后来在网上找打StackOverFlow的一篇博文:“”,找到了答案。

    在SQL Server中,默认的已提交读为了保证不读脏数据(既在内存中修改,还未落盘的数据),会对需要查找的数据上S锁,但如果发现数据并不是脏数据,则会优化跳过加S锁的步骤,代码3中的查询语句强制使用了X锁提示,但未进行任何数据修改,所以不存在脏数据,因此后续查询就通过优化放弃使用S锁,从而不阻塞,导致了意料之外的结果。

 

解决办法

   SQL Server对于该特性的优化仅仅对行锁生效,如果在指定查询时使用页锁提示,则会按照语句,对阻塞后续查询,代码如代码4所示。

1: SELECT  [key],value
2: FROM    dbo.DemoX D WITH (PAGLOCK,XLOCK);

代码4.

    但显而易见,该方法会降低并发,如果有可能,请不要对Select操作使用X锁提示,否则请加上页锁提示。

    另一个办法是使用CTE进行表更新,将代码1中的代码两部分合二为一,数据在更新时会导致脏数据,因此不会出现跳过S锁的情况。

转载于:https://www.cnblogs.com/CareySon/p/4226577.html

你可能感兴趣的文章
操作系统实验报告-系统调用
查看>>
UltraISO制作linux启动盘(包含写入不完整解决方法)
查看>>
【Xamarin报错】visual studio android 模拟器部署卡住
查看>>
Js事件对象EventUtil
查看>>
|Vijos|图论最短路|P1082 丛林冒险
查看>>
iOS开发——获取本设备IP
查看>>
20145239杜文超 《Java程序设计》第7周学习总结
查看>>
冒泡排序(C实现)
查看>>
Swift -> RunTime(动态性) 问题 浅析
查看>>
<虚树+树型DP> SDOI2011消耗战
查看>>
算法啊算法
查看>>
安装完Anaconda python 3.7,想使用python3.6方法
查看>>
有意公司可联系我,我会提交我的简历!
查看>>
组播协议——IGMP v2报文头介绍
查看>>
微信小程序之----问题
查看>>
thinkphp整合Ueditor编辑器
查看>>
小程序
查看>>
oracle汇编03
查看>>
UITextInputMode
查看>>
hdu 3790 最短路径问题
查看>>