sql查詢,nolock寫還是不寫,這是乙個問題

2022-01-12 01:00:06 字數 4991 閱讀 8557

在做過的很多專案中,發現大家不管對什麼表,逢select必定加上nolock(或者with(nolock)),好像已經是制度化的一種東西。有領導高人解釋說加上nolock可以提高查詢速度,不影響對資料表的其他併發操作。  

但是真有必要每個查詢都加nolock嗎?個人認為加不加nolock還是值得我們根據實際情況斟酌一番的(至少需要知其然然後知其所以然吧)。下面就來簡單分析一下加不加nolock以及加了nolock對實際查詢的一些影響。

一、重要概念

(此處沉思5秒,安靜回想經典資料庫教科書裡的一些重用概念。嗯......什麼,你也想不全了?那好吧,別閒煩,道理是要講的,書是不得不參考的(bs直接抄書的))

併發訪問:同一時間有多個使用者訪問同一資源。如果併發使用者中有其他使用者同時對資源進行了修改,這樣對同一資料的訪問就會出現「所見不是所得」的情況,從而對其它使用者產生某些不利的影響,包括:

1:髒讀:有乙個使用者對某乙個資源做了修改,此時另外乙個使用者正好讀取了這條被修改的記錄,然後第乙個使用者又放棄了修改,資料還原到修改之前,這兩個不同的結果就是髒讀。

2:幻讀:特指使用者讀取一批記錄的情況。使用者兩次查詢同一條件的一批記錄,第一次查詢後,有其它使用者對這批資料做了修改,方法可能是insert,update或delete,第二次查詢時,使用者會發現第一次查詢的記錄條目有的不在第二次查詢結果中,或者是第二次查詢的條目不在第一次查詢的內容中,造成前後查詢結果的不一致。

3:不可重複讀:系統中某乙個使用者的乙個操作是乙個事務,這個事務分兩次讀取同一條記錄。如果第一次讀取後,正好有另外乙個使用者修改了這條記錄,然後第二次讀取的正好是之前進行修改記錄的那位使用者的資料,這樣就有可能造成兩次讀取的資料不同。當然如果我們在事務中鎖定這條記錄就可以避免。

二、如何消除併發訪問的不利影響

如前所述,既然併發訪問會造成這麼多不利影響,我們又該如何解決呢?估計一般程式設計師的下意識反應就是像我們在控制多執行緒併發程式設計的時候一樣,加鎖,lock一下,over。沒錯,還真不能說你說的不對!真是聰明又幸福的程式設計師啊!

其實在ms的sql server中,有兩種併發訪問的控制機制:鎖和行版本控制,關於併發控制,ms的闡述和解決方案真是詳細而周到。你不得不pf我們的ms是多麼的親媽啊,真的什麼都幫我們想好並且做好了。

先分析一下資料庫的鎖。

小抄一段參考書上的:

1、鎖:「每個事務對所依賴的資源會請求不同型別的鎖,它可以阻止其他事務以某種可能會導致事務請求鎖出錯的方式修改資源。當事務不再依賴鎖定的資源時,鎖將被釋放」。 從資料庫系統的角度來看:我們可以把鎖分為共享鎖、獨佔鎖(排它鎖)和更新鎖:

(1)、共享 (s) :用於不更改或不更新資料的操作(唯讀操作),比如我們常見的select語句等。

(2)、更新 (u) :用於可更新的資源中。防止當多個會話在讀取、鎖定以及隨後可能進行的資源更新時發生常見形式的死鎖。

(3)、排它 (x) :用於資料修改操作,例如insert、update或delete。確保不會同時對同一資源進行多重更新。

對於如此看似簡單其實重要繁瑣的東西,當然不能讓龐大的程式設計師群體去設定或控制它們。sql server通過設定事務的隔離級別自動管理鎖的設定和控制。鎖管理器通過查詢分析器分析待執行的sql語句,進而來判斷這些sql語句將會訪問哪些資源,進行什麼操作,然後結合設定的隔離級別自動分配管理需要用到的鎖。

下面接著來了解一下行版本控制。

2、行版本控制:

還用想嗎?小抄一下:

(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

3、小結

根據前面的分析,我們知道,sql server 2005控制併發訪問已經有了兩種有效的途徑;nolock語句執行時不發出共享鎖,允許髒讀 ,等於read uncommitted事務隔離級別,從這種意義上來講,nolock確實在查詢的時候能提高速度。但現在我們再來問一下自己,nolock需要加嗎,不需要加嗎?真的需要加嗎,真的不需要加嗎??您能再肯定點回答嗎?

三、nolock的適用場景(下面的幾點完全是個人意見,可以54。)

1、「持久化」的表:也就是資料不會經常變動的表,比如我們熟知的省、市、縣和航空公司、機場等等。它們的共同特徵就是至少從目前來看,這些資料長時間不會有任何改變。其實從長遠來看,甚至乙個非常成熟的公司的部門表也可以作為這類資料來處理,但是和部門有關係的員工表就不可以;

2、允許髒讀的一些業務邏輯:這個沒什麼好說的,客戶需求決定了你不在這上面「較真」。比如我們要查詢某個業務部門某乙個季度或某一年的業績統計,需要了解大概情況就可以了。這種情形下,查詢nolock多少次都無所謂。

3、儲存了海量資料的表:這個毫無疑問,資料量大,重要性越強,訪問也就越多,併發操作影響到的記錄也就可能越大,所謂「樹大招風」,不過如此。我們給查詢加上nolock可以大大提公升效能和使用者體驗,當然,它是以犧牲資料一致性和安全性來提公升效能的。

最後,通過以上分析,我們得出結論,查詢(尤其是海量資料)不加鎖,毫無疑問,速度確確實實是提高了,但是我們更應該有選擇性的挑選最適合的表來使用nolock。因為我們已經都知道,「對資料表的併發操作」很可能造成一些查詢結果的困擾,比如我們所熟知的「髒讀「。設想一下吧,對於沒有預期的一些查詢(所謂」預期查詢「,就是使用者認為前後查詢結果不一致也是合理的,比如訂單查詢中乙個訂單的訂單狀態的變化導致前後結果不一致等等),因為」髒讀「造成的」髒資料「前後查詢結果不一致,一次兩次也就罷了,可能使用的人以為自己眼花了還是怎麼的。但是如果多次或者大資料量地出現資料不匹配,肯定會讓不明所以的使用者困惑,心理素質好的會習慣性地把問題推給系統,心裡素質不好的的還以為自己誤操作還是怎麼的,直接造成恐慌甚至懷疑自己rp。

sql查詢,nolock寫還是不寫,這是乙個問題

在做過的很多專案中,發現大家不管對什麼表,逢select必定加上nolock 或者with nolock 好像已經是制度化的一種東西。有領導高人解釋說加上nolock可以提高查詢速度,不影響對資料表的其他併發操作。但是真有必要每個查詢都加nolock嗎?個人認為加不加nolock還是值得我們根據實際...

子查詢sql怎麼寫

子查詢就是巢狀在主查詢中的查詢。子查詢可以巢狀在主查詢中所有位置,包括select from where order by。但並不是每個位置巢狀子查詢都是有意義並實用的。子查詢必須 自身就是乙個完整的查詢 即,它必須至少包括乙個select子句和from子句。個人認為這個sql很實用,都可以套著用。...

CI中寫原生SQL 封裝查詢

封裝,通過讓系統為你組裝各個查詢語句,能夠簡化你的查詢語法。參加下面的範例 sql select from some table where id and status and author this db query sql,array 3,live rick 查詢語句中的問號會自動被查詢函式中位...