sql查询,nolock写还是不写,这是一个问题
在做過的很多項目中,發(fā)現(xiàn)大家不管對什么表,逢select必定加上nolock(或者with(nolock)),好像已經(jīng)是制度化的一種東西。有領(lǐng)導高人解釋說加上nolock可以提高查詢速度,不影響對數(shù)據(jù)表的其他并發(fā)操作。??
但是真有必要每個查詢都加nolock嗎?個人認為加不加nolock還是值得我們根據(jù)實際情況斟酌一番的(至少需要知其然然后知其所以然吧)。下面就來簡單分析一下加不加nolock以及加了nolock對實際查詢的一些影響。
一、重要概念
(此處沉思5秒,安靜回想經(jīng)典數(shù)據(jù)庫教科書里的一些重用概念。嗯......什么,你也想不全了?那好吧,別閑煩,道理是要講的,書是不得不參考的(bs直接抄書的))
并發(fā)訪問:同一時間有多個用戶訪問同一資源。如果并發(fā)用戶中有其他用戶同時對資源進行了修改,這樣對同一數(shù)據(jù)的訪問就會出現(xiàn)“所見不是所得”的情況,從而對其它用戶產(chǎn)生某些不利的影響,包括:
1:臟讀:有一個用戶對某一個資源做了修改,此時另外一個用戶正好讀取了這條被修改的記錄,然后第一個用戶又放棄了修改,數(shù)據(jù)還原到修改之前,這兩個不同的結(jié)果就是臟讀。
2:幻讀:特指用戶讀取一批記錄的情況。用戶兩次查詢同一條件的一批記錄,第一次查詢后,有其它用戶對這批數(shù)據(jù)做了修改,方法可能是insert,update或delete,第二次查詢時,用戶會發(fā)現(xiàn)第一次查詢的記錄條目有的不在第二次查詢結(jié)果中,或者是第二次查詢的條目不在第一次查詢的內(nèi)容中,造成前后查詢結(jié)果的不一致。
3:不可重復讀:系統(tǒng)中某一個用戶的一個操作是一個事務(wù),這個事務(wù)分兩次讀取同一條記錄。如果第一次讀取后,正好有另外一個用戶修改了這條記錄,然后第二次讀取的正好是之前進行修改記錄的那位用戶的數(shù)據(jù),這樣就有可能造成兩次讀取的數(shù)據(jù)不同。當然如果我們在事務(wù)中鎖定這條記錄就可以避免。
二、如何消除并發(fā)訪問的不利影響
如前所述,既然并發(fā)訪問會造成這么多不利影響,我們又該如何解決呢?估計一般程序員的下意識反應就是像我們在控制多線程并發(fā)編程的時候一樣,加鎖,lock一下,over。沒錯,還真不能說你說的不對!真是聰明又幸福的程序員啊!
其實在ms的Sql Server中,有兩種并發(fā)訪問的控制機制:鎖和行版本控制,關(guān)于并發(fā)控制,ms的闡述和解決方案真是詳細而周到。你不得不pf我們的ms是多么的親媽啊,真的什么都幫我們想好并且做好了。
先分析一下數(shù)據(jù)庫的鎖。
小抄一段參考書上的:
1、鎖:“每個事務(wù)對所依賴的資源會請求不同類型的鎖,它可以阻止其他事務(wù)以某種可能會導致事務(wù)請求鎖出錯的方式修改資源。當事務(wù)不再依賴鎖定的資源時,鎖將被釋放”。?從數(shù)據(jù)庫系統(tǒng)的角度來看:我們可以把鎖分為共享鎖、獨占鎖(排它鎖)和更新鎖:
(1)、共享 (S) :用于不更改或不更新數(shù)據(jù)的操作(只讀操作),比如我們常見的select語句等。?
(2)、更新 (U) :用于可更新的資源中。防止當多個會話在讀取、鎖定以及隨后可能進行的資源更新時發(fā)生常見形式的死鎖。
(3)、排它 (X) :用于數(shù)據(jù)修改操作,例如insert、update或delete。確保不會同時對同一資源進行多重更新。
對于如此看似簡單其實重要繁瑣的東西,當然不能讓龐大的程序員群體去設(shè)置或控制它們。Sql Server通過設(shè)置事務(wù)的隔離級別自動管理鎖的設(shè)置和控制。鎖管理器通過查詢分析器分析待執(zhí)行的sql語句,進而來判斷這些sql語句將會訪問哪些資源,進行什么操作,然后結(jié)合設(shè)定的隔離級別自動分配管理需要用到的鎖。
下面接著來了解一下行版本控制。
2、行版本控制:
還用想嗎?小抄一下:
(1)、簡介
“?行版本控制的隔離是Sql Server 2005一個新的隔離框架。使用行版本控制的隔離可以在大量并發(fā)的情況下,顯著減少所得產(chǎn)生,并且與nolock相比,它又可以顯著降低骯臟讀,幻影,丟失更新等現(xiàn)象的發(fā)生(READ_COMMITTED_SNAPSHOT)。當在基于行版本控制的隔離下運行的事務(wù)讀取數(shù)據(jù)時,讀取操作不會獲取正被讀取的數(shù)據(jù)上的共享鎖(S 鎖),因此不會阻塞正在修改數(shù)據(jù)的事務(wù)。另外,鎖定資源的開銷隨著所獲取的鎖的數(shù)量的減少降至最低。使用行版本控制的已提交讀隔離和快照隔離可以提供副本數(shù)據(jù)的語句級或事務(wù)級讀取一致性”。
(2)、原理
“Sql Server 2005的行版本控制原理上很簡單(不說不知道,筆者注),就是在庫表中每一行的記錄上都悄悄的增加了一個類時間戳列(行版本列)。當使用行版本控制的隔離時,Sql Server 2005 Database Engine 向使用行版本控制操作數(shù)據(jù)的每個事務(wù)分配一個事務(wù)序列號 (XSN)。事務(wù)在執(zhí)行 BEGIN TRANSACTION 語句時啟動。但是,事務(wù)序列號在執(zhí)行 BEGIN TRANSACTION 語句后的第一次讀/寫操作時開始增加。事務(wù)序列號在每次分配時都增加1。當事務(wù)執(zhí)行時,Sql Server根據(jù)行版本列,來提供的行的相應版本。而Sql Server將維護所有在數(shù)據(jù)庫中執(zhí)行的數(shù)據(jù)修改的邏輯副本(版本)。特定的事務(wù)每次修改行時,數(shù)據(jù)庫引擎 實例都存儲以前提交的 tempdb 中行的圖像版本。每個版本都標記有進行此更改的事務(wù)的事務(wù)序列號。已修改行的版本使用鏈接列表鏈接在一起。最新的行值始終存儲在當前的數(shù)據(jù)庫中并鏈接至版本存儲區(qū) tempdb 中存儲的版本。(修改大型對象 (LOB) 時,只有已更改的片段才會復制到 tempdb 中的版本存儲區(qū),? 對于短期運行的事務(wù),已修改行的版本將可能保存在緩沖池中,而不會寫入 tempdb 數(shù)據(jù)庫的磁盤文件中。如果只是臨時需要副本行,它將只是簡單地從緩沖池中刪除而不會引發(fā) I/O 開銷。)”
(3)、優(yōu)勢
使用行版本控制的隔離級別具有以下優(yōu)點:
? a、讀取操作檢索一致的數(shù)據(jù)庫快照;
? b、select語句在讀取操作過程中不鎖定數(shù)據(jù)(讀取器不阻塞編寫器,編寫器也不阻塞讀取器);
? c、select語句可以在其他事務(wù)更新行時訪問最后提交的行值,而不阻塞應用程序;
? d、死鎖的數(shù)量減少;
? e、事務(wù)所需的鎖的數(shù)量減少,這減少了管理鎖所需的系統(tǒng)開銷;
???????f、鎖升級的次數(shù)減少。
(4)、行版本控制小結(jié):
當啟用了基于行版本控制的隔離級別時,數(shù)據(jù)庫引擎將維護修改的每一行的版本。應用程序可以指定事務(wù)使用行版本查看事務(wù)或查詢開始時存在的數(shù)據(jù),而不是使用鎖保護所有讀取。通過使用行版本控制,讀取操作阻止其他事務(wù)的可能性將大大降低,也就是相當于針對所有的表在查詢時都會加上nolock。雖然同樣會產(chǎn)生臟讀的現(xiàn)象,但差別在于我們不用每次查詢都加上nolock,行版本控制策略默認的一個設(shè)置就幫我們搞定了。
BTW,既然說到了基于行版本控制的隔離級別,不得不說下隔離級別。隔離級別,怎么說呢?您別不懷好意地笑,抄書ing:
<1>、用處:控制鎖的應用,即什么場景應用什么樣的鎖機制,解決并發(fā)處理帶來的種種問題;;
<2>、分類:
a、未提交讀(UnCommitted Read):悲觀,相當于(nolock;隔離事務(wù)的最低級別,只能保證不讀取物理上損壞的數(shù)據(jù)。
b、已提交讀(Read Committed):悲觀,數(shù)據(jù)庫引擎的缺省模式,讀操作共享鎖時間一直到讀取結(jié)束。
c、可重復讀(Repeatable Read):悲觀,讀操作共享鎖時間比已提交讀模式更長,一直到事務(wù)結(jié)束。
d、可序列化(Serializable):悲觀,相當于(HoldLock),最嚴謹。
e、已提交讀快照(Read Committed Snapshot):樂觀,2005新增,基于行版本控制,所有讀操作不受其他鎖的影響,歷史數(shù)據(jù)保存更短,Temp空間更少,支持分布式。
Alter Database 數(shù)據(jù)庫名稱 Set Read_Committed_Snapshot On
f、快照(Snapshot):樂觀,2005新增,基于行版本控制,所有讀操作不受其他鎖的影響,歷史數(shù)據(jù)保存更長,Temp空間更多,不支持分布式。
Alter Database 數(shù)據(jù)庫名稱 Set Allow_Snapshot_Isolation On
?<3>、查看當前隔離模式和行版本控制狀態(tài) (2005)
DBCC?UserOptionsSelect?name,?snapshot_isolation_state,?snapshot_isolation_state_desc,?is_read_committed_snapshot_on?From?sys.databases
?3、小結(jié)
根據(jù)前面的分析,我們知道,Sql Server 2005控制并發(fā)訪問已經(jīng)有了兩種有效的途徑;nolock語句執(zhí)行時不發(fā)出共享鎖,允許臟讀 ,等于READ UNCOMMITTED事務(wù)隔離級別,從這種意義上來講,nolock確實在查詢的時候能提高速度。但現(xiàn)在我們再來問一下自己,nolock需要加嗎,不需要加嗎?真的需要加嗎,真的不需要加嗎??您能再肯定點回答嗎?
三、nolock的適用場景(下面的幾點完全是個人意見,可以54。)
1、“持久化”的表:也就是數(shù)據(jù)不會經(jīng)常變動的表,比如我們熟知的省、市、縣和航空公司、機場等等。它們的共同特征就是至少從目前來看,這些數(shù)據(jù)長時間不會有任何改變。其實從長遠來看,甚至一個非常成熟的公司的部門表也可以作為這類數(shù)據(jù)來處理,但是和部門有關(guān)系的員工表就不可以;
2、允許臟讀的一些業(yè)務(wù)邏輯:這個沒什么好說的,客戶需求決定了你不在這上面“較真”。比如我們要查詢某個業(yè)務(wù)部門某一個季度或某一年的業(yè)績統(tǒng)計,需要了解大概情況就可以了。這種情形下,查詢nolock多少次都無所謂。
3、存儲了海量數(shù)據(jù)的表:這個毫無疑問,數(shù)據(jù)量大,重要性越強,訪問也就越多,并發(fā)操作影響到的記錄也就可能越大,所謂“樹大招風”,不過如此。我們給查詢加上nolock可以大大提升性能和用戶體驗,當然,它是以犧牲數(shù)據(jù)一致性和安全性來提升性能的。
最后,通過以上分析,我們得出結(jié)論,查詢(尤其是海量數(shù)據(jù))不加鎖,毫無疑問,速度確確實實是提高了,但是我們更應該有選擇性的挑選最適合的表來使用nolock。因為我們已經(jīng)都知道,“對數(shù)據(jù)表的并發(fā)操作”很可能造成一些查詢結(jié)果的困擾,比如我們所熟知的“臟讀“。設(shè)想一下吧,對于沒有預期的一些查詢(所謂”預期查詢“,就是使用者認為前后查詢結(jié)果不一致也是合理的,比如訂單查詢中一個訂單的訂單狀態(tài)的變化導致前后結(jié)果不一致等等),因為”臟讀“造成的”臟數(shù)據(jù)“前后查詢結(jié)果不一致,一次兩次也就罷了,可能使用的人以為自己眼花了還是怎么的。但是如果多次或者大數(shù)據(jù)量地出現(xiàn)數(shù)據(jù)不匹配,肯定會讓不明所以的使用者困惑,心理素質(zhì)好的會習慣性地把問題推給系統(tǒng),心里素質(zhì)不好的的還以為自己誤操作還是怎么的,直接造成恐慌甚至懷疑自己rp。
本文轉(zhuǎn)自JeffWong博客園博客,原文鏈接:http://www.cnblogs.com/jeffwongishandsome/archive/2010/01/21/1652254.html如需轉(zhuǎn)載請自行聯(lián)系原作者
總結(jié)
以上是生活随笔為你收集整理的sql查询,nolock写还是不写,这是一个问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 6、Gerrit插件
- 下一篇: 把sqlserver中存储过程改写到or