我申请这个blog是为了督促自己,把自己平时的一些想法和思考结果保留下来。 本博客所有内容均为原创,如有转载请注明作者和出处

无法利用主外键时控制主子表的并发访问(三)

上一篇 / 下一篇  2008-01-28 17:48:49 / 个人分类:ORACLE

主子表的并发控制,一般是通过主外键的约束来实现。通过这种方式,保证子表插入数据的情况下,主表不能将记录删除。

目前碰到一个问题,无法通过主外键的约束来控制并发访问。

这篇描述无法利用主外键时,如果进行并发控制。

无法利用主外键时控制主子表的并发访问(一):http://yangtingkun.itpub.net/post/468/453295

无法利用主外键时控制主子表的并发访问(二):http://yangtingkun.itpub.net/post/468/453397

 

 

仍然是下面的表结构:

SQL> DESC T_P
io K(@V"_MquP0 
名称                是否为空?类型
Q]"N%Y.ysh(^0 -------------------- -------- ------------------------
i[4g4W%c+x6e0 ID                   NOT NULL NUMBERITPUB个人空间-^HUu?-i6^/@
 FLAG                          VARCHAR2(1)

SQL> DESC T_FITPUB个人空间oZ9K)R8VVn*V
 
名称                是否为空?类型ITPUB个人空间#PZ9w5@4z5\}
 -------------------- -------- ------------------------
2{~+\-w2sK#~s0 ID                   NOT NULL NUMBER
b6Di7jlo6@B6W0 FID                           NUMBERITPUB个人空间"y:{9b9x!Q4W9z9v [
 FLAG                          VARCHAR2(1)

表中包含了一个FLAG标识位,而在程序进行删除操作的时候,并不是在数据库中进行产生,而是将这个FLAG状态从0置为1,表示这条记录已经删除。通过这种方式,来避免数据库中删除操作的发生。

下面描述如何构造这种情况下的并发控制。通过上面一篇文章的描述可以发现,通过检查机制是没有办法进行控制的。那么必须仿照主外键的机制建立锁表机制。

为了保证并发修改主子表的时候不会发生错误,就必须保证无论是插入子表,还是更新主表的删除字段,都必须获取同一个锁,这样并发才能得到保证。因此,这个锁只能通过锁定主表记录来完成。下面通过例子来说明先更新主表和先插入子表两种情况下,如何采用锁表方法控制并发访问:

首先插入记录:

SQL> INSERT INTO T_P VALUES (1, 0);

已创建1行。

SQL> INSERT INTO T_P VALUES (2, 0);

已创建1行。

SQL> COMMIT;

提交完成。

会话二设置SQLPROMPT来区别会话一:

SQL> SET SQLP 'SQL2> 'ITPUB个人空间ir&e$|8Cf)s!M)S/W
SQL2> SELECT * FROM T_P;

        ID F
GOT\ Z7b9hd1_0---------- -
E5`d L Sl1sH&L-_0         1 0ITPUB个人空间hvQ?'V@ q
         2 0

下面会话一修改主表的删除标识:

SQL> UPDATE T_P SET FLAG = 1 WHERE ID = 1 AND FLAG = 0;

已更新1行。

SQL> UPDATE T_F SET FLAG = 1 WHERE FID = 1 AND FLAG = 0;

已更新0行。

为了在主表资源上获取锁,因此先更新主表记录,在更新完主表记录后,在更新子表记录。

此时会话二要添加子表的记录,为了保证并发性,避免主表在更新子表的过程中被修改,会话二也需要先获取主表上的锁:

SQL2> SELECT * FROM T_P WHERE ID = 1 AND FLAG = 0 FOR UPDATE;

子表执行完操作后被锁定,只有主表提交或回滚才能继续执行。如果这个时候会话一回滚,那么就相当于没有会话一没有操作,当前会话可以顺利的获取主表上的锁,并插入记录。如果这个时候会话一提交:

SQL> COMMIT;

提交完成。

那么会话二将无法获取到锁资源:

ITPUB个人空间_3XA8K[TX.e
未选定行

SQL2>

这个时候,会话二就不能在继续执行下面的操作了,从而避免了并发修改造成的错误。下面看看先执行插入的情况:

SQL> SELECT * FROM T_P WHERE ID = 2 AND FLAG = 0 FOR UPDATE;

        ID F
*DYX"| BO0---------- -ITPUB个人空间XYr s2S4Y*d_;jT
         2 0

SQL> INSERT INTO T_F VALUES (2, 2, 0);

已创建1行。

这个时候在会话二尝试更新主表的删除表示位:

SQL2> UPDATE T_P SET FLAG = 1 WHERE ID = 2 AND FLAG = 0;

会话二会被锁住。如果会话一回滚,则会话二获得主表的锁,并继续更新。如果会话一提交:

SQL> COMMIT;

提交完成。

则会话二同样可以获取到锁的资源,而且继续更新子表记录,并不会造成主表记录被标识为删除,而子表记录没有被标识的情况:

SQL2> UPDATE T_P SET FLAG = 1 WHERE ID = 2 AND FLAG = 0;

已更新1行。

SQL2> UPDATE T_F SET FLAG = 1 WHERE FID = 2 AND FLAG = 0;

已更新1行。

SQL2> COMMIT;

提交完成。

通过这种方式,可以保证在并发修改下,主表记录和子表的记录的一致性状态。

SQL> SELECT * FROM T_P;

        ID FITPUB个人空间 z6z#zS4XK'~js
---------- -
z!H.g`u8VX0         1 1
RyTmrPn ^0         2 1

SQL> SELECT * FROM T_F;

        ID        FID FITPUB个人空间9?5e8q"pF/H%B.W
---------- ---------- -ITPUB个人空间G|0W/NG _X)A6x"G:nh
         2          2 1

 


TAG:

 

评分:0

我来说两句

显示全部

:loveliness: :handshake :victory: :funk: :time: :kiss: :call: :hug: :lol :'( :Q :L ;P :$ :P :o :@ :D :( :)

Open Toolbar