这里主要对前面触发器死锁那篇文章的具体化说明:
首先建立临时表(session级)。
create global temporary table tilog(
rid urowid,
lock_flag,
update_flag,
tmstmp timestamp(6)) on commit preserve rows;
由于是session级的,且触发器在内部不断嵌套触发过程中都是处于同一个session期。所以该临时表适合于此种情况,而且适合于多并发的环境。
解决思路:
触发器为update T表的时候触发。触发级别为for each row;
假设客户编号C下面有A1、A2、A3三个帐号。
步骤1:当对T表执行update语句的时候,即已经锁定了对应update所对应的行A1。此时,把对应的更新的这些行的rowid,锁定标识(lock_flag=1),更新标识(update_flag=0),时间戳(cast(sysdate as timestamp))存放到临时表tilog。
然后根据客户编号,找到对应的其他账户编号(A2、A3)。并批量放入到数组中(bulk collect into)。
步骤2:从数组中循环取出一个账户编号A2,并从tilog中查找该记录的锁定情况和更新情况。这里对于A2,tilog中尚未存在该记录。因此判断为无锁定和无更新。直接进入更新语句update。
步骤3:此时就又触发了该触发器,往tilog中插入A2的信息(rowid,lock_flag=1,update_flag=0,timestamp)。然后再次由客户编号得到A2外的两个账户编号A1和A3。然后从tilog中查找A1对应的锁定和更新的信息。由步骤1可得到对应A1目前处于锁定状态。因此不能执行update(否则造成死锁)。因而循环到A3。A3的更新跟步骤相同。往tilog中插入对应锁定和更新的信息。
步骤4:A3触发后进入触发器的第三次触发。此时根据A3从tilog中找到的A1和A2都是锁定的记录了。于是第三次触发完成,跳出,执行commit,解除行锁定标识并更新A3记录的更新标识为1(标识该记录已更新)。
步骤5:A3完成后,步骤3中对A2也进行commit及更新标识置1。然后回到步骤1。再次进入A3,从tilog中取出更新标识和锁定标识。由于此时更新标识为1,即已更新。不再对该记录进行处理。
步骤6:再次回到步骤1,更新A1记录。此次触发全过程完成。更新行锁定标识为0及行更新标识为1。
下面是一个简单的更新流程跟踪:
c_fundacco=680760000029 islocked=0 isupdated=0
update->680760000029
c_fundacco=681850000091 islocked=1 isupdated=0
681850000091 is LOCKED!
c_fundacco=681850000079 islocked=0 isupdated=0
update->681850000079
c_fundacco=681850000091 islocked=1 isupdated=0
681850000091 is LOCKED!
c_fundacco=680760000029 islocked=1 isupdated=0
680760000029 is LOCKED!
c_fundacco=681000000027 islocked=0 isupdated=0
update->681000000027
c_fundacco=681850000091 islocked=1 isupdated=0
681850000091 is LOCKED!
c_fundacco=680760000029 islocked=1 isupdated=0
680760000029 is LOCKED!
c_fundacco=681850000079 islocked=1 isupdated=0
681850000079 is LOCKED!
commit[u]-unlock-updated->681000000027
commit[u]-unlock-updated->681850000079
c_fundacco=681000000027 islocked=0 isupdated=1
681000000027 is UPDATED!
commit[u]-unlock-updated->680760000029
c_fundacco=681850000079 islocked=0 isupdated=1
681850000079 is UPDATED!
c_fundacco=681000000027 islocked=0 isupdated=1
681000000027 is UPDATED!