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

本文共 2406 字,大约阅读时间需要 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锁的情况。

转载地址:http://hnbdx.baihongyu.com/

你可能感兴趣的文章
在Tomcat上配置Proxool的DataSource(Jndi)
查看>>
CI框架代码体会
查看>>
iOS-设备唯一标识符
查看>>
网络管理员的应用监控工作
查看>>
618商战大片谢幕,销量冠军竟然有两个?
查看>>
【从中国到美国轻松跨越】—万网美国硅谷机房上线啦,低至78元/月!
查看>>
30 行 Javascript 代码搞定智能家居系统
查看>>
我的友情链接
查看>>
Windows 命令
查看>>
ROS下的PPTP配置
查看>>
Discord 公司如何使用 Cassandra 存储上亿条线上数据
查看>>
让nginx支持shtml
查看>>
CentOS6.5菜鸟之旅:U盘安装CentOS64位
查看>>
我的友情链接
查看>>
Twitter Storm 使用maven构建storm项目
查看>>
百度云推送的一些注意事项
查看>>
我的友情链接
查看>>
CNVD-2018-19126 EmpireCMS后台任意代码执行可GetShell
查看>>
debian安装ELK日志服务器配置apt镜像源
查看>>
线程池
查看>>