Oracle中樂(lè)觀鎖定的四種實(shí)現(xiàn)方式:
- 更新前在應(yīng)用中存儲(chǔ)所要操作行的“前映像”,更新時(shí)使用存儲(chǔ)的舊記錄來(lái)判斷當(dāng)前值是否已經(jīng)改變;
- 使用一個(gè)特殊的列,這個(gè)列由一個(gè)數(shù)據(jù)庫(kù)觸發(fā)器或應(yīng)用程序代碼維護(hù),可以告訴我們記錄的 “版本”;
- 使用一個(gè)校驗(yàn)和或散列值,這是使用原來(lái)的數(shù)據(jù)計(jì)算得出的;
- 使用新增的 Oracle 10g 特性 ORA_ROWSCN 。
1.使用版本列
附加一個(gè)LAST_MODIFIED列,類型可以為 timestamp with time zone default systemstamp(ora 9i以上),這個(gè)類型在Oracle中精度最高,精確到百萬(wàn)分之一秒。
不建議使用觸發(fā)器,因?yàn)殚_(kāi)銷(xiāo)太大,而由DML來(lái)負(fù)責(zé)。
2.使用校驗(yàn)和的樂(lè)觀鎖定
使用一個(gè)虛擬的版本列來(lái)判斷數(shù)據(jù)是否改變。
與使用版本列的做法一樣,我們可以采用同樣的方法使用這些散列值或校驗(yàn)和,只需把從數(shù)據(jù)庫(kù)讀出數(shù)據(jù)時(shí)得到的散列或校驗(yàn)和值與修改數(shù)據(jù)前得到的散列或校驗(yàn)和值進(jìn)行比較。在我們讀出數(shù)據(jù)之后, 但是在修改數(shù)據(jù)之前,如果有人在這段時(shí)間內(nèi)修改了這一行的值,散列值或校驗(yàn)和值往往會(huì)大不相同。
以下為Oracle提供的三種計(jì)算散列值得數(shù)據(jù)包:
OWA_OPT_LOCK.CHECKSUM :這個(gè)方法在 Oracle8i 8.1.5 及以上版本中提供。給定一個(gè)串,其中一個(gè)函數(shù)會(huì)返回一個(gè) 16 位的校驗(yàn)和。給定 ROWID 時(shí),另一個(gè)函數(shù)會(huì)計(jì)算該行的 16 位校驗(yàn)和,而且同時(shí)將這一行鎖定。出現(xiàn)沖突的可能性是 65 536 分之一( 65 536 個(gè)串中有一個(gè)沖突,這是假警報(bào)的最大幾率)。
DBMS_OBFUSCATION_TOOLKIT.MD5 :這個(gè)方法在 Oracle8i 8.1.7 及以上版本中提供。它會(huì)計(jì)算一個(gè) 128 位的消息摘要。沖突的可能性是 3.4028E+38 分之一(非常小)。
DBMS_CRYPTO.HASH :這個(gè)方法在 Oracle 10g Release 1 及以上版本中提供。它能計(jì)算一個(gè)SHA-1 (安全散列算法 1 , Secure Hash Algorithm 1 )或 MD4/MD5 消息摘要。建議你使用 SHA- 1算法。
注意 很多編程語(yǔ)言中都提供了一些散列和校驗(yàn)和函數(shù),所以還可以使用數(shù)據(jù)庫(kù)之外的散列和校驗(yàn)和函數(shù)。
要記住,計(jì)算散列或校驗(yàn)和是一個(gè) CPU 密集型操作(相當(dāng)占用 CPU ),其計(jì)算代價(jià)很昂貴。如果系統(tǒng)上 CPU 是稀有資源,在這種系統(tǒng)上就必須充分考慮到這一點(diǎn)。不過(guò),如果從 “ 網(wǎng)絡(luò)友好性 ” 角度看,這種方法會(huì)比較好,因?yàn)橹恍柙诰W(wǎng)絡(luò)上傳輸相當(dāng)小的散列值,而不是行的完整的前映像和后映像(以便逐列地進(jìn)行比較),所以消耗的資源會(huì)少得多。下面會(huì)使用一個(gè)新的 Oracle 10g 函數(shù) ORA_ROWSCN ,它不僅很小(類似于散列),而且計(jì)算時(shí)不是 CPU 密集的(不會(huì)過(guò)多占用 CP U )。
3.使用ORA_ROWSCN
ORA_ROWSCN 建立在內(nèi)部 Oracle 系統(tǒng)時(shí)鐘( SCN )基礎(chǔ)上。在 Oracle 中,每次提交時(shí), SCN 都會(huì)推進(jìn)(其他情況也可能導(dǎo)致 SCN 推進(jìn),要注意, SCN 只會(huì)推進(jìn),絕對(duì)不會(huì)后退)。這個(gè)概念與前面在獲取數(shù)據(jù) 時(shí)得到 ORA_ROWSCN 的方法是一樣的,更新數(shù)據(jù)時(shí)要驗(yàn)證 SCN 未修改過(guò)。之所以我會(huì)強(qiáng)調(diào)這一點(diǎn)(而不是草草帶過(guò)),原因是除非你創(chuàng)建表時(shí)支持在行級(jí)維ORA_ROWSCN ,否則 Oracle 會(huì)在塊級(jí)維護(hù)。也就是說(shuō),默認(rèn)情況下,一個(gè)塊上的多行會(huì)共享相同的 ORA_ROWSCN 值。如果更新一個(gè)塊上的某一行,而且 這個(gè)塊上還有另外 50 行,那么這些行的 ORA_ROWSCN 也會(huì)推進(jìn)。這往往會(huì)導(dǎo)致許多假警報(bào),你認(rèn)為某一行已經(jīng)修改,但實(shí)際上它并沒(méi)有改動(dòng)。因此,需要注意這一點(diǎn),并了解如何改變這種行為。
要修改這個(gè)特性,只能重新建立表,并啟用 ROWDEPENDENCIES 。
使用樂(lè)觀鎖定還是悲觀鎖定?
那么哪種方法最好呢?根據(jù)我的經(jīng)驗(yàn),悲觀鎖定在 Oracle 中工作得非常好(但是在其他數(shù)據(jù)庫(kù)中可能不是這樣),而且與樂(lè)觀鎖定相比,悲觀鎖定有很多優(yōu)點(diǎn)。不過(guò),它需要與數(shù)據(jù)庫(kù)有一條有狀態(tài)的連接,如客戶 / 服務(wù)器連接,因?yàn)闊o(wú)法跨連接持有鎖。正是因?yàn)檫@一點(diǎn),在當(dāng)前的許多情況下,悲觀鎖定不太現(xiàn)實(shí)。過(guò)去,客戶 / 服務(wù)器應(yīng)用可能只有數(shù)十個(gè)或數(shù)百個(gè)用戶,對(duì)于這些應(yīng)用,悲觀鎖定是我的不二選擇。不過(guò),如今對(duì)大多數(shù)應(yīng)用來(lái)說(shuō),我都建議采用樂(lè)觀并發(fā)控制。要在整個(gè)事務(wù)期間保持連接,這個(gè)代價(jià)太大了,一般無(wú)法承受。
在這些可用的方法中,我使用哪一種呢?我喜歡使用版本列方法,并增加一個(gè)時(shí)間戳列(而不只是一個(gè) NUMBER )。從長(zhǎng)遠(yuǎn)看,這樣能為我提供一個(gè)額外的信息: “ 這一行最后一次更新發(fā)生在什么時(shí)間? ” 所以意義更大。而且與散列或校驗(yàn)和方法相比,計(jì)算的代價(jià)不那么昂貴,在處理 LONG 、 LONG RAW 、 CLOB 、 BLO B和其他非常大的列時(shí),散列或校驗(yàn)和方法可能會(huì)遇到一些問(wèn)題,而版本列方法則沒(méi)有這些問(wèn)題。
如果必須向一個(gè)表增加樂(lè)觀并發(fā)控制,而此時(shí)還在利用悲觀鎖定機(jī)制使用這個(gè)表(例如,客戶 / 服務(wù)器應(yīng)用都在訪問(wèn)這個(gè)表,而且還在通過(guò) Web 訪問(wèn)),我則傾向于選擇 ORA_ROWSCN 方法。這是因?yàn)椋诂F(xiàn)有的遺留應(yīng)用中,可能不希望出現(xiàn)一個(gè)新列,或者即使我們另外增加一步把這個(gè)額外的列隱藏起來(lái)(使用散列等等方式),為了維護(hù)這個(gè)列,可能需要一個(gè)必要的觸發(fā)器,而這個(gè)觸發(fā)器的開(kāi)銷(xiāo)非常大,這是我們無(wú)法承受的。 ORA_ROWSCN技術(shù)沒(méi)有干擾性,而且在這個(gè)方面是輕量級(jí)的(當(dāng)然,這是指我們執(zhí)行表的重建之后)。
散列 / 校驗(yàn)和方法在數(shù)據(jù)庫(kù)獨(dú)立性方面很不錯(cuò),特別是如果我們?cè)跀?shù)據(jù)庫(kù)之外計(jì)算散列或校驗(yàn)和,則更是如此。不過(guò),如果在中間層而不是在數(shù)據(jù)庫(kù)中執(zhí)行計(jì)算,從 CPU 使用和網(wǎng)絡(luò)傳輸方面來(lái)看,就會(huì)帶來(lái)更大的資源使用開(kāi)銷(xiāo)。