关于X锁的问题--由select+X锁是否持有到事务结束的误区
前言:看了宋桑的文章《一次意外的X锁不阻塞问题》,结合本人的测试,说明一下我对select中使用X锁是否会持有到事务结束产生的误区;
详情不多说了,详见宋桑的《一次意外的X锁不阻塞问题》和《消失的共享锁》,对Select+X锁和Select+S锁的情况进行了解释。以下只描述我的测试;测试表结构及数据如下:
1 /****** Script for SelectTopNRows command from SSMS ******/ 2 CREATE TABLE [test_a].[dbo].[tmp_byxl_01](id INT IDENTITY,flag int) 3 INSERT INTO [test_a].[dbo].[tmp_byxl_01](flag) VALUES(null) 4 go 7 5 UPDATE [test_a].[dbo].[tmp_byxl_01] SET flag=id 6 7 SELECT TOP 1000 [id] 8 ,[flag] 9 FROM [test_a].[dbo].[tmp_byxl_01] 10 11 12 ------------------------------- 13 id flag 14 ----------- ---- 15 1 1 16 2 2 17 3 3 18 4 4 19 5 5 20 6 6 21 7 7 22 23 (7 行受影响)
由于案例出自系统续费问题,业务采用的是调用存储过程的方式实现,因此每一次调用时,都是select+X锁的方式;这和上述文章中提到的“Select+X锁和Select+S锁”的情况不太相同
先说我的误区
误区:select中指定的X锁将在查询结束后立即释放,并不持续到tran结束
测试代码如下:
--Session_A BEGIN TRANSELECT * FROM [test_a].[dbo].[tmp_byxl_01](xlock) WHERE flag=2 WAITFOR DELAY '00:00:10' COMMIT--Session_B BEGIN TRANSELECT * FROM [test_a].[dbo].[tmp_byxl_01](xlock) WHERE flag=2 COMMIT
由于两个tran都是申请xlock,在执行时,Session_A(spid=53)先执行,Session_B(spid=55)大约5秒后执行,通过SP_LOCK可以看到,spid=55申请X锁时被阻塞
从执行时间上看,spid(55)晚于spid(53)5秒左右开始,执行时间上基本吻合。
这个测试验证了上述的误区。加在Select上的X锁持续到了tran结尾,因此才能阻塞其他进程的相同查询(也是Xlock)如此长的时间;
对于此类情况,一般应用的场景如商家的充值系统、抢购系统等
需要将大并发的环境转化为单一进程持有锁的情况(select是为了进行判断,如账户起始金额不能为负、或查询当前商品信息以防出现超售的情况)
对于此类问题,个人认为,增加Xlock进行查询,是为了有效的避免脏读,尽管增加pagelock的方式可以避免S锁的优化问题,但可能导致锁范围过大。
如果不存在普通S锁的查询,不添加pagelock提升锁级别,也是可以满足大并发需求的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
2014-01-15 Replication的犄角旮旯(一)--变更订阅端表名的应用场景