前言:看了宋桑的文章《一次意外的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提升鎖級別,也是可以滿足大並發需求的。
