SQL查詢優化,nolock問題

在作過的不少項目中,發現你們無論對什麼表,逢select一定加上nolock(或者with(nolock)),好像已是制度化的一種東西。有領導高人解釋說加上nolock能夠提升查詢速度,不影響對數據表的其餘併發操做。  
可是真有必要每一個查詢都加nolock嗎?我的認爲加不加nolock仍是值得咱們根據實際狀況斟酌一番的(至少須要知其然而後知其因此然吧)。下面就來簡單分析一下加不加nolock以及加了nolock對實際查詢的一些影響。
1、重要概念
(此處沉思5秒,安靜回想經典數據庫教科書裏的一些重用概念。嗯......什麼,你也想不全了?那好吧,別閒煩,道理是要講的,書是不得不參考的(bs直接抄書的))
併發訪問:同一時間有多個用戶訪問同一資源。若是併發用戶中有其餘用戶同時對資源進行了修改,這樣對同一數據的訪問就會出現「所見不是所得」的狀況,從而對其它用戶產生某些不利的影響,包括:
1:髒讀:有一個用戶對某一個資源作了修改,此時另一個用戶正好讀取了這條被修改的記錄,而後第一個用戶又放棄了修改,數據還原到修改以前,這兩個不一樣的結果就是髒讀。程序員

2:幻讀:特指用戶讀取一批記錄的狀況。用戶兩次查詢同一條件的一批記錄,第一次查詢後,有其它用戶對這批數據作了修改,方法多是insert,update或delete,第二次查詢時,用戶會發現第一次查詢的記錄條目有的不在第二次查詢結果中,或者是第二次查詢的條目不在第一次查詢的內容中,形成先後查詢結果的不一致。
3:不可重複讀:系統中某一個用戶的一個操做是一個事務,這個事務分兩次讀取同一條記錄。若是第一次讀取後,正好有另一個用戶修改了這條記錄,而後第二次讀取的正好是以前進行修改記錄的那位用戶的數據,這樣就有可能形成兩次讀取的數據不一樣。固然若是咱們在事務中鎖定這條記錄就能夠避免。sql


2、如何消除併發訪問的不利影響
如前所述,既然併發訪問會形成這麼多不利影響,咱們又該如何解決呢?估計通常程序員的下意識反應就是像咱們在控制多線程併發編程的時候同樣,加鎖,lock一下,over。沒錯,還真不能說你說的不對!真是聰明又幸福的程序員啊!
其實在ms的Sql Server中,有兩種併發訪問的控制機制:鎖和行版本控制,關於併發控制,ms的闡述和解決方案真是詳細而周到。你不得不pf咱們的ms是多麼的親媽啊,真的什麼都幫咱們想好而且作好了。
先分析一下數據庫的鎖。
小抄一段參考書上的:數據庫

一、鎖:「每一個事務對所依賴的資源會請求不一樣類型的鎖,它能夠阻止其餘事務以某種可能會致使事務請求鎖出錯的方式修改資源。當事務再也不依賴鎖定的資源時,鎖將被釋放」。 從數據庫系統的角度來看:咱們能夠把鎖分爲共享鎖、獨佔鎖(排它鎖)和更新鎖:
(1)、共享 (S) :用於不更改或不更新數據的操做(只讀操做),好比咱們常見的select語句等。
(2)、更新 (U) :用於可更新的資源中。防止當多個會話在讀取、鎖定以及隨後可能進行的資源更新時發生常見形式的死鎖。
(3)、排它 (X) :用於數據修改操做,例如insert、update或delete。確保不會同時對同一資源進行多重更新。編程

對於如此看似簡單其實重要繁瑣的東西,固然不能讓龐大的程序員羣體去設置或控制它們。Sql Server經過設置事務的隔離級別自動管理鎖的設置和控制。鎖管理器經過查詢分析器分析待執行的sql語句,進而來判斷這些sql語句將會訪問哪些資源,進行什麼操做,而後結合設定的隔離級別自動分配管理須要用到的鎖。安全

