一些人總當NOLOCK查詢提示是SQL Server里的加速器,因為它避免了大量的死鎖情景。在這篇文章里,我想向你展示下為什么NOLOCK查詢提示是個不好的想法。
臟讀(Dirty Reads)NOLOCK查詢提示一個最大的副作用就是在你的記錄集會出現所謂的臟讀這個事實。我們來看下面的代碼:
1 BEGIN TRANSACTION2 3 UPDATE Person.Person4 SET FirstName = 'James'5 WHERE LastName = 'Jones'
如你所見,我開始了一個新的事務,對AdventureWorks2012數據庫里的Person.Person表進行UPDATE語句?,F在當你嘗試同時在另一個會話里讀取這個記錄時,這個SELECT語句會阻塞——在這個情況下請求的共享鎖(S)會被已經授予的排它鎖(X)阻塞——寫阻塞讀操作。
1 SELECT2 FirstName,3 LastName4 FROM Person.Person5 WHERE LastName = 'Jones'6 GO
一些人現在回應用SQL Server里的加速器,使用NOLOCK查詢提示:
1 SELECT2 FirstName,3 LastName4 FROM Person.Person WITH (NOLOCK) -- It's a kind of magic...5 WHERE LastName = 'Jones'6 GO
如你所見死鎖情景馬上解決了,你從SQL Server返回記錄了——遺憾的是在你面前現在有臟讀(Dirty Read):你讀取了尚未提交的數據。假設現在用UPDATE語句的第1個事務回滾了:在這個情況下,你已讀取的數據在SQL Server里邏輯上是從未存在的。
因此NOLOCK查詢提示并不是在每個情景都是有用的。如果你想運行一個報表,或者你只想快速返回結果,記錄不用100%正確,這“可以是”個可取的(考慮下一天的平均銷售額)。當然,當你需要精確的結果,NOLOCK并不可取。當然也有一些特定場景即使NOLOCK也會阻塞。
讀提交快照隔離(Read Committed Snapshot Isolation (RCSI))有人給我提了為什么NOLOCK查詢提示基本是一個不方便的(no-go)的選擇:你不能在明顯的方式里切換你的數據庫查詢到讀提交快照隔離(Read Committed Snapshot Isolation (RCSI))。我從未想過這個情景,但沒錯是對的。讀提交快照隔離(Read Committed Snapshot Isolation (RCSI))是個數據庫選項。當你啟用它的時候,每個查詢都是在新的讀提交快照樂觀隔離級別里——只要在你的事務里不指定任何隔離級別。
對于特定的SQL語句使用NOLOCK查詢提示,你臨時修改隔離級別為未提交讀(Read Uncommitted)。因此SQL語句也不會從讀提交快照隔離受益,因為語句并沒有在默認的提交讀(Read Committed)隔離運行。這就對了!當你下次寫下神奇的……WITH(NOLOCK)……時,好好想下這個額外參數。
小結使用NOLOCK查詢提示運行每個查詢并不都有意義。一方面你會通過臟讀(Dirty Reads)返回不一致的數據。另一方面你不能從讀提交快照隔離(Read Committed Snapshot Isolation (RCSI))樂觀隔離級別里受益,因為你臨時修改了你SQL語句的默認隔離級別。
感謝關注!
新聞熱點
疑難解答