©
本文档使用
php.cn手册 发布
摘要: |
本文简单阐述了一般数据库锁,以ORACLE为例的加锁问题,提出了解决ORACLE的数据行加锁及并发性处理的一般办法.并给出了用PB编程两种实现方法. |
关键词: |
ORACLE,加锁,解锁,行锁, PowerBuilder |
数据的完整性和并发性:
A0事务 |
|
B0事务 |
||
加 锁 |
|
加 锁 |
||
|
|
|
|
|
解 锁 |
解 锁 |
|||
A1事务 |
|
B1事务 |
并发性和一致性
A 数据锁,用于封锁表或表中的指定行.属于数据操纵锁,
B 字典锁,用于保护数据库对象(表,索引)的创建维护.属于数据定义锁
C 内部锁。用于保护数据库的内部结构.
正常情况下,为完成数据操纵,一般需要两种级别的封锁方式,封锁整表或行封。通常只在热备份数据,时候,需要对整表进行锁定. 而应用程序的事务处理通常是数据行的数据操纵语句:
INSERT, |
UPDATE, |
DELETE, |
SELECT (FOR UPDATE) |
以上操作在执行过程中,数据库都会对相应的行做数据行封锁.
一般情况下,只要行封事务不放弃封锁, 此行就不可能被其他任何用户的事务修改(部分语句,如SELECT仍然允许不加锁地读出)。只有当数据库重新启动,数据表空间离线/在线处理,当事务正常提交、回滚时,行封事务才能被自动解除封锁. (通常 ROLLBACK ,COMMIT 可以自动解除行封锁。)在表上的数据锁的高低级别为:
表专有 |
共享行专有 |
共享 |
行专有 |
行共享 |
Exclusive |
ShareRoweXclusive |
Share |
RoweXclusive |
RowShare |
EX |
SRX |
S |
RX |
RS |
简单的SELECT 语句无任何锁,INSERT, UPDATE, DELETE为行专有(RX),允许在操作中多次修改这些行, 提交或回滚后自动解除封锁,
SELECT (FOR UPDATE )为专有行的数据锁.在应用程序中使用最多.也是通常对数据行进行控制的简单有效的方法。例如:在Windows95下,同时分别用两个以上SQL*PLUS,以相同的用户联接Oracle Server相同的实例,在其中一个窗口下做Update/Delete中任意操作,只要没有提交,均不影响其它窗口的不加锁查询,但是不可以再其它窗口下对同一条记录作Update/Delete 操作,Insert没有提交以前,其它操作均无效。只有当一方事务完成(提交/回滚)以后立即释放相应资源,另一方申请的事务才能成功响应。
在实际应用中,可能由于一次事务提交的数据太多,造成数据加锁时间太长,破坏了数据的并发共享特性。如果事务独占了相应的资源,没有得到释放,而另一个事务占有该事务的下一个资源,就有可能造成数据库应用系统死锁。另外,如果在加锁过程中,出现网络中断,客户端掉电等突发问题,被加锁的资源无法解锁,其它应用程序将无法继续获得已经被加锁的资源,也可以出现死锁. 通常只有DBA才能取消事务或通过数据表空间的在线/离线操作解除加锁事务(通常大型应用系统这样做有一定的风险)。如果在程序应用中经常出现这样的问题,则造成应用数据库应用系统无法正常工作。虽然这样的情况在C/S程序的开发/应用中并不经常出现,但是在应用程序设计中都是应该严格考虑的。
A 在客户端编程方法:在客户端的编程处理的方法最简单,调试也很直观、方便。但是这种方法仅仅在中、小型网络中可以使用。因为当网络用户数量增加,或应用程序在客户端应用过程中意外中断(死机,退出),则可能造成数据行的死锁。其他用户无法正确独占加锁。但是实际应用表明此种方法在网络自身可靠性好、用户数量不大时经常被采纳
。下面是用POWERBUILDER 与 ORACLE的数据联结处理实例。
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 ; // 对数据加锁,不等待。
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 ;// 及时回滚解锁
messagebox("系统提示", "库存数量已经不足!");
end if
else
ROLLBACK;
messagebox("系统提示", "库存数量已经不足!");
end if // end if 02
else //其中 ORA-00054: resource busy and acquire with NOWAIT specified
ROLLBACK ;// 及时回滚解锁
messagebox("系统提示", "数据读入有误,请稍候再试!",stopsign! ok!);
end if // end if 01
//同时在当前的FROM WINDOW中的CLOSEQUERY中显示写明ROLLBACK ;
设计说明:当第一句SELECT完成以后,如果数据加锁正确,则可以继续工作,但是此时可能数据被别的应用程序控制,则返回Oracle的错误代码:
ORA-00054说明共享冲突。此句可以用一个有限的循环控制,但是一定要防止数据死锁。同时为了保证万无一失,在当前FORM的关闭事件中显示写明回滚(ROLLBACK)。
B 在客户端按传输(Transaction )方法处理:将要提交的所有SQL任务作为多个子串传送到服务器端,用动态SQL方法在客户端一次传输完成后,在服务器端一次执行完成,只有当事务执行正确,才能提交, 否则回滚。这样在执行多个表操作提交时,数据执行加锁的时间影响将大大减小。这样极大程度的缓解了多表共享数据的加锁问题。好处是不需要服务器端的调试,但是,编程调试过程复杂。但是实际效果与第一种方法基本一致。
C 存储过程方法:这种方法是直接调用服务器的存储过程,所有的对Client端程序代码可以大大减少,数据一致性,并发性,安全性都能得到保证。客户端的异常通常不会影起数据的死锁。
下面是用POWERBUILDER 与 ORACLE的数据联结处理实例。
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 ) ;
// 从库存数中减去 2
execute pstore_goods;
if SQLCA.SLQCODE < 0 then
ROLLBACK ;
messagebox("系统提示", "数据操作有误,请稍候再试!",stopsign! ok!);
end if
else
messagebox("系统提示", "数据输入有误!",information!, ok!);
end if
//同时在当前的FROM WINDOW中的CLOSEQUERY中显示写明ROLLBACK ;
在服务器端
-- 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 ;
设计说明
:首先在SQL*PLUS中运行存储过程,在PB的程序端调用。只有当SELECT语句正常加锁后, UPDATE语句才可以正常执行。在由此可见在客户端不知道存储过程运行的内容,调试/维护工作较麻烦。但是,因为所有的运行都在服务器端,基本可以忽略除服务器端以外的所有问题,如网络的通畅,客户端异常等等。实际应用中对大型繁重的数据处理大多采用这样的方法。
在Oracle的连接中,需要解释的是数据行加锁在同一个事务中是不可见的,但是执行权限级别顺序为:Delete >Update >Insert >Select.
本文以Oracle为例,说明在数据库处理过程中,在数据库设计上,要考虑数据的数据的完整性,在数据库的应用设计上必须着重事务的并发性和数据的一致性。对于数据行锁要谨慎对待。
(以上程序在广东大亚湾核电物资管理系统中运行通过)
(全文完)
联系地址: 610054 成都市 建设北路 电子科技大学 97S4 张卫华
Email : hamlet@163.net