下面接着來了解一下行版本控制。
二、行版本控制:
還用想嗎?小抄一下:
(1)、簡介
「 行版本控制的隔離是Sql Server 2005一個新的隔離框架。使用行版本控制的隔離能夠在大量併發的狀況下,顯著減小所得產生,而且與nolock相比,它又能夠顯著下降骯髒讀,幻影,丟失更新等現象的發生(READ_COMMITTED_SNAPSHOT)。當在基於行版本控制的隔離下運行的事務讀取數據時,讀取操做不會獲取正被讀取的數據上的共享鎖(S 鎖),所以不會阻塞正在修改數據的事務。另外,鎖定資源的開銷隨着所獲取的鎖的數量的減小降至最低。使用行版本控制的已提交讀隔離和快照隔離能夠提供副本數據的語句級或事務級讀取一致性」。
(2)、原理
「Sql Server 2005的行版本控制原理上很簡單(不說不知道,筆者注),就是在庫表中每一行的記錄上都悄悄的增長了一個類時間戳列(行版本列)。當使用行版本控制的隔離時,Sql Server 2005 Database Engine 向使用行版本控制操做數據的每一個事務分配一個事務序列號 (XSN)。事務在執行 BEGIN TRANSACTION 語句時啓動。可是,事務序列號在執行 BEGIN TRANSACTION 語句後的第一次讀/寫操做時開始增長。事務序列號在每次分配時都增長1。當事務執行時,Sql Server根據行版本列,來提供的行的相應版本。而Sql Server將維護全部在數據庫中執行的數據修改的邏輯副本(版本)。特定的事務每次修改行時,數據庫引擎 實例都存儲之前提交的 tempdb 中行的圖像版本。每一個版本都標記有進行此更改的事務的事務序列號。已修改行的版本使用連接列表連接在一塊兒。最新的行值始終存儲在當前的數據庫中並連接至版本存儲區 tempdb 中存儲的版本。(修改大型對象 (LOB) 時,只有已更改的片斷纔會複製到 tempdb 中的版本存儲區,  對於短時間運行的事務,已修改行的版本將可能保存在緩衝池中,而不會寫入 tempdb 數據庫的磁盤文件中。若是隻是臨時須要副本行,它將只是簡單地從緩衝池中刪除而不會引起 I/O 開銷。)」
(3)、優點
使用行版本控制的隔離級別具備如下優勢:
  a、讀取操做檢索一致的數據庫快照;
  b、select語句在讀取操做過程當中不鎖定數據(讀取器不阻塞編寫器,編寫器也不阻塞讀取器);
  c、select語句能夠在其餘事務更新行時訪問最後提交的行值,而不阻塞應用程序;
  d、死鎖的數量減小;
  e、事務所需的鎖的數量減小,這減小了管理鎖所需的系統開銷;
       f、鎖升級的次數減小。
