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