今天一个需求让我把很久没有琢磨的内容 “锁”又重新拾起来:
JBoss环境,并行的产生DML,对某张表RAW_DATA进行INSERT/UPDATE,
每个操作都会产生一个序列号,要求保证每个session的DML都是按顺序被记录,
以保证每个批次的操作可以被追踪,比如session1 产生了3个insert,1个update
另一个session产生了1个insert,5个update,他们的序号必须是:
SE1.insert1(1),SE1.insert2(2),SE1.insert3(3),SE1.update1(4),
SE2.insert1(5),SE2.update1(6),SE2.update2(7),SE2.update3(8)

于是考虑先对表RAW_DATA加锁,方法如下:
1. lock table RAW_DATA in share mode;
2. select * from RAW_DATA for update;
3. lock table RAW_DATA in share row exclusive mode;

一下测试一下每种情况:
情况1:
Session1(简称S1):
lock table t1 in share mode;
S2:
lock table t1 in share mode;
S1:
insert into t1 values (1,1,'a');
S2:
insert into t1 values (2,2,'a');
这时S1得到错误信息: "ORA-00060: deadlock detected while waiting for resource"
说明S1,S2产生了死锁,即S1的insert等待S2的TM锁释放,同时S2的insert也等待S1的TM锁释放,
持有并等待,符合死锁的描述。
我们接下来再看看LOCK TABLE IN SHARE MODE的锁类型:  SHARE 类型的锁
这种锁只针对其他类型的排它锁,如Row Exclusive,或者Share Row Exclusive锁,
但两个share mode的锁不互相排斥,所以当接下来执行Row Exclusive操作时,出现死锁。
SELECT   TYPE || '-' || id1 || '-' || id2 "resource", SID,
         DECODE (lmode,
                 0, 'None',
                 1, 'Null',
                 2, 'Row share',
                 3, 'Row Exclusive',
                 4, 'Share',
                 5, 'Share Row Exculsive',
                 6, 'Exculsive'
                ) lock_type,
         DECODE (request,
                 0, 'None',
                 1, 'Null',
                 2, 'Row share',
                 3, 'Row Exclusive',
                 4, 'Share',
                 5, 'Share Row Exculsive',
                 6, 'Exculsive'
                ) request,
         ctime, BLOCK
    FROM v$lock
   WHERE TYPE IN ('TX', 'TM')
ORDER BY "resource", ctime DESC

resource                    SID LOCK_TYPE           REQUEST                  CTIME      BLOCK
-------------------- ---------- ------------------- ------------------- ---------- ----------
TM-55859-0                   32 Share               Share Row Exculsive        246          0
TM-60949-0                   32 Share Row Exculsive None                       626          0

情况2: 使用select * from t for update;
S1:
select * from t for update;
S2:
select * from t for update;
这时被block住了,看一下锁类型: 是Share Row Exculsive
(这与使用lock table in share row exclusive方式产生的锁一样)

resource                    SID LOCK_TYPE           REQUEST                  CTIME      BLOCK
-------------------- ---------- ------------------- ------------------- ---------- ----------
TM-60949-0                   32 Row Exclusive       None                        27          0
TX-65579-2637                32 Exculsive           None                        27          0

但使用select for update时有下面两个特点:
   - 如果是空表,不互相锁,如S1执行select for update, 这时S2也可以执行select for update;
   - 可以配合wait <timeout seconds>使用,如

情况3: 使用lock table t in share row exclusive mode; 这种情况与情况2类似,
可以block 相同的操作。只是lock type不同,这时是个SHARE ROW EXCLUSIVE模式的锁。
S1:
lock table t in share row exclusive mode;

resource               SID LOCK_TYPE           REQUEST                  CTIME      BLOCK
--------------- ---------- ------------------- ------------------- ---------- ----------
TM-60949-0              46 Share Row Exculsive None                        15          0


以上3种情况都会对这张大表上锁,不但会block所有其他的Operation
而且在加锁和释放的过程,需要一定的开销。所以可以考虑使用如下机制:
1. 创建一张小表
create table t_lock (group_id number, max_sq number);
insert into t_lock values (1,1);
insert into t_lock values (2,4);
insert into t_lock values (3,2);
commit;
2. 一个session进来对小表取得锁:
col max_sq new_value v_max_sq print
select max_sq from t_lock where group_id=1 for update wait 5;
insert into t values (&v_max_sq,'TEST','TEST');
commit;
3. 此时如果另一个session执行上面的操作,如果前面有锁没释放,就会被block住,
timeout时间为5秒。
select max_sq from t_lock where group_id=1 for update wait 5;
ERROR at line 1:
ORA-30006: resource busy; acquire with WAIT timeout expired
当抛出错误以后,JBOSS就会通过消息机制,停止目前的process, 这样后面的操作就不会被执行了。

这样处理的好处在于,通过对小型表的锁,来控制对大表的DML操作的顺序执行,
可以减少对大表的影响,减少冲突,提高性能。另外小表中包含的控制信息如sequence又可以在
select for update的时候被取出来传给后面的DML。