(4)、行版本控制小結:
當啓用了基於行版本控制的隔離級別時,數據庫引擎將維護修改的每一行的版本。應用程序能夠指定事務使用行版本查看事務或查詢開始時存在的數據,而不是使用鎖保護全部讀取。經過使用行版本控制,讀取操做阻止其餘事務的可能性將大大下降,也就是至關於針對全部的表在查詢時都會加上nolock。雖然一樣會產生髒讀的現象,但差異在於咱們不用每次查詢都加上nolock,行版本控制策略默認的一個設置就幫咱們搞定了。
BTW,既然說到了基於行版本控制的隔離級別,不得不說下隔離級別。隔離級別,怎麼說呢?您別不懷好意地笑,抄書ing:
<1>、用處:控制鎖的應用,即什麼場景應用什麼樣的鎖機制,解決併發處理帶來的種種問題;;
<2>、分類:多線程

  a、未提交讀(UnCommitted Read):悲觀,至關於(nolock;隔離事務的最低級別,只能保證不讀取物理上損壞的數據。
b、已提交讀(Read Committed):悲觀,數據庫引擎的缺省模式,讀操做共享鎖時間一直到讀取結束。
c、可重複讀(Repeatable Read):悲觀,讀操做共享鎖時間比已提交讀模式更長,一直到事務結束。
d、可序列化(Serializable):悲觀,至關於(HoldLock),最嚴謹。
e、已提交讀快照(Read Committed Snapshot):樂觀,2005新增,基於行版本控制,全部讀操做不受其餘鎖的影響,歷史數據保存更短,Temp空間更少,支持分佈式。
Alter Database 數據庫名稱 Set Read_Committed_Snapshot On
f、快照(Snapshot):樂觀,2005新增,基於行版本控制,全部讀操做不受其餘鎖的影響,歷史數據保存更長,Temp空間更多,不支持分佈式。
Alter Database 數據庫名稱 Set Allow_Snapshot_Isolation On併發

 <3>、查看當前隔離模式和行版本控制狀態 (2005)框架


DBCC UserOptions
Select name, snapshot_isolation_state, snapshot_isolation_state_desc, is_read_committed_snapshot_on From sys.databases分佈式

 三、小結
根據前面的分析,咱們知道,Sql Server 2005控制併發訪問已經有了兩種有效的途徑;nolock語句執行時不發出共享鎖,容許髒讀 ,等於READ UNCOMMITTED事務隔離級別,從這種意義上來說,nolock確實在查詢的時候能提升速度。但如今咱們再來問一下本身,nolock須要加嗎,不須要加嗎?真的須要加嗎,真的不須要加嗎??您能再確定點回答嗎?性能

3、nolock的適用場景(下面的幾點徹底是我的意見,能夠54。)
一、「持久化」的表:也就是數據不會常常變更的表,好比咱們熟知的省、市、縣和航空公司、機場等等。它們的共同特徵就是至少從目前來看,這些數據長時間不會有任何改變。其實從長遠來看,甚至一個很是成熟的公司的部門表也能夠做爲這類數據來處理,可是和部門有關係的員工表就不能夠;
二、容許髒讀的一些業務邏輯:這個沒什麼好說的,客戶需求決定了你不在這上面「較真」。好比咱們要查詢某個業務部門某一個季度或某一年的業績統計,須要瞭解大概狀況就能夠了。這種情形下,查詢nolock多少次都無所謂。
三、存儲了海量數據的表:這個毫無疑問,數據量大,重要性越強,訪問也就越多,併發操做影響到的記錄也就可能越大,所謂「樹大招風」,不過如此。咱們給查詢加上nolock能夠大大提高性能和用戶體驗,固然,它是以犧牲數據一致性和安全性來提高性能的。
最後,經過以上分析,咱們得出結論,查詢(尤爲是海量數據)不加鎖,毫無疑問,速度確確實實是提升了,可是咱們更應該有選擇性的挑選最適合的表來使用nolock。由於咱們已經都知道,「對數據表的併發操做」極可能形成一些查詢結果的困擾,好比咱們所熟知的「髒讀「。設想一下吧,對於沒有預期的一些查詢(所謂」預期查詢「,就是使用者認爲先後查詢結果不一致也是合理的,好比訂單查詢中一個訂單的訂單狀態的變化致使先後結果不一致等等),由於」髒讀「形成的」髒數據「先後查詢結果不一致,一次兩次也就罷了,可能使用的人覺得本身眼花了仍是怎麼的。可是若是屢次或者大數據量地出現數據不匹配,確定會讓不明因此的使用者困惑,心理素質好的會習慣性地把問題推給系統,內心素質很差的的還覺得本身誤操做仍是怎麼的,直接形成恐慌甚至懷疑本身rp。

主要示例:  SELECT  * FROM  t_BOS220000001 with(nolock)(有關鍵字with和沒有關鍵字with都沒有關係)nolock關鍵字緊跟着表名後面,  SELECT  * FROM  t_BOS220000001 (nolock)這樣也能夠或是  update table(nolock)set a='''' where id=1

 

本文轉載自:http://youzhangcai.blog.163.com/blog/static/1668481842010111782534757/

相關文章
相關標籤/搜索