| 2005 年 12 月 26 日 在實(shí)際的生產(chǎn)運行環(huán)境中,筆者在國內很多客戶(hù)現場(chǎng)都看到開(kāi)發(fā)人員和系統管理人員遇到很多有關(guān)于鎖而引起的性能問(wèn)題,進(jìn)而被多次問(wèn)起DB2和Oracle中鎖的區別比較問(wèn)題,筆者根據自己在工作中對DB2和Oracle數據庫的使用經(jīng)驗積累寫(xiě)下這篇文章。 在關(guān)系數據庫(DB2,Oracle,Sybase,Informix和SQL Server)最小的恢復和交易單位為一個(gè)事務(wù)(Transactions),事務(wù)具有ACID(原子性,一致性,隔離性和永久性)特征。關(guān)系數據庫為了確保并發(fā)用戶(hù)在存取同一數據庫對象時(shí)的正確性(即無(wú)丟失更新、可重復讀、不讀"臟"數據,無(wú)"幻像"讀),數據庫中引入了并發(fā)(鎖)機制?;镜逆i類(lèi)型有兩種:排它鎖(Exclusive locks記為X鎖)和共享鎖(Share locks記為S鎖)。 排它鎖:若事務(wù)T對數據D加X(jué)鎖,則其它任何事務(wù)都不能再對D加任何類(lèi)型的鎖,直至T釋放D上的X鎖;一般要求在修改數據前要向該數據加排它鎖,所以排它鎖又稱(chēng)為寫(xiě)鎖。 共享鎖:若事務(wù)T對數據D加S鎖,則其它事務(wù)只能對D加S鎖,而不能加X(jué)鎖,直至T釋放D上的S鎖;一般要求在讀取數據前要向該數據加共享鎖,所以共享鎖又稱(chēng)為讀鎖。
DB2支持對表空間、表、行和索引加鎖(大型機上的數據庫還可以支持對數據頁(yè)加鎖)來(lái)保證數據庫的并發(fā)完整性。不過(guò)在考慮用戶(hù)應用程序的并發(fā)性的問(wèn)題上,通常并不檢查用于表空間和索引的鎖。該類(lèi)問(wèn)題分析的焦點(diǎn)在于表鎖和行鎖。 DB2可以只對表進(jìn)行加鎖,也可以對表和表中的行進(jìn)行加鎖。如果只對表進(jìn)行加鎖,則表中所有的行都受到同等程度的影響。如果加鎖的范圍針對于表及下屬的行,則在對表加鎖后,相應的數據行上還要加鎖。究竟應用程序是對表加行鎖還是同時(shí)加表鎖和行鎖,是由應用程序執行的命令和系統的隔離級別確定。 2.2.1 DB2表鎖的模式 DB2在表一級加鎖可以使用以下加鎖方式: 表一:DB2數據庫表鎖的模式 ![]() 下面對幾種表鎖的模式進(jìn)一步加以闡述: IS、IX、SIX方式用于表一級并需要行鎖配合,他們可以阻止其他應用程序對該表加上排它鎖。
S、U、X和Z方式用于表一級,但并不需要行鎖配合,是比較嚴格的表加鎖策略。
IN鎖用于表上以允許未提交讀這一概念。 2.2.2 DB2行鎖的模式 除了表鎖之外,DB2還支持以下幾種方式的行鎖。 表二:DB2數據庫行鎖的模式 ![]() 2.2.3 DB2鎖的兼容性 表三:DB2數據庫表鎖的相容矩陣 ![]() 表四:DB2數據庫行鎖的相容矩陣 ![]() 下表是筆者總結了DB2中各SQL語(yǔ)句產(chǎn)生表鎖的情況(假設缺省的隔離級別為CS): ![]() 每個(gè)鎖在內存中都需要一定的內存空間,為了減少鎖需要的內存開(kāi)銷(xiāo),DB2提供了鎖升級的功能。鎖升級是通過(guò)對表加上非意圖性的表鎖,同時(shí)釋放行鎖來(lái)減少鎖的數目,從而達到減少鎖需要的內存開(kāi)銷(xiāo)的目的。鎖升級是由數據庫管理器自動(dòng)完成的,有兩個(gè)數據庫的配置參數直接影響鎖升級的處理: locklist--在一個(gè)數據庫全局內存中用于鎖存儲的內存。單位為頁(yè)(4K)。 maxlocks--一個(gè)應用程序允許得到的鎖占用的內存所占locklist大小的百分比。 鎖升級會(huì )在這兩種情況下被觸發(fā):
鎖升級是有可能會(huì )失敗的,比如,現在一個(gè)應用程序已經(jīng)在一個(gè)表上加有IX鎖,表中的某些行上加有X鎖,另一個(gè)應用程序又來(lái)請求表上的IS鎖,以及很多行上的S鎖,由于申請的鎖數目過(guò)多引起鎖的升級。數據庫管理器試圖為該應用程序申請表上的S鎖來(lái)減少所需要的鎖的數目,但S鎖與表上原有的IX鎖沖突,鎖升級不能成功。 如果鎖升級失敗,引起鎖升級的應用程序將接到一個(gè)-912的SQLCODE。在鎖升級失敗后,DBA應該考慮增加locklist的大小或者增大maxlocks的百分比。同時(shí)對編程人員來(lái)說(shuō)可以在程序里對發(fā)生鎖升級后程序回滾后重新提交事務(wù)(例如:if sqlca.sqlcode=-912 then rollback and retry等)。
根據保護對象的不同,Oracle數據庫鎖可以分為以下幾大類(lèi): (1) DML lock(data locks,數據鎖):用于保護數據的完整性; (2) DDL lock(dictionary locks,字典鎖):用于保護數據庫對象的結構(例如表、視圖、索引的結構定義); (3) Internal locks 和latches(內部鎖與閂):保護內部數據庫結構; (4) Distributed locks(分布式鎖):用于OPS(并行服務(wù)器)中; (5) PCM locks(并行高速緩存管理鎖):用于OPS(并行服務(wù)器)中。 在Oracle中最主要的鎖是DML(也可稱(chēng)為data locks,數據鎖)鎖。從封鎖粒度(封鎖對象的大?。┑慕嵌瓤?,Oracle DML鎖共有兩個(gè)層次,即行級鎖和表級鎖。 許多對Oracle不太了解的技術(shù)人員可能會(huì )以為每一個(gè)TX鎖代表一條被封鎖的數據行,其實(shí)不然。TX的本義是Transaction(事務(wù)),當一個(gè)事務(wù)第一次執行數據更改(Insert、Update、Delete)或使用SELECT… FOR UPDATE語(yǔ)句進(jìn)行查詢(xún)時(shí),它即獲得一個(gè)TX(事務(wù))鎖,直至該事務(wù)結束(執行COMMIT或ROLLBACK操作)時(shí),該鎖才被釋放。所以,一個(gè)TX鎖,可以對應多個(gè)被該事務(wù)鎖定的數據行(在我們用的時(shí)候多是啟動(dòng)一個(gè)事務(wù),然后SELECT… FOR UPDATE NOWAIT)。 在Oracle的每行數據上,都有一個(gè)標志位來(lái)表示該行數據是否被鎖定。Oracle不像DB2那樣,建立一個(gè)鏈表來(lái)維護每一行被加鎖的數據,這樣就大大減小了行級鎖的維護開(kāi)銷(xiāo),也在很大程度上避免了類(lèi)似DB2使用行級鎖時(shí)經(jīng)常發(fā)生的鎖數量不夠而進(jìn)行鎖升級的情況。數據行上的鎖標志一旦被置位,就表明該行數據被加X(jué)鎖,Oracle在數據行上沒(méi)有S鎖。 3.2.1 意向鎖的引出 表是由行組成的,當我們向某個(gè)表加鎖時(shí),一方面需要檢查該鎖的申請是否與原有的表級鎖相容;另一方面,還要檢查該鎖是否與表中的每一行上的鎖相容。比如一個(gè)事務(wù)要在一個(gè)表上加S鎖,如果表中的一行已被另外的事務(wù)加了X鎖,那么該鎖的申請也應被阻塞。如果表中的數據很多,逐行檢查鎖標志的開(kāi)銷(xiāo)將很大,系統的性能將會(huì )受到影響。為了解決這個(gè)問(wèn)題,可以在表級引入新的鎖類(lèi)型來(lái)表示其所屬行的加鎖情況,這就引出了"意向鎖"的概念。 意向鎖的含義是如果對一個(gè)結點(diǎn)加意向鎖,則說(shuō)明該結點(diǎn)的下層結點(diǎn)正在被加鎖;對任一結點(diǎn)加鎖時(shí),必須先對它的上層結點(diǎn)加意向鎖。如:對表中的任一行加鎖時(shí),必須先對它所在的表加意向鎖,然后再對該行加鎖。這樣一來(lái),事務(wù)對表加鎖時(shí),就不再需要檢查表中每行記錄的鎖標志位了,系統效率得以大大提高。 3.2.2 意向鎖的類(lèi)型 由兩種基本的鎖類(lèi)型(S鎖、X鎖),可以自然地派生出兩種意向鎖: 意向共享鎖(Intent Share Lock,簡(jiǎn)稱(chēng)IS鎖):如果要對一個(gè)數據庫對象加S鎖,首先要對其上級結點(diǎn)加IS鎖,表示它的后裔結點(diǎn)擬(意向)加S鎖; 意向排它鎖(Intent Exclusive Lock,簡(jiǎn)稱(chēng)IX鎖):如果要對一個(gè)數據庫對象加X(jué)鎖,首先要對其上級結點(diǎn)加IX鎖,表示它的后裔結點(diǎn)擬(意向)加X(jué)鎖。 另外,基本的鎖類(lèi)型(S、X)與意向鎖類(lèi)型(IS、IX)之間還可以組合出新的鎖類(lèi)型,理論上可以組合出4種,即:S+IS,S+IX,X+IS,X+IX,但稍加分析不難看出,實(shí)際上只有S+IX有新的意義,其它三種組合都沒(méi)有使鎖的強度得到提高(即:S+IS=S,X+IS=X,X+IX=X,這里的"="指鎖的強度相同)。所謂鎖的強度是指對其它鎖的排斥程度。 這樣我們又可以引入一種新的鎖的類(lèi)型: 共享意向排它鎖(Shared Intent Exclusive Lock,簡(jiǎn)稱(chēng)SIX鎖):如果對一個(gè)數據庫對象加SIX鎖,表示對它加S鎖,再加IX鎖,即SIX=S+IX。例如:事務(wù)對某個(gè)表加SIX鎖,則表示該事務(wù)要讀整個(gè)表(所以要對該表加S鎖),同時(shí)會(huì )更新個(gè)別行(所以要對該表加IX鎖)。 這樣數據庫對象上所加的鎖類(lèi)型就可能有5種:即S、X、IS、IX、SIX。 具有意向鎖的多粒度封鎖方法中任意事務(wù)T要對一個(gè)數據庫對象加鎖,必須先對它的上層結點(diǎn)加意向鎖。申請封鎖時(shí)應按自上而下的次序進(jìn)行;釋放封鎖時(shí)則應按自下而上的次序進(jìn)行;具有意向鎖的多粒度封鎖方法提高了系統的并發(fā)度,減少了加鎖和解鎖的開(kāi)銷(xiāo)。 Oracle的DML鎖(數據鎖)正是采用了上面提到的多粒度封鎖方法,其行級鎖雖然只有一種(即X鎖),但其TM鎖(表級鎖)類(lèi)型共有5種,分別稱(chēng)為共享鎖(S鎖)、排它鎖(X鎖)、行級共享鎖(RS鎖)、行級排它鎖(RX鎖)、共享行級排它鎖(SRX鎖),與上面提到的S、X、IS、IX、SIX相對應。需要注意的是,由于Oracle在行級只提供X鎖,所以與RS鎖(通過(guò)SELECT … FOR UPDATE語(yǔ)句獲得)對應的行級鎖也是X鎖(但是該行數據實(shí)際上還沒(méi)有被修改),這與理論上的IS鎖是有區別的。鎖的兼容性是指當一個(gè)應用程序在表(行)上加上某種鎖后,其他應用程序是否能夠在表(行)上加上相應的鎖,如果能夠加上,說(shuō)明這兩種鎖是兼容的,否則說(shuō)明這兩種鎖不兼容,不能對同一數據對象并發(fā)存取。 下表為Oracle數據庫TM鎖的兼容矩陣(Y=Yes,表示兼容的請求; N=No,表示不兼容的請求;-表示沒(méi)有加鎖請求): 表五:Oracle數據庫TM鎖的相容矩陣 ![]() 一方面,當Oracle執行SELECT…FOR UPDATE、INSERT、UPDATE、DELETE等DML語(yǔ)句時(shí),系統自動(dòng)在所要操作的表上申請表級RS鎖(SELECT…FOR UPDATE)或RX鎖(INSERT、UPDATE、DELETE),當表級鎖獲得后,系統再自動(dòng)申請TX鎖,并將實(shí)際鎖定的數據行的鎖標志位置位(指向該TX鎖);另一方面,程序或操作人員也可以通過(guò)LOCK TABLE語(yǔ)句來(lái)指定獲得某種類(lèi)型的TM鎖。下表是筆者總結了Oracle中各SQL語(yǔ)句產(chǎn)生TM鎖的情況: 表六:Oracle數據庫TM鎖小結 ![]() 我們可以看到,通常的DML操作(SELECT…FOR UPDATE、INSERT、UPDATE、DELETE),在表級獲得的只是意向鎖(RS或RX),其真正的封鎖粒度還是在行級;另外,Oracle數據庫的一個(gè)顯著(zhù)特點(diǎn)是,在缺省情況下,單純地讀數據(SELECT)并不加鎖,Oracle通過(guò)回滾段(Rollback segment)來(lái)保證用戶(hù)不讀"臟"數據。這些都提高了系統的并發(fā)程度。 由于意向鎖及數據行上鎖標志位的引入,減小了Oracle維護行級鎖的開(kāi)銷(xiāo),這些技術(shù)的應用使Oracle能夠高效地處理高度并發(fā)的事務(wù)請求。
在DB2中對鎖進(jìn)行監控主要有兩種方式,第一種方式是快照監控,第二種是事件監控方式。 當使用快照方式進(jìn)行鎖的監控前,必須把監控鎖的開(kāi)關(guān)打開(kāi),可以從實(shí)例級別和會(huì )話(huà)級別打開(kāi),具體命令如下: db2 update dbm cfg using dft_mon_lock on(實(shí)例級別) 當開(kāi)關(guān)打開(kāi)后,可以執行下列命令來(lái)進(jìn)行鎖的監控 db2 get snapshot for locks on ebankdb(可以得到當前數據庫中具體鎖的詳細信息)
db2 get snapshot for database on dbname |grep -i locks(UNIX,LINUX平臺)
db2 get snapshot for database on dbname |find /i "locks"(NT平臺)
也可以執行下列表函數(注:在DB2 V8之前只能通過(guò)命令,DB2 V8后可以通過(guò)表函數,推薦使用表函數來(lái)進(jìn)行鎖的監控) db2 select * from table(snapshot_lock(‘DBNAME‘,-1)) as locktable監控鎖信息 當使用事件監控器進(jìn)行鎖的監控時(shí)候,只能監控死鎖(死鎖的產(chǎn)生是因為由于鎖請求沖突而不能結束事務(wù),并且該請求沖突不能夠在本事務(wù)內解決。通常是兩個(gè)應用程序互相持有對方所需要的鎖,在得不到自己所需要的鎖的情況下,也不會(huì )釋放現有的鎖)的情況,具體步驟如下: db2 create event monitor dlock for deadlocks with details write to file ‘$HOME/dir‘
為了監控Oracle系統中鎖的狀況,我們需要對幾個(gè)系統視圖有所了解: v$lock視圖列出當前系統持有的或正在申請的所有鎖的情況,其主要字段說(shuō)明如下: 表七:v$lock視圖主要字段說(shuō)明 ![]() 其中在TYPE字段的取值中,本文只關(guān)心TM、TX兩種DML鎖類(lèi)型; v$locked_object視圖列出當前系統中哪些對象正被鎖定,其主要字段說(shuō)明如下: 表八:v$locked_object視圖字段說(shuō)明 ![]() 根據上述系統視圖,可以編制腳本來(lái)監控數據庫中鎖的狀況。 5.3.1 showlock.sql 第一個(gè)腳本showlock.sql,該腳本通過(guò)連接v$locked_object與all_objects兩視圖,顯示哪些對象被哪些會(huì )話(huà)鎖?。?/p>
第二個(gè)腳本showalllock.sql,該腳本主要顯示當前所有TM、TX鎖的信息;
以下示例均運行在DB2 UDB中,適用所有數據庫版本。首先打開(kāi)三個(gè)命令行窗口(DB2 CLP),其中兩個(gè)(以下用SESS#1、SESS#2表示)以db2admin用戶(hù)連入數據庫,以操作SAMPLE庫中提供的示例表(employee);另一個(gè)(以下用SESS#3表示)以db2admin用戶(hù)連入數據庫,對執行的每一種類(lèi)型的SQL語(yǔ)句監控加鎖的情況;希望讀者通過(guò)這種方式對每一種類(lèi)型的SQL語(yǔ)句監控加鎖的情況。(因為示例篇幅很大,筆者在此就不做了,建議讀者用類(lèi)似方法驗證加鎖情況)
注:db2 +c為不自動(dòng)提交(commit)SQL語(yǔ)句,也可以通過(guò) db2 update command options using c off關(guān)閉自動(dòng)提交(autocommit,缺省是自動(dòng)提交)
總的來(lái)說(shuō),DB2的鎖和Oracle的鎖主要有以下大的區別: 1.Oracle通過(guò)具有意向鎖的多粒度封鎖機制進(jìn)行并發(fā)控制,保證數據的一致性。其DML鎖(數據鎖)分為兩個(gè)層次(粒度):即表級和行級。通常的DML操作在表級獲得的只是意向鎖(RS或RX),其真正的封鎖粒度還是在行級;DB2也是通過(guò)具有意向鎖的多粒度封鎖機制進(jìn)行并發(fā)控制,保證數據的一致性。其DML鎖(數據鎖)分為兩個(gè)層次(粒度):即表級和行級。通常的DML操作在表級獲得的只是意向鎖(IS,SIX或IX),其真正的封鎖粒度也是在行級;另外,在Oracle數據庫中,單純地讀數據(SELECT)并不加鎖,這些都提高了系統的并發(fā)程度,Oracle強調的是能夠"讀"到數據,并且能夠快速的進(jìn)行數據讀取。而DB2的鎖強調的是"讀一致性",進(jìn)行讀數據(SELECT)時(shí)會(huì )根據不同的隔離級別(RR,RS,CS)而分別加S,IS,IS鎖,只有在使用UR隔離級別時(shí)才不加鎖。從而保證不同應用程序和用戶(hù)讀取的數據是一致的。 2. 在支持高并發(fā)度的同時(shí),DB2和Oracle對鎖的操縱機制有所不同:Oracle利用意向鎖及數據行上加鎖標志位等設計技巧,減小了Oracle維護行級鎖的開(kāi)銷(xiāo),使其在數據庫并發(fā)控制方面有著(zhù)一定的優(yōu)勢。而DB2中對每個(gè)鎖會(huì )在鎖的內存(locklist)中申請分配一定字節的內存空間,具體是X鎖64字節內存,S鎖32字節內存(注:DB2 V8之前是X鎖72字節內存而S鎖36字節內存)。 3. Oracle數據庫中不存在鎖升級,而DB2數據庫中當數據庫表中行級鎖的使用超過(guò)locklist*maxlocks會(huì )發(fā)生鎖升級。 4. 在Oracle中當一個(gè)session對表進(jìn)行insert,update,delete時(shí)候,另外一個(gè)session仍然可以從Orace回滾段或者還原表空間中讀取該表的前映象(before image); 而在DB2中當一個(gè)session對表進(jìn)行insert,update,delete時(shí)候,另外一個(gè)session仍然在讀取該表數據時(shí)候會(huì )處于lock wait狀態(tài),除非使用UR隔離級別可以讀取第一個(gè)session的未提交的值;所以Oracle同一時(shí)刻不同的session有讀不一致的現象,而DB2在同一時(shí)刻所有的session都是"讀一致"的。
DB2中關(guān)于并發(fā)控制(鎖)的建議 1.正確調整locklist,maxlocks,dlchktime和locktimeout等和鎖有關(guān)的數據庫配置參數(locktimeout最好不要等于-1)。如果鎖內存不足會(huì )報SQL0912錯誤而影響并發(fā)。 2.寫(xiě)出高效而簡(jiǎn)潔的SQL語(yǔ)句(非常重要)。 3.在業(yè)務(wù)邏輯處理完后盡可能快速commit釋放鎖。 4.對引起鎖等待(SQL0911返回碼68)和死鎖(SQL0911返回碼2)的SQL語(yǔ)句創(chuàng )建最合理的索引(非常重要,盡量創(chuàng )建復合索引和包含索引)。 5.使用 altER TABLE 語(yǔ)句的 LOCKSIZE 參數控制如何在持久基礎上對某個(gè)特定表進(jìn)行鎖定。檢查syscat.tables中locksize字段,盡量在符合業(yè)務(wù)邏輯的情況下,每個(gè)表中該字段為"R"(行級鎖)。 6.根據業(yè)務(wù)邏輯使用正確的隔離級別(RR,RS,CS和UR)。 7.當執行大量更新時(shí),更新之前,在整個(gè)事務(wù)期間鎖定整個(gè)表(使用 SQL LOCK TABLE 語(yǔ)句)。這只使用了一把鎖從而防止其它事務(wù)進(jìn)行這些更新,但是對于其他用戶(hù)它的確減少了數據并發(fā)性。
本文所述觀(guān)點(diǎn)是基于作者個(gè)人對相關(guān)產(chǎn)品的理解,并不代表 IBM 的官方觀(guān)點(diǎn),IBM 不對本文中的信息負責。
| |||||||||||||||||||||||||||||||||||||||||||||||||||||
聯(lián)系客服