概念介紹sql
開發人員喜歡在SQL腳本中使用WITH(NOLOCK), WITH(NOLOCK)實際上是表提示(table_hint)中的一種。它等同於 READUNCOMMITTED 。 具體的功能做用以下所示(摘自MSDN):數據庫
1: 指定容許髒讀。不發佈共享鎖來阻止其餘事務修改當前事務讀取的數據,其餘事務設置的排他鎖不會阻礙當前事務讀取鎖定數據。容許髒讀可能產生較多的併發操做,但其代價是讀取之後會被其餘事務回滾的數據修改。這可能會使您的事務出錯,向用戶顯示從未提交過的數據,或者致使用戶兩次看到記錄(或根本看不到記錄)。有關髒讀、不可重複讀和幻讀的詳細信息,請參閱併發影響。服務器
2: READUNCOMMITTED 和 NOLOCK 提示僅適用於數據鎖。全部查詢(包括那些帶有 READUNCOMMITTED 和 NOLOCK 提示的查詢)都會在編譯和執行過程當中獲取 Sch-S(架構穩定性)鎖。所以,當併發事務持有表的 Sch-M(架構修改)鎖時,將阻塞查詢。例如,數據定義語言 (DDL) 操做在修改表的架構信息以前獲取 Sch-M 鎖。全部併發查詢(包括那些使用 READUNCOMMITTED 或 NOLOCK 提示運行的查詢)都會在嘗試獲取 Sch-S 鎖時被阻塞。相反,持有 Sch-S 鎖的查詢將阻塞嘗試獲取 Sch-M 鎖的併發事務。有關鎖行爲的詳細信息,請參閱鎖兼容性(數據庫引擎)。session
3: 不能爲經過插入、更新或刪除操做修改過的表指定 READUNCOMMITTED 和 NOLOCK。SQL Server 查詢優化器忽略 FROM 子句中應用於 UPDATE 或 DELETE 語句的目標表的 READUNCOMMITTED 和 NOLOCK 提示。架構
功能與缺陷併發
使用WIHT(NOLOCK)有利也有弊,因此在決定使用以前,你必定須要瞭解清楚WITH(NOLOCK)的功能和缺陷,看其是否適合你的業務需求,不要以爲它能提高性能,稀裏糊塗的就使用它。app
1:使用WITH(NOLOCK)時查詢不受其它排他鎖阻塞高併發
打開會話窗口1,執行下面腳本,不提交也不回滾事務,模擬事務真在執行過程中post
BEGIN TRAN
UPDATE TEST SET NAME='Timmy' WHERE OBJECT_ID =1;
--ROLLBACK
打開會話窗口2,執行下面腳本,你會發現執行結果一直查詢不出來(其實才兩條記錄)。當前會話被阻塞了性能
SELECT * FROM TEST;
打開會話窗口3,執行下面腳本,查看阻塞狀況,你會發如今會話2被會話1給阻塞了,會話2的等待類型爲LCK_M_S:「當某任務正在等待獲取共享鎖時出現」
SELECT wt.blocking_session_id AS BlockingSessesionId
,sp.program_name AS ProgramName
,COALESCE(sp.LOGINAME, sp.nt_username) AS HostName
,ec1.client_net_address AS ClientIpAddress
,db.name AS DatabaseName
,wt.wait_type AS WaitType
,ec1.connect_time AS BlockingStartTime
,wt.WAIT_DURATION_MS/1000 AS WaitDuration
,ec1.session_id AS BlockedSessionId
,h1.TEXT AS BlockedSQLText
,h2.TEXT AS BlockingSQLText
FROM sys.dm_tran_locks AS tl
INNER JOIN sys.databases db
ON db.database_id = tl.resource_database_id
INNER JOIN sys.dm_os_waiting_tasks AS wt
ON tl.lock_owner_address = wt.resource_address
INNER JOIN sys.dm_exec_connections ec1
ON ec1.session_id = tl.request_session_id
INNER JOIN sys.dm_exec_connections ec2
ON ec2.session_id = wt.blocking_session_id
LEFT OUTER JOIN master.dbo.sysprocesses sp
ON SP.spid = wt.blocking_session_id
CROSS APPLY sys.dm_exec_sql_text(ec1.most_recent_sql_handle) AS h1
CROSS APPLY sys.dm_exec_sql_text(ec2.most_recent_sql_handle) AS h2
此時查看會話1(會話1的會話ID爲53,執行腳本1前,能夠用SELECT @@spid查看會話ID)的鎖信息狀況,你會發現表TEST(ObjId=1893581784)持有的鎖信息以下所示
打開會話窗口4,執行下面腳本.你會發現查詢結果很快就出來,會話4並不會被會話1阻塞。
SELECT * FROM TEST WITH(NOLOCK)
從上面模擬的這個小例子能夠看出,正是因爲加上WITH(NOLOCK)提示後,會話1中事務設置的排他鎖不會阻礙當前事務讀取鎖定數據,因此會話4不會被阻塞,從而提高併發時查詢性能。
2:WITH(NOLOCK) 不發佈共享鎖來阻止其餘事務修改當前事務讀取的數據,這個就不舉例子了。
本質上WITH(NOLOCK)是經過減小鎖和不受排它鎖影響來減小阻塞,從而提升併發時的性能。所謂凡事有利也有弊,WITH(NOLOCK)在提高性能的同時,也會產生髒讀現象。
以下所示,表TEST有兩條記錄,我準備更新OBJECT_ID=1的記錄,此時事務既沒有提交也沒有回滾
BEGIN TRAN
UPDATE TEST SET NAME='Timmy' WHERE OBJECT_ID =1;
--ROLLBACK
此時另一個會話使用WITH(NOLOCK)查到的記錄爲未提交的記錄值
假如因爲某種緣由,該事務回滾了,那麼咱們讀取到的OBJECT_ID=1的記錄就是一條髒數據。
髒讀又稱無效數據的讀出,是指在數據庫訪問中,事務T1將某一值修改,而後事務T2讀取該值,此後T1由於某種緣由撤銷對該值的修改,這就致使了T2所讀取到的數據是無效的。
WITH(NOLOCK)使用場景
何時可使用WITH(NOLOCK)? 何時不能使用WITH(NOLOCK),這個要視你係統業務狀況,綜合考慮性能狀況與業務要求來決定是否使用WITH(NOLOCK), 例如涉及到金融或會計成本之類的系統,出現髒讀那是要產生嚴重問題的。關鍵業務系統也要慎重考慮。大致來講通常有下面一些場景可使用WITH(NOLOCK)
1: 基礎數據表,這些表的數據不多變動。
2:歷史數據表,這些表的數據不多變動。
3:業務容許髒讀狀況出現涉及的表。
4:數據量超大的表,出於性能考慮,而容許髒讀。
另一點就是不要濫用WITH(NOLOCK),我發現有個奇怪現象,不少開發知道WITH(NOLOCK),可是有不瞭解髒讀,習慣性的使用WITH(NOLOCK)。
WITH(NOLOCK)與 NOLOCK區別
爲了搞清楚WITH(NOLOCK)與NOLOCK的區別,我查了大量的資料,咱們先看看下面三個SQL語句有啥區別
SELECT * FROM TEST NOLOCK
SELECT * FROM TEST (NOLOCK);
SELECT * FROM TEST WITH(NOLOCK);
上面的問題歸納起來也就是說NOLOCK、(NOLOCK)、 WITH(NOLOCK)的區別:
1: NOLOCK這樣的寫法,其實NOLOCK其實只是別名的做用,而沒有任何實質做用。因此不要粗心將(NOLOCK)寫成NOLOCK
2:(NOLOCK)與WITH(NOLOCK)其實功能上是同樣的。(NOLOCK)只是WITH(NOLOCK)的別名,可是在SQL Server 2008及之後版本中,(NOLOCK)不推薦使用了,"不借助 WITH 關鍵字指定表提示」的寫法已通過時了。 具體參見MSDN http://msdn.microsoft.com/zh-cn/library/ms143729%28SQL.100%29.aspx
2.1 至於網上說WITH(NOLOCK)在SQL SERVER 2000不生效,我驗證後發現徹底是個謬論。
2.2 在使用連接服務器的SQL當中,(NOLOCK)不會生效,WITH(NOLOCK)纔會生效。以下所示
消息 4122,級別 16,狀態 1,第 1 行
Remote table-valued function calls are not allowed.
WITH(NOLOCK)會不會產生鎖
不少人誤覺得使用了WITH(NOLOCK)後,數據庫庫不會產生任何鎖。實質上,使用了WITH(NOLOCK)後,數據庫依然對該表對象生成Sch-S(架構穩定性)鎖以及DB類型的共享鎖, 以下所示,能夠在一個會話中查詢一個大表,而後在另一個會話中查看鎖信息(也可使用SQL Profile查看會話鎖信息)
不使用WTIH(NOLOCK)
使用WITH(NOLOCK)
從上能夠看出使用WITH(NOLOCK)後,數據庫並非不生成相關鎖。 對比能夠發現使用WITH(NOLOCK)後,數據庫只會生成DB類型的共享鎖、以及TAB類型的架構穩定性鎖.
另外,使用WITH(NOLOCK)並非說就不會被其它會話阻塞,依然可能會產生Schema Change Blocking
會話1:執行下面SQL語句,暫時不提交,模擬事務正在執行
BEGIN TRAN
ALTER TABLE TEST ADD Grade VARCHAR(10) ;
會話2:執行下面語句,你會發現會話被阻塞,截圖以下所示。
SELECT * FROM TEST WITH(NOLOCK)