時間流逝比較快,博主也在馬不停蹄學習SQL Server,下班回來再晚也不忘記更新下博客,時間擠擠總會有的,如今的努力求的是將來所謂的安穩,每學一門爲的是深度而不是廣度,求的是知識自成體系而不是零散,廢話很少說本節咱們來說講SQL Server基礎系列最後幾節內容,這話博主說了n次,呵呵。html
NOLOCK和READPAST
NOLOCK
隨便翻翻博客園對於各類鎖的介紹真的是一個字【多】,僅僅介紹其概念,再要麼就是轉載其概念,不知道那些轉載概念的園友是否已經弄懂了,稍微發下感慨。NOLOCK在概念上相似於READ UNCOMMITTED隔離級別,而且只針對於SELECT查詢語句,它不會獲取表的共享鎖,換句話說不會阻止排它鎖來更新數據行。當咱們對錶進行NOLOCK有什麼好處呢?它可以提升併發性能,由於此時SQL Server數據庫引擎沒必要去維護共享鎖,因爲不會對正在讀取的表獲取共享鎖,因此可能致使未提交的事務也會被讀取,因此此時缺點顯而易見將致使髒讀,至於髒讀是何含義則無需我再多講。咱們重點的明白什麼狀況下應該用NOLOCK。咱們看下實際例子來理解NOLOCK,創建測試表並插入300條測試數據:程序員
IF OBJECT_ID('Example')>0 DROP TABLE Example; GO CREATE TABLE [dbo].[Example] ( [SaleID] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY, [Product] [char](150) NULL, [SaleDate] [datetime] NULL, [SalePrice] [money] NULL ) GO DECLARE @i SMALLINT SET @i = 1 WHILE (@i <=100) BEGIN INSERT INTO Example (Product, SaleDate, SalePrice) VALUES ('Computer', DATEADD(mm, @i, '3/11/1919'), DATEPART(ms, GETDATE()) + (@i + 57)) INSERT INTO Example (Product, SaleDate, SalePrice) VALUES ('BigScreen', DATEADD(mm, @i, '3/11/1927'), DATEPART(ms, GETDATE()) + (@i + 13)) INSERT INTO Example (Product, SaleDate, SalePrice) VALUES ('PoolTable', DATEADD(mm, @i, '3/11/1908'), DATEPART(ms, GETDATE()) + (@i + 29)) SET @i = @i + 1 END GO
此時咱們再來插入一條測試數據:sql
BEGIN TRANSACTION INSERT INTO Example (Product, SaleDate, SalePrice) VALUES ('PoolTable', GETDATE(), 500)
此時咱們保持該事務窗口打開,因此此時在表中仍然會記錄着對其所發出的鎖,接下來咱們在另一個窗口查詢表中數據總行數並使用NOLOCK提示。數據庫
SELECT COUNT(*) FROM Example WITH(NOLOCK)
此時顯示數據總函數爲301,由於上述插入語句的事務進入到了表中只是並未提交而已,此時咱們不想插入那條數據進行撤銷即回滾併發
BEGIN TRANSACTION INSERT INTO Example (Product, SaleDate, SalePrice) VALUES ('PoolTable', GETDATE(), 500) ROLLBACK TRANSACTION
此時咱們回滾了以前插入的數據,咱們再來利用NOLOCK提示來查詢數據總函數。函數
此時返回的爲實際總數據行,而我麼第一次查詢的數據並未提交這就是典型的-髒讀。高併發
READPAST
READPAST表提示相信不少童鞋用的比較少,可是實際上其做用很是大,當在表中用READPAST指定提示時此時SQL Server數據庫引擎在返回結果集時將不會返回鎖定的行或者數據頁。它除了和NOLOCK同樣不會致使查詢阻塞外,由於不會返回鎖定的行記錄因此其優勢好包括不存在髒讀。可是其缺點則是由於不包含鎖定的行記錄可是很難保證結果集或者修改語句是否包含咱們所必須須要返回的行。有可能在咱們的業務邏輯中,須要返回咱們必須須要的行。它的使用方式和NOLOCK同樣,下面咱們來看下實際例子,更新測試表中的SalePrice列,以下:post
BEGIN TRANSACTION UPDATE TOP(1) Example SET SalePrice = SalePrice + 1
因爲咱們並未提交或者回滾事務因此此時更新的數據行已經被影響,下面咱們利用READPAST提示來查詢表中總數據行。性能
SELECT COUNT(*) FROM Example WITH(READPAST)
在咱們的測試表中數據行爲300條,同時咱們進行了上述更新,當咱們利用READPAST提示進行查詢總數據行時,由於更新而未提交或者回滾致使此時有一行記錄被排它鎖鎖住,而READPAST的做用則是跳過鎖住的行,因此此時很明顯只返回299條數據,以下:學習
經過上述圖顯示因爲更新數據行被鎖定,因此此時利用READPAST來查詢總數據行時致使更新數據行將被忽略。
UPDLOCK和HOLDLOCK
UPDLOCK
怎麼會出現一個更新鎖的呢,原來咱們對於查詢和更新死鎖說到了排它鎖,這個排它鎖和更新鎖不是同樣的麼,此言差矣,容我娓娓道來,這個UPDLOCK只是針對於表中的某一行記錄來鎖定從而阻止其餘操做對該行的數據更新,說到這裏想必咱們已經明瞭,UPDLOCK是行級別,而排它鎖則是表級別,兩者不可同日而語。也就說當咱們對某一行添加UPDLOCK提示時並不會阻塞其餘查詢操做,下面咱們來看看,咱們打開一個窗口來更新測試表中篩選條件爲SaleID等於1的記錄並用UPDLOCK鎖住。
BEGIN TRAN select * from Example WITH (UPDLOCK) where SaleID = 1
此時咱們再來開一個窗口進行查詢,以下:
select * from Example
此時咱們將看到可以查詢出全部數據,以下:
HOLDLOCK
這個又是什麼玩意了,根據詞達意翻譯爲厚住鎖【哈哈】,這個翻譯雖然有點勉強,可是很是明確的表達了其意思,有點強制性的意味,當咱們使用HOLDLOCK提示時,此時查詢將鎖定表且被強制序列化,直到事務完成,纔會被釋放,其相似於SERIALIZABLE最高隔離級別。咱們結合上述例子來看下,當咱們對錶進行HOLDLOCK後再進行查詢
BEGIN TRAN select * from Example WITH (UPDLOCK,HOLDLOCK) where SaleID = 1
此時咱們再來運行查詢
select * from Example
什麼狀況仍是能查詢出數據,不知道看到本文的你是否心生疑竇,咱們並未提交事務並用UPDLOCK和HOLDLOCK提示此時再查詢時應該會出現阻塞,由於此時已有排它鎖的存在。咱們先擱置疑問,在咱們建立測試表時毫無疑問會對主鍵建立彙集索引,此時咱們刪除彙集索引試試。
此時咱們從新運行上述語句,此時將致使查詢阻塞,以下:
咱們簡短的解釋一下,若是咱們對錶創建了彙集索引或非彙集索引此時排它鎖將消失代替的則是RangeS-U鎖,因此當咱們未添加彙集索引排它鎖則存在致使查詢阻塞,有關RangeS-S,RangeS-U,RangeX-X,RangeI-N咱們將深刻研究。因此上述因爲致使了查詢阻塞,咱們結合本節所學內容,咱們利用NOLOCK來查詢數據。
select * from Example WITH(NOLOCK)
此時毫無疑問將可以查詢出數據,以下:
固然除非咱們意識到NOLOCK致使髒讀的問題,不然謹慎用。
實戰拓展
關於NOLOCK和UPDLOCK以及HOLDLOCK則沒有什麼可講的,咱們來說講UPDLOCK和READPAST,經過UPDLOCK和READPAST的結合咱們可以解決許多問題,好比我當前項目中對於更新預定人數,則用到了UPDLOCK和READPAST,由於考慮到併發若是固定預定人數爲100,那麼當出現併發時將有可能致使預定超出的狀況,利用UPDLOCK則能夠解決其餘進程過來時對其進行修改的狀況,同時結合READPAST解決髒讀,同時不會阻塞,當有請求過來時咱們直接利用表變量對預定人數進行更新,若更新失敗咱們再進行回滾,算是一個解決方案。同時利用UPDLOCK和READPAST還能夠解決其餘問題,好比,當有多個併發時咱們要根據篩選條件獲取第一值,也就是說第二個請求過來時獲取到的值是下一個,那麼這樣的問題該如何處理呢,若咱們只是簡單進行處理,那麼第二個請求同時過來時可能也會讀取到以前讀取的那個值,基於此場景,咱們能夠利用UPDLOCK和READPAST來解決。咱們看以下代碼就能夠理解。
DECLARE @Next INTEGER BEGIN TRANSACTION -- 找到下一個知足條件的值 SELECT TOP 1 @Next = Id FROM Test WITH (UPDLOCK, READPAST) WHERE Flag = 0 ORDER BY Id ASC --若找到利用標識更新,防止下一次被讀取到 IF (@Next IS NOT NULL) BEGIN UPDATE Test SET Flag = 1 WHERE Id = @Next END COMMIT TRANSACTION -- 返回咱們查詢到的值 IF (@Next IS NOT NULL) SELECT * FROM Test WHERE Id = @Next
固然上述能夠避免阻塞,咱們也能夠在阻塞的狀況下來處理利用ROWLOCK和HOLDLOCK來解決
BEGIN TRAN SELECT FROM Test WITH (HOLDLOCK, ROWLOCK) WHERE Id = 1 --TODO COMMIT TRAN
爲了加深記憶,又轉發一篇博客,以下:
轉發至:http://www.javashuo.com/article/p-yjjwqmmo-t.html
前段時間**公司DBA來咱們這培訓。講了一大堆MYSQL的優化。 QA環節一程序員問「SQL語句中的 with nolock 除了不鎖表外,是否能讀其餘鎖住的數據"。
講課的人嘟嘟了半天沒解釋清楚(有多是MYSQL裏沒有這個機制),公司的另外一程序員給出了一個很簡潔明瞭的回答:
WITH NOLOCK 除了自己不鎖表(不加任何鎖) 也不會受其餘的已存在的鎖影響, 鎖住的行數據也照樣讀,
我的認爲這句話說得很清楚明瞭,一句話就能說明白的事,不過好奇怪的是程序員常常用這個語句居然也不去試一下。 這裏順便總結一下 其餘的 SQLSERVER 中的with鎖級別:
WITH NOLOCK:無鎖
WITH HOLDLOCK:掛一個保持鎖
WITH UPDLOCK:掛一個更新鎖
WITH XLOCK:掛一個排他鎖
須要注意的是 with nolock 是不能用於update,delete insert 這種更新語句的,說繞了。簡單的說 with nolock 只能用於select。
例如:update dbo.test with(NOLOCK) set username='wokofo' --這樣的語句是錯誤的
彈回:INSERT、UPDATE、DELETE 或 MERGE 語句的目標表不容許使用 NOLOCK 和 READUNCOMMITTED 鎖提示。
實際使用:
selecttop10*from dbo.test with(NOLOCK) selecttop10*from dbo.test with(HOLDLOCK) selecttop10*from dbo.test with(XLOCK) selecttop10*from dbo.test with(UPDLOCK) update dbo.test with(HOLDLOCK) set username='wokofo' update dbo.test with(XLOCK) set username='wokofo' update dbo.test with(UPDLOCK) set username='wokofo'
NOLOCK(不加鎖)
此選項被選中時,SQL Server 在讀取或修改數據時不加任何鎖。 在這種狀況下, 用戶有可能讀取到未完成事務(Uncommited Transaction)或回滾(Roll Back)中的數據, 即所謂的「髒數據」。
HOLDLOCK(保持鎖)
此選項被選中時,SQL Server 會將此共享鎖保持至整個事務結束,而不會在途中釋放。
UPDLOCK(修改鎖)
此選項被選中時,SQL Server 在讀取數據時使用修改鎖來代替共享鎖, 並將此鎖保持至整個事務或命令結束。使用此選項可以保證多個進程能同時讀取數據但只有該進程能修改數據。
TABLOCK(表鎖)
此選項被選中時,SQL Server 將在整個表上置共享鎖直至該命令結束。 這個選項保證其餘進程只能讀取而不能修改數據。
PAGLOCK(頁鎖)
此選項爲默認選項, 當被選中時,SQL Server 使用共享頁鎖。
TABLOCKX(排它表鎖)
此選項被選中時,SQL Server 將在整個表上置排它鎖直至該命令或事務結束。這將防止其餘進程讀取或修改表中的數據。
HOLDLOCK 持有共享鎖,直到整個事務完成,應該在被鎖對象不須要時當即釋放,等於SERIALIZABLE事務隔離級別
NOLOCK 語句執行時不發出共享鎖,容許髒讀 ,等於 READ UNCOMMITTED事務隔離級別
PAGLOCK 在使用一個表鎖的地方用多個頁鎖
READPAST 讓sql server跳過任何鎖定行,執行事務,適用於READ UNCOMMITTED事務隔離級別只跳過RID鎖,不跳過頁,區域和表鎖
ROWLOCK 強制使用行鎖
TABLOCKX 強制使用獨佔表級鎖,這個鎖在事務期間阻止任何其餘事務使用這個表
UPLOCK 強制在讀表時使用更新而不用共享鎖
注意: 鎖定數據庫的一個表的區別
SELECT * FROM table WITH (HOLDLOCK) 其餘事務能夠讀取表,但不能更新刪除
SELECT * FROM table WITH (TABLOCKX) 其餘事務不能讀取表,更新和刪
總結
本節咱們講述了博主比較疑惑的幾種鎖例如READPAST,以前未接觸過,項目中在老大的指導下才知道,原本打算今天結束SQL Server基礎系列,誰知中途學習時遇到了其餘問題,好比還有其餘四種鎖類型,我還得再研究研究,真的是SQL Server基礎系列最後一篇,真的不騙你,同時.NET Core也會不定時更新,歡迎你們繼續關注博客