?
? ????? PHP ??? ???? ??? ?? ??
摘要: |
本文簡(jiǎn)單闡述了一般數(shù)據(jù)庫鎖,以O(shè)RACLE為例的加鎖問題,提出了解決ORACLE的數(shù)據(jù)行加鎖及并發(fā)性處理的一般辦法.并給出了用PB編程兩種實(shí)現(xiàn)方法. |
關(guān)鍵詞: |
ORACLE,加鎖,解鎖,行鎖, PowerBuilder |
數(shù)據(jù)的完整性和并發(fā)性:
A0事務(wù) |
|
B0事務(wù) |
||
加 鎖 |
|
加 鎖 |
||
|
|
|
|
|
解 鎖 |
解 鎖 |
|||
A1事務(wù) |
|
B1事務(wù) |
并發(fā)性和一致性
A 數(shù)據(jù)鎖,用于封鎖表或表中的指定行.屬于數(shù)據(jù)操縱鎖,
B 字典鎖,用于保護(hù)數(shù)據(jù)庫對(duì)象(表,索引)的創(chuàng)建維護(hù).屬于數(shù)據(jù)定義鎖
C 內(nèi)部鎖。用于保護(hù)數(shù)據(jù)庫的內(nèi)部結(jié)構(gòu).
正常情況下,為完成數(shù)據(jù)操縱,一般需要兩種級(jí)別的封鎖方式,封鎖整表或行封。通常只在熱備份數(shù)據(jù),時(shí)候,需要對(duì)整表進(jìn)行鎖定. 而應(yīng)用程序的事務(wù)處理通常是數(shù)據(jù)行的數(shù)據(jù)操縱語句:
INSERT, |
UPDATE, |
DELETE, |
SELECT (FOR UPDATE) |
以上操作在執(zhí)行過程中,數(shù)據(jù)庫都會(huì)對(duì)相應(yīng)的行做數(shù)據(jù)行封鎖.
一般情況下,只要行封事務(wù)不放棄封鎖, 此行就不可能被其他任何用戶的事務(wù)修改(部分語句,如SELECT仍然允許不加鎖地讀出)。只有當(dāng)數(shù)據(jù)庫重新啟動(dòng),數(shù)據(jù)表空間離線/在線處理,當(dāng)事務(wù)正常提交、回滾時(shí),行封事務(wù)才能被自動(dòng)解除封鎖. (通常 ROLLBACK ,COMMIT 可以自動(dòng)解除行封鎖。)在表上的數(shù)據(jù)鎖的高低級(jí)別為:
表專有 |
共享行專有 |
共享 |
行專有 |
行共享 |
Exclusive |
ShareRoweXclusive |
Share |
RoweXclusive |
RowShare |
EX |
SRX |
S |
RX |
RS |
簡(jiǎn)單的SELECT 語句無任何鎖,INSERT, UPDATE, DELETE為行專有(RX),允許在操作中多次修改這些行, 提交或回滾后自動(dòng)解除封鎖,
SELECT (FOR UPDATE )為專有行的數(shù)據(jù)鎖.在應(yīng)用程序中使用最多.也是通常對(duì)數(shù)據(jù)行進(jìn)行控制的簡(jiǎn)單有效的方法。例如:在Windows95下,同時(shí)分別用兩個(gè)以上SQL*PLUS,以相同的用戶聯(lián)接Oracle Server相同的實(shí)例,在其中一個(gè)窗口下做Update/Delete中任意操作,只要沒有提交,均不影響其它窗口的不加鎖查詢,但是不可以再其它窗口下對(duì)同一條記錄作Update/Delete 操作,Insert沒有提交以前,其它操作均無效。只有當(dāng)一方事務(wù)完成(提交/回滾)以后立即釋放相應(yīng)資源,另一方申請(qǐng)的事務(wù)才能成功響應(yīng)。
在實(shí)際應(yīng)用中,可能由于一次事務(wù)提交的數(shù)據(jù)太多,造成數(shù)據(jù)加鎖時(shí)間太長(zhǎng),破壞了數(shù)據(jù)的并發(fā)共享特性。如果事務(wù)獨(dú)占了相應(yīng)的資源,沒有得到釋放,而另一個(gè)事務(wù)占有該事務(wù)的下一個(gè)資源,就有可能造成數(shù)據(jù)庫應(yīng)用系統(tǒng)死鎖。另外,如果在加鎖過程中,出現(xiàn)網(wǎng)絡(luò)中斷,客戶端掉電等突發(fā)問題,被加鎖的資源無法解鎖,其它應(yīng)用程序?qū)o法繼續(xù)獲得已經(jīng)被加鎖的資源,也可以出現(xiàn)死鎖. 通常只有DBA才能取消事務(wù)或通過數(shù)據(jù)表空間的在線/離線操作解除加鎖事務(wù)(通常大型應(yīng)用系統(tǒng)這樣做有一定的風(fēng)險(xiǎn))。如果在程序應(yīng)用中經(jīng)常出現(xiàn)這樣的問題,則造成應(yīng)用數(shù)據(jù)庫應(yīng)用系統(tǒng)無法正常工作。雖然這樣的情況在C/S程序的開發(fā)/應(yīng)用中并不經(jīng)常出現(xiàn),但是在應(yīng)用程序設(shè)計(jì)中都是應(yīng)該嚴(yán)格考慮的。
A 在客戶端編程方法:在客戶端的編程處理的方法最簡(jiǎn)單,調(diào)試也很直觀、方便。但是這種方法僅僅在中、小型網(wǎng)絡(luò)中可以使用。因?yàn)楫?dāng)網(wǎng)絡(luò)用戶數(shù)量增加,或應(yīng)用程序在客戶端應(yīng)用過程中意外中斷(死機(jī),退出),則可能造成數(shù)據(jù)行的死鎖。其他用戶無法正確獨(dú)占加鎖。但是實(shí)際應(yīng)用表明此種方法在網(wǎng)絡(luò)自身可靠性好、用戶數(shù)量不大時(shí)經(jīng)常被采納
。下面是用POWERBUILDER 與 ORACLE的數(shù)據(jù)聯(lián)結(jié)處理實(shí)例。
STRING ls_goodscode
INTEGER li_stroe_qty
ls_goodscode = sle_goodscode.text
SELECT QTY
INTO li_store_qty
FROM AMS.TAB_STORE_GOODSINFO
WHERE GOODS_NO = SUBSTR(:ls_goodscode , 1, 13)
AND SERIAL_NO = SUBSTR(:ls_goodscode , 14, 4)
FOR UPDATE NOWAITE ; // 對(duì)數(shù)據(jù)加鎖,不等待。
if SQLCA.SQLCODE = 0 then // if _01
if li_store_qty >=2 then // if _02
UPDATE AMS.TAB_STORE_GOODSINFO
SET QTY = :li_store_qty - 2
WHERE GOODS_NO = SUBSTR(:ls_goodscode , 1, 13)
AND SERIAL_NO = SUBSTR(:ls_goodscode , 14, 4);
if SQLCA.SQLCODE = 0 then
COMMIT ; // 成功提交
else
ROLLBACK ;// 及時(shí)回滾解鎖
messagebox("系統(tǒng)提示", "庫存數(shù)量已經(jīng)不足!");
end if
else
ROLLBACK;
messagebox("系統(tǒng)提示", "庫存數(shù)量已經(jīng)不足!");
end if // end if 02
else //其中 ORA-00054: resource busy and acquire with NOWAIT specified
ROLLBACK ;// 及時(shí)回滾解鎖
messagebox("系統(tǒng)提示", "數(shù)據(jù)讀入有誤,請(qǐng)稍候再試!",stopsign! ok!);
end if // end if 01
//同時(shí)在當(dāng)前的FROM WINDOW中的CLOSEQUERY中顯示寫明ROLLBACK ;
設(shè)計(jì)說明:當(dāng)?shù)谝痪銼ELECT完成以后,如果數(shù)據(jù)加鎖正確,則可以繼續(xù)工作,但是此時(shí)可能數(shù)據(jù)被別的應(yīng)用程序控制,則返回Oracle的錯(cuò)誤代碼:
ORA-00054說明共享沖突。此句可以用一個(gè)有限的循環(huán)控制,但是一定要防止數(shù)據(jù)死鎖。同時(shí)為了保證萬無一失,在當(dāng)前FORM的關(guān)閉事件中顯示寫明回滾(ROLLBACK)。
B 在客戶端按傳輸(Transaction )方法處理:將要提交的所有SQL任務(wù)作為多個(gè)子串傳送到服務(wù)器端,用動(dòng)態(tài)SQL方法在客戶端一次傳輸完成后,在服務(wù)器端一次執(zhí)行完成,只有當(dāng)事務(wù)執(zhí)行正確,才能提交, 否則回滾。這樣在執(zhí)行多個(gè)表操作提交時(shí),數(shù)據(jù)執(zhí)行加鎖的時(shí)間影響將大大減小。這樣極大程度的緩解了多表共享數(shù)據(jù)的加鎖問題。好處是不需要服務(wù)器端的調(diào)試,但是,編程調(diào)試過程復(fù)雜。但是實(shí)際效果與第一種方法基本一致。
C 存儲(chǔ)過程方法:這種方法是直接調(diào)用服務(wù)器的存儲(chǔ)過程,所有的對(duì)Client端程序代碼可以大大減少,數(shù)據(jù)一致性,并發(fā)性,安全性都能得到保證。客戶端的異常通常不會(huì)影起數(shù)據(jù)的死鎖。
下面是用POWERBUILDER 與 ORACLE的數(shù)據(jù)聯(lián)結(jié)處理實(shí)例。
STRING ls_goodscode
INTEGER li_stroe_qty
ls_goodscode = st.text
if len(ls_goodscode) = 17 then
DECLARE pstore_goods PROCEDURE FOR pd_store_goods(:ls_goodscode ) ;
// 從庫存數(shù)中減去 2
execute pstore_goods;
if SQLCA.SLQCODE < 0 then
ROLLBACK ;
messagebox("系統(tǒng)提示", "數(shù)據(jù)操作有誤,請(qǐng)稍候再試!",stopsign! ok!);
end if
else
messagebox("系統(tǒng)提示", "數(shù)據(jù)輸入有誤!",information!, ok!);
end if
//同時(shí)在當(dāng)前的FROM WINDOW中的CLOSEQUERY中顯示寫明ROLLBACK ;
在服務(wù)器端
-- procedure pd_store_goods
create or replace procedure pd_store_goods ( ls_goodscode in varchar2 )
IS
ln_store_qty number(10) ;
begin
SELECT nvl(QTY, -1)
INTO ln_store_qty
FROM AMS.TAB_STORE_GOODS_INFO
WHERE GOODS_NO = SUBSTR(ls_goodscode , 1, 13)
AND SERIAL_NO = SUBSTR(ls_goodscode , 14, 4)
FOR UPDATE NOWAIT;
if ln_store_qty >= 2 then
UPDATE AMS.TAB_STORE_GOODS_INFO
SET QTY = ln_store_qty - 2
WHERE GOODS_NO = SUBSTR(ls_goodscode , 1, 13)
AND SERIAL_NO = SUBSTR(ls_goodscode , 14, 4);
COMMIT ;
else
ROLLBACK ;
RAISE_APPLICATION_ERROR(-200002, 'store not enougth!');
end if;
EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-200001, 'store not SCUESSCULL!');
end ;
設(shè)計(jì)說明
:首先在SQL*PLUS中運(yùn)行存儲(chǔ)過程,在PB的程序端調(diào)用。只有當(dāng)SELECT語句正常加鎖后, UPDATE語句才可以正常執(zhí)行。在由此可見在客戶端不知道存儲(chǔ)過程運(yùn)行的內(nèi)容,調(diào)試/維護(hù)工作較麻煩。但是,因?yàn)樗械倪\(yùn)行都在服務(wù)器端,基本可以忽略除服務(wù)器端以外的所有問題,如網(wǎng)絡(luò)的通暢,客戶端異常等等。實(shí)際應(yīng)用中對(duì)大型繁重的數(shù)據(jù)處理大多采用這樣的方法。
在Oracle的連接中,需要解釋的是數(shù)據(jù)行加鎖在同一個(gè)事務(wù)中是不可見的,但是執(zhí)行權(quán)限級(jí)別順序?yàn)椋篋elete >Update >Insert >Select.
本文以O(shè)racle為例,說明在數(shù)據(jù)庫處理過程中,在數(shù)據(jù)庫設(shè)計(jì)上,要考慮數(shù)據(jù)的數(shù)據(jù)的完整性,在數(shù)據(jù)庫的應(yīng)用設(shè)計(jì)上必須著重事務(wù)的并發(fā)性和數(shù)據(jù)的一致性。對(duì)于數(shù)據(jù)行鎖要謹(jǐn)慎對(duì)待。
(以上程序在廣東大亞灣核電物資管理系統(tǒng)中運(yùn)行通過)
(全文完)
聯(lián)系地址: 610054 成都市 建設(shè)北路 電子科技大學(xué) 97S4 張衛(wèi)華
Email : hamlet@163.net