秒殺已經很不陌生了,秒殺對於我們程式員來說更多的是並髮帶來的思考,也許有天才考慮的是如何做秒殺器來橫掃“秒殺江湖”。前日應邀來最佳化秒殺的sql。
讓我們來看看這秒殺的預存程序。(部分代碼如下)
create PROCEDURE [dbo].[kill]
@userid nvarchar(64),
@killId int
AS
BEGIN
BEGIN TRAN
declare @storage int
--判斷庫存
select @storage=storage from killProduct with(tablockx) where killID=@killId
IF(@@rowcount = 0)
BEGIN
COMMIT TRAN
select '沒貨'
return
END
IF(@storage <= 0)
BEGIN
COMMIT TRAN
select '沒貨'
return
END
--繼續判定秒殺是否結束
....
--判斷userId是否已經參加過秒殺
....
初看有如下幾個問題:
1.if else 太多,影響效能,由於sql server本身不善於運算,一個if 分支的效能損耗比clr中的if高出許多,所以建議多多在clr中進行此等判定,同時建議類似分頁等,第一頁單獨寫sql,不要走這一的if(pageIndex=1)的分支。
2.TRAN 這東西上得太早了。幹嘛這麼猴急呢,也不來點前戲就直接上,太不懂情調了。怎麼說呢,事物的粒度一定要把握好,不要把太多資源鎖在事物裡,事物粒度太大,勢必影響到效能。
3.with(tablockx) 這個固然是有必要,因為秒殺自有的特點就是並發大,是個男人都知道,一定要挺住那麼幾秒鐘。問題就是你要lock整個表,還要獨享,那你必須要打敗其他人了,所以就意味著其他人都是犧牲品了(排它鎖,鎖住整個表,其他人連查詢其他行的資料都得排隊)。
4.沒事找事 如上兩個if 您不覺得第一個沒有存在的必要嗎?
好了,我也不得瑟了,下面貼一段如何hold住的代碼吧。(修改後的部分代碼)
--直接進行更新,通過子查詢確認使用者是否有參加過秒殺
--通過hold來確保此過程結束前沒有其他使用者更新此行,可讀,rowlock是共用鎖定
UPDATE killProduct WITH(ROWLOCK,HOLDLOCK)SET storage=storage-1,@storage=storage
WHERE killID=@killId AND storage>0 AND (SELECT COUNT(0) FROM KILLlIST(NOLOCK) WHERE USERID=@USERID)=0
IF(@storage>0)
BEGIN
--秒殺成功
END
ELSE
BEGIN
--失敗
END
holdlock,hold住此行資料(rowlock),直到整個事務結束。
此處可以將holdlock換成UPDLOCK來實現同樣的效果。
秒殺你hold住了嗎?您在處理這樣的並發時又是如何hold住的呢?請大家分享之!