天天看點

SQL Server 中WITH (NOLOCK)淺析

<b>概念介紹</b>

開發人員喜歡在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

3:  不能為通過插入、更新或删除操作修改過的表指定 readuncommitted 和 nolock。sql server 查詢優化器忽略

from 子句中應用于 update 或 delete 語句的目标表的 readuncommitted 和 nolock 提示。

<b></b> 

<b>功能與缺陷</b>

    使用wiht(nolock)有利也有弊,是以在決定使用之前,你一定需要了解清楚with(nolock)的功能和缺陷,看其是否适合你的業務需求,不要覺得它能提升性能,稀裡糊塗的就使用它。

    1:使用with(nolock)時查詢不受其它排他鎖阻塞

    打開會話視窗1,執行下面腳本,不送出也不復原事務,模拟事務真在執行過程當中

   打開會話視窗2,執行下面腳本,你會發現執行結果一直查詢不出來(其實才兩條記錄)。目前會話被阻塞了

    打開會話視窗3,執行下面腳本,檢視阻塞情況,你會發現在會話2被會話1給阻塞了,會話2的等待類型為lck_m_s:“當某任務正在等待擷取共享鎖時出現”

<a href="http://images.cnitblog.com/blog/73542/201408/301203565323293.png"></a>

SQL Server 中WITH (NOLOCK)淺析

此時檢視會話1(會話1的會話id為53,執行腳本1前,可以用select  @@spid檢視會話id)的鎖資訊情況,你會發現表test(objid=1893581784)持有的鎖資訊如下所示

SQL Server 中WITH (NOLOCK)淺析

打開會話視窗4,執行下面腳本.你會發現查詢結果很快就出來,會話4并不會被會話1阻塞。

    select * from test with(nolock)

從上面模拟的這個小例子可以看出,正是由于加上with(nolock)提示後,會話1中事務設定的排他鎖不會阻礙目前事務讀取鎖定資料,是以會話4不會被阻塞,進而提升并發時查詢性能。

2:with(nolock) 不釋出共享鎖來阻止其他事務修改目前事務讀取的資料,這個就不舉例子了。

本質上with(nolock)是通過減少鎖和不受排它鎖影響來減少阻塞,進而提高并發時的性能。所謂凡事有利也有弊,with(nolock)在提升性能的同時,也會産生髒讀現象。

如下所示,表test有兩條記錄,我準備更新object_id=1的記錄,此時事務既沒有送出也沒有復原

SQL Server 中WITH (NOLOCK)淺析

此時另外一個會話使用with(nolock)查到的記錄為未送出的記錄值

SQL Server 中WITH (NOLOCK)淺析

假如由于某種原因,該事務復原了,那麼我們讀取到的object_id=1的記錄就是一條髒資料。

<b>with(nolock)使用場景</b>

麼時候可以使用with(nolock)?

什麼時候不能使用with(nolock),這個要視你系統業務情況,綜合考慮性能情況與業務要求來決定是否使用with(nolock),

例如涉及到金融或會計成本之類的系統,出現髒讀那是要産生嚴重問題的。關鍵業務系統也要慎重考慮。大體來說一般有下面一些場景可以使用

with(nolock)

   1: 基礎資料表,這些表的資料很少變更。

   2:曆史資料表,這些表的資料很少變更。

   3:業務允許髒讀情況出現涉及的表。

   4:資料量超大的表,出于性能考慮,而允許髒讀。

另外一點就是不要濫用with(nolock),我發現有個奇怪現象,很多開發知道with(nolock),但是有不了解髒讀,習慣性的使用with(nolock)。

<b>with(nolock)與 nolock差別</b>

為了搞清楚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.1  至于網上說with(nolock)在sql server 2000不生效,我驗證後發現完全是個謬論。

    2.2  在使用連結伺服器的sql當中,(nolock)不會生效,with(nolock)才會生效。如下所示

SQL Server 中WITH (NOLOCK)淺析

    消息 4122,級别 16,狀态 1,第 1 行

    remote table-valued function calls are not allowed.

<b>with(nolock)會不會産生鎖</b>

很多人誤以為使用了with(nolock)後,資料庫庫不會産生任何鎖。實質上,使用了with(nolock)後,資料庫依然對該表對象生成sch-

s(架構穩定性)鎖以及db類型的共享鎖, 如下所示,可以在一個會話中查詢一個大表,然後在另外一個會話中檢視鎖資訊(也可以使用sql

profile檢視會話鎖資訊)

    不使用wtih(nolock)

SQL Server 中WITH (NOLOCK)淺析

  使用with(nolock)

SQL Server 中WITH (NOLOCK)淺析

  從上可以看出使用with(nolock)後,資料庫并不是不生成相關鎖。  對比可以發現使用with(nolock)後,資料庫隻會生成db類型的共享鎖、以及tab類型的架構穩定性鎖.

另外,使用with(nolock)并不是說就不會被其它會話阻塞,依然可能會産生schema change blocking

會話1:執行下面sql語句,暫時不送出,模拟事務正在執行

會話2:執行下面語句,你會發現會話被阻塞,截圖如下所示。

SQL Server 中WITH (NOLOCK)淺析