?? -
字號:
作者:jaklin
email: cjl-ccy@china.com
日期:2001-7-24 12:58:11
數據庫應用中并發控制若干實現途徑
張在建
01-7-10 下午 01:57:05
一.引言
并發控制是指在多用戶的環境下,對數據庫進行并發操作進行規范的機制。其目的是為了避免對數據的丟失修改、讀臟數據與不可重復讀等,從而保證數據的正確性與一致性。并發控制在多用戶的模式下是十分重要的,但這一點經常被一些數據庫開發人員忽視,而且因為并發控制的層次和類型非常豐富,有時使人在選擇時比較迷惑,不清楚衡量并發控制層次選擇的原則和途徑。本文將從一個例子入手,結合數據庫理論的相關知識,對數據庫應用中并發控制的途徑、方法做出一個較全面的總結,希望能幫助讀者找到合理的并發控制方法。
二.一個并發控制失敗的例子
為了更好的理解并發控制的概念,我們先重復一個經常列舉的例子。先給出銀行數據庫中一個經過簡化的帳戶表(account)信息的數據字典定義,以后我們都將會以此表作為示例。
列名稱 列代碼 列類型
帳戶號 Id(鍵值列) Char(10)
戶主 Uname Char(10)
存入金額 Mdeposit Currency
支出金額 Mpayout Currency
存款余額 Mbalance Currency
這個例子是在客戶程序與服務器端數據庫的會話過程中產生的:某戶主代表在銀行前臺取款2,000元,銀行出納查詢用戶的存款信息顯示銀行存款余額20,000元;正在這時,另一銀行帳戶轉帳支票支付該帳戶5,000元,機器查詢也得到當前用戶存款20,000元,這時銀行的出納員看到用戶存款超過了取款額,就支付了客戶2,000元并將用戶存款改為18,000元,然后銀行的另一名操作員根據支票,將匯入的5,000元加上,把用戶的余額改為25,000元。很明顯銀行將會損失2000元,因為另一個出納員所做的修改被覆蓋了。
這是由于對并發操作控制的失敗造成的,由于沒有對兩個并發操作進行合理的隔離,對數據進行合理的鎖定,導致經出納員查詢所得到的客戶端數據集與數據庫的數據出現不一致,結果便產生了丟失修改。
三.數據庫并發控制理論基礎
在此對并發控制中經常用到的概念略做解釋,具體內容請讀者查資料。事務是數據庫中一個重要概念,它是一系列要么都做,要么都不做的程序集合,是數據庫并發控制的單位。事務并發控制不當的話,可以產生丟失修改、讀臟數據、不可重復讀等數據不一致。但在應用中為了并發度的提高,可以容忍一些這樣的不一致,例如大多數業務邏輯經適當的調整以后是可以容忍不可重復讀的。當今流行的關系數據庫系統(如oracle,sql server等)是通過事務隔離級別(TRANSACTION ISOLATION LEVEL)與封鎖機制來定義并發控制所要達到的目標的,根據其提供的協議,我們可以得到幾乎任何類型的合理的并發控制方式。例如,Microsoft sql server系統中有四種鎖:共享鎖,排它鎖,意向鎖(又分為共享意向鎖,排它意向鎖,共享意向排它鎖),修改鎖。各種鎖之間有確定的相容關系。有四種事務隔離級別:未提交讀、提交讀、可重復讀、串行化讀,不同的隔離級別所規定的封鎖協議不同。這一部分內容非常之豐富,足足可以寫一本書,篇幅所限,不擬細述,有關內容請查閱數據庫理論方面的教材。
封鎖類型與隔離級別如此之豐富,那么選擇時到底本著一個什么原則呢?那就是數據一致性要求與并發度兩個方面。例如四種隔離級別數據一致性依次升高,但并發度依次降低,一般系統默認的隔離級別是提交讀。一般來說這可以滿足應用的要求了,但這種隔離級別不能避免不可重復讀的現象,就是說在你瀏覽數據庫記錄的期間,不同時間讀的同一條記錄可以有不同的內容。有時需要動態的通過sql語句來改變其封鎖狀態或者隔離級別。
四.并發控制技術的實現途徑
并發控制的實現途徑有多種,如果DBMS支持的話,當然最好是運用其自身的并發控制能力。如果系統不能提供這樣的功能,可以借助開發工具的支持,還可以考慮調整數據庫應用程序,而且有的時候可以通過調整工作模式來避開這種會影響效率的并發操作。筆者對各種策略做了一個總結,主要有一下幾點:
(一)調整工作模式,修改應用程序,避免不必要的并發。
這在某些情況下是可行的,例如規定錄入人員只能修改自己所創造的記錄,那么就不會出現并發操作中的各種錯誤,因為這時各個不同的用戶所能更新的記錄不會發生重合。這種情況下,需要在數據庫表中增加用戶列。在用戶瀏覽記錄時,將用戶列作為一個過濾條件,對應用程序的sql語句做相應的調整。但這種策略的作用有限,因為在大量情況下,并發控制不可避免。
(二)借助于DBMS的功能。
大型關系系統都有比較好的并發控制功能。例如可以采用更新游標、顯式加鎖、更改事務隔離級別等等。當然在其使用方面有很多注意的技巧,如:(1)事務定義最好不要包含客戶交互部分。(2)只有在數據一致性要求特別嚴格,但并發度要求不高的時候采用可重復讀與可串行讀的隔離級別。(3)在同一個事務當中,要適當根據需要來變更數據的鎖定級別,但一般情況下不要用TABLOCK這樣粗粒度的封鎖。(4)不同事務之間可以根據并發度的需要來顯式設定隔離級別。(5)在包含客戶交互的操作中使用游標,并盡可能縮短交互時間。
我們看一個informix數據庫中采用更新游標的例子。定義更新游標語法:DECLARE CURSOR-name CURSOR FOR SELECT-statement FOR UPDATE[OF column-list]。更新游標在完成數據的瀏覽和修改時,要對當前的記錄隱式加鎖,注意更新游標只對可更新視圖有效。為了提高并發度,經常要結合滾動游標來使用,滾動游標定義方法:DECLARE CURSORname SCROLL CURSOR[with hold] FOR SELECTstatement),不過滾動游標不對當前的記錄加鎖。
下段代碼完成客戶對帳戶內容的瀏覽和修改,代碼采用informix的esql/c(以c語言作為宿主語言)來編寫,展示了更新游標的使用方法:
$DECLARE mycurs CURSOR FOR SELECT Mdeposit,Mpayout,Mbalance FROM acount FOR UPDATE; //定義更新游標
$OPEN mycurs; //打開游標
for(;;)
{$FETCH mycurs INTO $ Mdeposit,$Mpayout,$Mbalance;// 從游標中讀記錄
if sqlcode=SQLNOTFOUND then exit;//如果記錄取完,則退出循環
….//顯示記錄內容給用戶
….//如果用戶決定要修改記錄,則繼續執行
$UPDATE acount SET (Mdeposit,Mpayout,Mbalance)=
($Mdeposit,$Mpayout,$Mbalance) WHERE CURRENT of mycurs;//更新數值
}
帶有FOR UPDATE的游標語句有加鎖功能??瓷厦娴拇a,在進行FETCH操作以后,游標所指向的當前記錄被加共享鎖,當用戶決定要修改時,將該記錄上的鎖提升為排它鎖。那么此時其它用戶不可以更新此記錄。這種方法有個缺點,就是即使用戶不對當前記錄進行修改,也要對當前的記錄加鎖,影響了并發度,那么此時可以采用的一個方法是:(1)定義一個滾動游標來完成查詢;(2)獲取游標的一個記錄,顯示給用戶;(2)用戶瀏覽記錄,直到要修改或者刪除的記錄;(3)當用戶選擇修改一個記錄時,為用戶想要修改的記錄定義一個更新游標(4)使用更新游標獲取記錄,并重新顯示鎖定的記錄;(5)更新這個記錄。那么上面的程序就可以更改為:
$DECLARE mycurs SCROLL CURSOR for SELECT Id, Mdeposit,Mpayout,Mbalance FROM acount;//定義滾動游標
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -