計(jì)算機(jī)學(xué)習(xí)積累

          ----轉(zhuǎn)載有理,轉(zhuǎn)載是想研究,想研究才會(huì)看,看了才會(huì)有感想,轉(zhuǎn)載后我有時(shí)會(huì)寫一些自己的感受
          數(shù)據(jù)加載中……

          MS SQL Server數(shù)據(jù)庫事務(wù)鎖機(jī)制分析(轉(zhuǎn))

          ???? 鎖是網(wǎng)絡(luò)數(shù)據(jù)庫中的一個(gè)非常重要的概念,它主要用于多用戶環(huán)境下保證數(shù)據(jù)庫完整性和一致性。各種大型數(shù)據(jù)庫所采用的鎖的基本理論是一致的,但在具體實(shí)現(xiàn)上各有差別。目前,大多數(shù)數(shù)據(jù)庫管理系統(tǒng)都或多或少具有自我調(diào)節(jié)、自我管理的功能,因此很多用戶實(shí)際上不清楚鎖的理論和所用數(shù)據(jù)庫中鎖的具體實(shí)現(xiàn)。

          Microsoft SQL Server(以下簡(jiǎn)稱SQL Server)作為一種中小型數(shù)據(jù)庫管理系統(tǒng),已經(jīng)得到了廣泛的應(yīng)用,該系統(tǒng)更強(qiáng)調(diào)由系統(tǒng)來管理鎖。在用戶有SQL請(qǐng)求時(shí),系統(tǒng)分析請(qǐng)求,自動(dòng)在滿足鎖定條件和系統(tǒng)性能之間為數(shù)據(jù)庫加上適當(dāng)?shù)逆i,同時(shí)系統(tǒng)在運(yùn)行期間常常自動(dòng)進(jìn)行優(yōu)化處理,實(shí)行動(dòng)態(tài)加鎖。對(duì)于一般的用戶而言,通過系統(tǒng)的自動(dòng)鎖定管理機(jī)制基本可以滿足使用要求,但如果對(duì)數(shù)據(jù)安全、數(shù)據(jù)庫完整性和一致性有特殊要求,就必須自己控制數(shù)據(jù)庫的鎖定和解鎖,這就需要了解SQL Server的鎖機(jī)制,掌握數(shù)據(jù)庫鎖定方法。

          鎖的多粒度性以及鎖升級(jí)

          數(shù)據(jù)庫中的鎖是指一種軟件機(jī)制,用來指示某個(gè)用戶(也即進(jìn)程會(huì)話,下同)已經(jīng)占用了某種資源,從而防止其他用戶做出影響本用戶的數(shù)據(jù)修改或?qū)е聰?shù)據(jù)庫數(shù)據(jù)的非完整性和非一致性。這兒所謂資源,主要指用戶可以操作的數(shù)據(jù)行、索引以及數(shù)據(jù)表等。根據(jù)資源的不同,鎖有多粒度(multigranular)的概念,也就是指可以鎖定的資源的層次。SQL Server中能夠鎖定的資源粒度包括:數(shù)據(jù)庫、表、區(qū)域、頁面、鍵值(指帶有索引的行數(shù)據(jù))、行標(biāo)識(shí)符(RID,即表中的單行數(shù)據(jù))。

          采用多粒度鎖的重要用途是用來支持并發(fā)操作和保證數(shù)據(jù)的完整性。SQL Server根據(jù)用戶的請(qǐng)求,做出分析后自動(dòng)給數(shù)據(jù)庫加上合適的鎖。假設(shè)某用戶只操作一個(gè)表中的部分行數(shù)據(jù),系統(tǒng)可能會(huì)只添加幾個(gè)行鎖(RID)或頁面鎖,這樣可以盡可能多地支持多用戶的并發(fā)操作。但是,如果用戶事務(wù)中頻繁對(duì)某個(gè)表中的多條記錄操作,將導(dǎo)致對(duì)該表的許多記錄行都加上了行級(jí)鎖,數(shù)據(jù)庫系統(tǒng)中鎖的數(shù)目會(huì)急劇增加,這樣就加重了系統(tǒng)負(fù)荷,影響系統(tǒng)性能。因此,在數(shù)據(jù)庫系統(tǒng)中,一般都支持鎖升級(jí)(lock escalation)。所謂鎖升級(jí)是指調(diào)整鎖的粒度,將多個(gè)低粒度的鎖替換成少數(shù)的更高粒度的鎖,以此來降低系統(tǒng)負(fù)荷。在SQL Server中當(dāng)一個(gè)事務(wù)中的鎖較多,達(dá)到鎖升級(jí)門限時(shí),系統(tǒng)自動(dòng)將行級(jí)鎖和頁面鎖升級(jí)為表級(jí)鎖。特別值得注意的是,在SQL Server中,鎖的升級(jí)門限以及鎖升級(jí)是由系統(tǒng)自動(dòng)來確定的,不需要用戶設(shè)置。

          鎖的模式和兼容性

          在數(shù)據(jù)庫中加鎖時(shí),除了可以對(duì)不同的資源加鎖,還可以使用不同程度的加鎖方式,即鎖有多種模式,SQL Server中鎖模式包括:

          1.共享鎖

          SQL Server中,共享鎖用于所有的只讀數(shù)據(jù)操作。共享鎖是非獨(dú)占的,允許多個(gè)并發(fā)事務(wù)讀取其鎖定的資源。默認(rèn)情況下,數(shù)據(jù)被讀取后,SQL Server立即釋放共享鎖。例如,執(zhí)行查詢“SELECT * FROM my_table”時(shí),首先鎖定第一頁,讀取之后,釋放對(duì)第一頁的鎖定,然后鎖定第二頁。這樣,就允許在讀操作過程中,修改未被鎖定的第一頁。但是,事務(wù)隔離級(jí)別連接選項(xiàng)設(shè)置和SELECT語句中的鎖定設(shè)置都可以改變SQL Server的這種默認(rèn)設(shè)置。例如,“ SELECT * FROM my_table HOLDLOCK”就要求在整個(gè)查詢過程中,保持對(duì)表的鎖定,直到查詢完成才釋放鎖定。

          2.修改鎖

          修改鎖在修改操作的初始化階段用來鎖定可能要被修改的資源,這樣可以避免使用共享鎖造成的死鎖現(xiàn)象。因?yàn)槭褂霉蚕礞i時(shí),修改數(shù)據(jù)的操作分為兩步,首先獲得一個(gè)共享鎖,讀取數(shù)據(jù),然后將共享鎖升級(jí)為獨(dú)占鎖,然后再執(zhí)行修改操作。這樣如果同時(shí)有兩個(gè)或多個(gè)事務(wù)同時(shí)對(duì)一個(gè)事務(wù)申請(qǐng)了共享鎖,在修改數(shù)據(jù)的時(shí)候,這些事務(wù)都要將共享鎖升級(jí)為獨(dú)占鎖。這時(shí),這些事務(wù)都不會(huì)釋放共享鎖而是一直等待對(duì)方釋放,這樣就造成了死鎖。如果一個(gè)數(shù)據(jù)在修改前直接申請(qǐng)修改鎖,在數(shù)據(jù)修改的時(shí)候再升級(jí)為獨(dú)占鎖,就可以避免死鎖。修改鎖與共享鎖是兼容的,也就是說一個(gè)資源用共享鎖鎖定后,允許再用修改鎖鎖定。

          3.獨(dú)占鎖

          獨(dú)占鎖是為修改數(shù)據(jù)而保留的。它所鎖定的資源,其他事務(wù)不能讀取也不能修改。獨(dú)占鎖不能和其他鎖兼容。

          4.結(jié)構(gòu)鎖

          結(jié)構(gòu)鎖分為結(jié)構(gòu)修改鎖(Sch-M)和結(jié)構(gòu)穩(wěn)定鎖(Sch-S)。執(zhí)行表定義語言操作時(shí),SQL Server采用Sch-M鎖,編譯查詢時(shí),SQL Server采用Sch-S鎖。

          5.意向鎖

          意向鎖說明SQL Server有在資源的低層獲得共享鎖或獨(dú)占鎖的意向。例如,表級(jí)的共享意向鎖說明事務(wù)意圖將獨(dú)占鎖釋放到表中的頁或者行。意向鎖又可以分為共享意向鎖、獨(dú)占意向鎖和共享式獨(dú)占意向鎖。共享意向鎖說明事務(wù)意圖在共享意向鎖所鎖定的低層資源上放置共享鎖來讀取數(shù)據(jù)。獨(dú)占意向鎖說明事務(wù)意圖在共享意向鎖所鎖定的低層資源上放置獨(dú)占鎖來修改數(shù)據(jù)。共享式獨(dú)占鎖說明事務(wù)允許其他事務(wù)使用共享鎖來讀取頂層資源,并意圖在該資源低層上放置獨(dú)占鎖。

          6.批量修改鎖

          批量復(fù)制數(shù)據(jù)時(shí)使用批量修改鎖。可以通過表的TabLock提示或者使用系統(tǒng)存儲(chǔ)過程sp_tableoption的“table lock on bulk load”選項(xiàng)設(shè)定批量修改鎖。

          另外,SQL Server命令語句操作會(huì)影響鎖定的方式,語句的組合也同樣能產(chǎn)生不同的鎖定,詳情如下表:

          鎖沖突及其防止辦法

          在數(shù)據(jù)庫系統(tǒng)中,死鎖是指多個(gè)用戶(進(jìn)程)分別鎖定了一個(gè)資源,并又試圖請(qǐng)求鎖定對(duì)方已經(jīng)鎖定的資源,這就產(chǎn)生了一個(gè)鎖定請(qǐng)求環(huán),導(dǎo)致多個(gè)用戶(進(jìn)程)都處于等待對(duì)方釋放所鎖定資源的狀態(tài)。

          在SQL Server中,系統(tǒng)能夠自動(dòng)定期搜索和處理死鎖問題。系統(tǒng)在每次搜索中標(biāo)識(shí)所有等待鎖定請(qǐng)求的進(jìn)程會(huì)話,如果在下一次搜索中該被標(biāo)識(shí)的進(jìn)程仍處于等待狀態(tài),SQL Server就開始遞歸死鎖搜索。

          (上接第D21版) 當(dāng)搜索檢測(cè)到鎖定請(qǐng)求環(huán)時(shí),系統(tǒng)將根據(jù)各進(jìn)程會(huì)話的死鎖優(yōu)先級(jí)別來結(jié)束一個(gè)優(yōu)先級(jí)最低的事務(wù),此后,系統(tǒng)回滾該事務(wù),并向該進(jìn)程發(fā)出1205號(hào)錯(cuò)誤信息。這樣,其他事務(wù)就有可能繼續(xù)運(yùn)行了。死鎖優(yōu)先級(jí)的設(shè)置語句為:

          SET DEADLOCK_PRIORITY { LOW | NORMAL}

          其中LOW說明該進(jìn)程會(huì)話的優(yōu)先級(jí)較低,在出現(xiàn)死鎖時(shí),可以首先中斷該進(jìn)程的事務(wù)。另外,各進(jìn)程中通過設(shè)置LOCK_TIMEOUT選項(xiàng)能夠設(shè)置進(jìn)程處于鎖定請(qǐng)求狀態(tài)的最長(zhǎng)等待時(shí)間。該設(shè)置的語句:

          SET LOCK_TIMEOUT { timeout_period }

          其中,timeout_period以毫秒為單位。

          理解了死鎖的概念,在應(yīng)用程序中就可以采用下面的一些方法來盡量避免死鎖了:

          (1)合理安排表訪問順序。

          (2)在事務(wù)中盡量避免用戶干預(yù),盡量使一個(gè)事務(wù)處理的任務(wù)少些。

          (3)采用臟讀技術(shù)。臟讀由于不對(duì)被訪問的表加鎖,而避免了鎖沖突。在客戶機(jī)/服務(wù)器應(yīng)用環(huán)境中,有些事務(wù)往往不允許讀臟數(shù)據(jù),但在特定的條件下,我們可以用臟讀。

          (4)數(shù)據(jù)訪問時(shí)域離散法。數(shù)據(jù)訪問時(shí)域離散法是指在客戶機(jī)/服務(wù)器結(jié)構(gòu)中,采取各種控制手段控制對(duì)數(shù)據(jù)庫或數(shù)據(jù)庫中的對(duì)象訪問時(shí)間段。主要通過以下方式實(shí)現(xiàn): 合理安排后臺(tái)事務(wù)的執(zhí)行時(shí)間,采用工作流對(duì)后臺(tái)事務(wù)進(jìn)行統(tǒng)一管理。工作流在管理任務(wù)時(shí),一方面限制同一類任務(wù)的線程數(shù)(往往限制為1個(gè)),防止資源過多占用; 另一方面合理安排不同任務(wù)執(zhí)行時(shí)序、時(shí)間,盡量避免多個(gè)后臺(tái)任務(wù)同時(shí)執(zhí)行,另外, 避免在前臺(tái)交易高峰時(shí)間運(yùn)行后臺(tái)任務(wù)。

          (5)數(shù)據(jù)存儲(chǔ)空間離散法。數(shù)據(jù)存儲(chǔ)空間離散法是指采取各種手段,將邏輯上在一個(gè)表中的數(shù)據(jù)分散到若干離散的空間上去,以便改善對(duì)表的訪問性能。主要通過以下方法實(shí)現(xiàn): 第一,將大表按行或列分解為若干小表; 第二,按不同的用戶群分解。

          (6)使用盡可能低的隔離性級(jí)別。隔離性級(jí)別是指為保證數(shù)據(jù)庫數(shù)據(jù)的完整性和一致性而使多用戶事務(wù)隔離的程度,SQL92定義了4種隔離性級(jí)別:未提交讀、提交讀、可重復(fù)讀和可串行。如果選擇過高的隔離性級(jí)別,如可串行,雖然系統(tǒng)可以因?qū)崿F(xiàn)更好隔離性而更大程度上保證數(shù)據(jù)的完整性和一致性,但各事務(wù)間沖突而死鎖的機(jī)會(huì)大大增加,大大影響了系統(tǒng)性能。

          (7)使用Bound Connections。Bound connections 允許兩個(gè)或多個(gè)事務(wù)連接共享事務(wù)和鎖,而且任何一個(gè)事務(wù)連接要申請(qǐng)鎖如同另外一個(gè)事務(wù)要申請(qǐng)鎖一樣,因此可以允許這些事務(wù)共享數(shù)據(jù)而不會(huì)有加鎖的沖突。

          (8)考慮使用樂觀鎖定或使事務(wù)首先獲得一個(gè)獨(dú)占鎖定。一個(gè)最常見的死鎖情況發(fā)生在系列號(hào)生成器中,它們通常是這樣編寫的:

          begin tran

          select new_id from keytab holdlock

          update keytab set new_id=new_id+l

          commit tran

          如果有兩個(gè)用戶在同時(shí)運(yùn)行這一事務(wù),他們都會(huì)得到共享鎖定并保持它。當(dāng)兩個(gè)用戶都試圖得到keytab表的獨(dú)占鎖定時(shí),就會(huì)進(jìn)入死鎖。為了避免這種情況的發(fā)生,應(yīng)將上述事務(wù)重寫成如下形式:

          begin tran

          update keytab set new_id=new_id+l

          select new_id from keytab

          commit tran

          以這種方式改寫后,只有一個(gè)事務(wù)能得到keytab的獨(dú)占鎖定,其他進(jìn)程必須等到第一個(gè)事務(wù)的完成,這樣雖增加了執(zhí)行時(shí)間,但避免了死鎖。

          如果要求在一個(gè)事務(wù)中具有讀取的可重復(fù)能力,就要考慮以這種方式來編寫事務(wù),以獲得資源的獨(dú)占鎖定,然后再去讀數(shù)據(jù)。例如,如果一個(gè)事務(wù)需要檢索出titles表中所有書的平均價(jià)格,并保證在update被應(yīng)用前,結(jié)果不會(huì)改變,優(yōu)化器就會(huì)分配一個(gè)獨(dú)占的表鎖定。考慮如下的SQL代碼:

          begin tran

          update titles set title_idid=title_id .

          where 1=2

          if (selectavg(price)fromtitles)>$15

          begin

          /* perform some additional processing */

          end

          update titles set price=price*1.10

          where price<(select avg(price)from titles)

          commit tran

          在這個(gè)事務(wù)中,重要的是沒有其他進(jìn)程修改表中任何行的price,或者說在事務(wù)結(jié)束時(shí)檢索的值與事務(wù)開始時(shí)檢索的值不同。這里的where子句看起來很奇怪,但是不管你相信與否,這是迄今為止優(yōu)化器所遇到的最完美有效的where子句,盡管計(jì)算出的結(jié)果總是false。當(dāng)優(yōu)化器處理此查詢時(shí),因?yàn)樗也坏饺魏斡行У腟ARG,它的查詢規(guī)劃就會(huì)強(qiáng)制使用一個(gè)獨(dú)占鎖定來進(jìn)行表掃描。此事務(wù)執(zhí)行時(shí),where子句立即得到一個(gè)false值,于是不會(huì)執(zhí)行實(shí)際上的掃描,但此進(jìn)程仍得到了一個(gè)獨(dú)占的表鎖定。

          因?yàn)榇诉M(jìn)程現(xiàn)在已有一個(gè)獨(dú)占的表鎖,所以可以保證沒有其他事務(wù)會(huì)修改任何數(shù)據(jù)行,能進(jìn)行重復(fù)讀,且避免了由于holdlock所引起的潛在性死鎖。但是,要避免死鎖,不可能不付出代價(jià)。在使用表鎖定來盡可能地減少死鎖的同時(shí),也增加了對(duì)表鎖定的爭(zhēng)用。因此,在實(shí)現(xiàn)這種方法之前,你需要權(quán)衡一下:避免死鎖是否比允許并發(fā)地對(duì)表進(jìn)行訪問更重要。

          手工加鎖

          SQL Server系統(tǒng)中建議讓系統(tǒng)自動(dòng)管理鎖,該系統(tǒng)會(huì)分析用戶的SQL語句要求,自動(dòng)為該請(qǐng)求加上合適的鎖,而且在鎖的數(shù)目太多時(shí),系統(tǒng)會(huì)自動(dòng)進(jìn)行鎖升級(jí)。如前所述,升級(jí)的門限由系統(tǒng)自動(dòng)配置,并不需要用戶配置。

          在實(shí)際應(yīng)用中,有時(shí)為了應(yīng)用程序正確運(yùn)行和保持?jǐn)?shù)據(jù)的一致性,必須人為地給數(shù)據(jù)庫的某個(gè)表加鎖。比如,在某應(yīng)用程序的一個(gè)事務(wù)操作中,需要根據(jù)一編號(hào)對(duì)幾個(gè)數(shù)據(jù)表做統(tǒng)計(jì)操作,為保證統(tǒng)計(jì)數(shù)據(jù)時(shí)間的一致性和正確性,從統(tǒng)計(jì)第一個(gè)表開始到全部表結(jié)束,其他應(yīng)用程序或事務(wù)不能再對(duì)這幾個(gè)表寫入數(shù)據(jù),這個(gè)時(shí)候,該應(yīng)用程序希望在從統(tǒng)計(jì)第一個(gè)數(shù)據(jù)表開始或在整個(gè)事務(wù)開始時(shí)能夠由程序人為地(顯式地)鎖定這幾個(gè)表,這就需要用到手工加鎖(也稱顯式加鎖)技術(shù)。

          在SQL Server 的SQL語句(SELECT、INSERT、DELETE、UPDATE)支持顯式加鎖。這4個(gè)語句在顯式加鎖的語法上類似,下面僅以SELECT語句為例給出語法:

          SELECT FROM [ WITH ]

          其中,指需要在該語句執(zhí)行時(shí)添加在該表上的鎖類型。所指定的鎖類型有如下幾種:

          1.HOLDLOCK: 在該表上保持共享鎖,直到整個(gè)事務(wù)結(jié)束,而不是在語句執(zhí)行完立即釋放所添加的鎖。

          2.NOLOCK:不添加共享鎖和排它鎖,當(dāng)這個(gè)選項(xiàng)生效后,可能讀到未提交讀的數(shù)據(jù)或“臟數(shù)據(jù)”,這個(gè)選項(xiàng)僅僅應(yīng)用于SELECT語句。

          3. PAGLOCK:指定添加頁面鎖(否則通常可能添加表鎖)。

          4.READCOMMITTED:設(shè)置事務(wù)為讀提交隔離性級(jí)別。

          5.READPAST: 跳過已經(jīng)加鎖的數(shù)據(jù)行,這個(gè)選項(xiàng)將使事務(wù)讀取數(shù)據(jù)時(shí)跳過那些已經(jīng)被其他事務(wù)鎖定的數(shù)據(jù)行,而不是阻塞直到其他事務(wù)釋放鎖,READPAST僅僅應(yīng)用于READ COMMITTED隔離性級(jí)別下事務(wù)操作中的SELECT語句操作。

          6.READUNCOMMITTED:等同于NOLOCK。

          7.REPEATABLEREAD:設(shè)置事務(wù)為可重復(fù)讀隔離性級(jí)別。

          8.ROWLOCK:指定使用行級(jí)鎖。

          9.SERIALIZABLE:設(shè)置事務(wù)為可串行的隔離性級(jí)別。

          10.TABLOCK:指定使用表級(jí)鎖,而不是使用行級(jí)或頁面級(jí)的鎖,SQL Server在該語句執(zhí)行完后釋放這個(gè)鎖,而如果同時(shí)指定了HOLDLOCK,該鎖一直保持到這個(gè)事務(wù)結(jié)束。

          11.TABLOCKX:指定在表上使用排它鎖,這個(gè)鎖可以阻止其他事務(wù)讀或更新這個(gè)表的數(shù)據(jù),直到這個(gè)語句或整個(gè)事務(wù)結(jié)束。

          12. UPDLOCK :指定在讀表中數(shù)據(jù)時(shí)設(shè)置修改鎖(update lock)而不是設(shè)置共享鎖,該鎖一直保持到這個(gè)語句或整個(gè)事務(wù)結(jié)束,使用UPDLOCK的作用是允許用戶先讀取數(shù)據(jù)(而且不阻塞其他用戶讀數(shù)據(jù)),并且保證在后來再更新數(shù)據(jù)時(shí),這一段時(shí)間內(nèi)這些數(shù)據(jù)沒有被其他用戶修改。

          由上可見,在SQL Server中可以靈活多樣地為SQL語句顯式加鎖,若適當(dāng)使用,我們完全可以完成一些程序的特殊要求,保證數(shù)據(jù)的一致性和完整性。對(duì)于一般使用者而言,了解鎖機(jī)制并不意味著必須使用它。事實(shí)上,SQL Server建議讓系統(tǒng)自動(dòng)管理數(shù)據(jù)庫中的鎖,而且一些關(guān)于鎖的設(shè)置選項(xiàng)也沒有提供給用戶和數(shù)據(jù)庫管理人員,對(duì)于特殊用戶,通過給數(shù)據(jù)庫中的資源顯式加鎖,可以滿足很高的數(shù)據(jù)一致性和可靠性要求,只是需要特別注意避免死鎖現(xiàn)象的出現(xiàn)。


          哦﹐簡(jiǎn)單講一下﹐如果你使用where語句對(duì)update,delete等操作進(jìn)行限制﹐如果修改了一條﹐有可能會(huì)使用行鎖,行鎖分RID與KID﹐如果有索引﹐SQL產(chǎn)生的是KID﹐如何沒有就會(huì)產(chǎn)生RID﹐都是鎖定一行﹔如果更新涉及行數(shù)較多﹐就會(huì)上升為頁面鎖(每頁8K)或表鎖。

          posted on 2007-09-20 08:57 freebird 閱讀(274) 評(píng)論(0)  編輯  收藏 所屬分類: 數(shù)據(jù)庫

          主站蜘蛛池模板: 临沂市| 甘南县| 兴仁县| 格尔木市| 乃东县| 新干县| 团风县| 福贡县| 肃宁县| 鄂托克旗| 金湖县| 鸡泽县| 安阳市| 响水县| 安塞县| 华宁县| 金沙县| 东乡县| 怀安县| 西安市| 尉氏县| 鹿泉市| 济源市| 化德县| 阿克苏市| 莲花县| 安福县| 临桂县| 介休市| 台前县| 南乐县| 通渭县| 文安县| 平陆县| 晋城| 外汇| 左云县| 托克逊县| 阿勒泰市| 疏勒县| 从化市|