ITPub博客

首页 > Linux操作系统 > Linux操作系统 > 无法利用主外键时控制主子表的并发访问(二)

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

原创 Linux操作系统 作者:yangtingkun 时间:2008-01-27 23:50:55 0 删除 编辑

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

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

这篇讨论目前的问题,无法使用主外键进行并发控制的情况。

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

 

 

目前的问题是,数据库表结构采用下面的设计:

SQL> DESC T_P
 
名称                 是否为空? 类型
 -------------------- -------- ------------------------
 ID                   NOT NULL NUMBER
 FLAG                          VARCHAR2(1)

SQL> DESC T_F
 
名称                 是否为空? 类型
 -------------------- -------- ------------------------
 ID                   NOT NULL NUMBER
 FID                           NUMBER
 FLAG                          VARCHAR2(1)

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

很快就发现,这种设计将面临着一个严重的问题,就是并发控制的问题。由于数据本身并没有从数据库中删除,而只是标示位的改变,因此主外键关系一直存在,也就是说,无法通过主外键来帮助实现主子表的并发控制了。

程序员试图通过程序检查的方式来避免问题,但是这种方式是不可行的。下面通过一个例子来说明:

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

已创建 1 行。

SQL> COMMIT;

提交完成。

SQL> SELECT * FROM T_P;

        ID F
---------- -
         1 0

下面另启动一个会话,并设置SQLPROMPT和会话一进行区别:

SQL> SET SQLP 'SQL2> '
SQL2> SELECT * FROM T_P;

        ID F
---------- -
         1 0

现在会话一和会话二都可以看到主记录,那么会话一尝试删除记录,也就是将FLAG表示置为1

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

已更新 1 行。

会话二尝试添加子表记录:

SQL2> INSERT INTO T_F VALUES (1, 1, 0);

已创建 1 行。

现在发现,两个操作可以同时进行,而且会话一和会话二都可以成功提交:

SQL> COMMIT;

提交完成。

会话二:

SQL2> COMMIT;

提交完成。

现在的问题时,主表记录被置于删除状态,但是子表仍然向其中插入了新的记录。这是违背业务逻辑的。

这是由于主表的记录仍然存在,因此子表的插入不会引发主外键的冲突。因此主外键对于当前设计面临逻辑业务冲突无能为力。

而程序员试图通过操作前的检查操作也是徒劳的。

插入一条新的记录:

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

已创建 1 行。

SQL> COMMIT;

提交完成。

下面在会话二中尝试插入子表记录,插入之前检查主表的表示是否为0

SQL2> SELECT * FROM T_P WHERE ID = 2;

        ID F
---------- -
         2 0

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

已创建 1 行。

下面在会话一中尝试删除主表记录,即将标示位置为1,在删除之前检查是否存在标志位为0的记录:

SQL> SELECT * FROM T_F WHERE FID = 2 AND FLAG = 0;

未选定行

SQL> UPDATE T_P SET FLAG = 1 WHERE ID = 2;

已更新 1 行。

会话一和会话二在操作之前都进行了检查,但是他们看不到互相的操作内容,因此他们认为操作可以进行执行,而如果此时对两个会话分别提交,依然会出现主表记录被置为删除,单子表记录存在且状态不为删除的情况。

那么如果操作之前检查是行不通的,将检查放在操作之后进行,也就是说在提交之前先检查主子表的情况:

SQL> SELECT * FROM T_F WHERE FID = 2 AND FLAG = 0;

未选定行

会话二的检查:

SQL2> SELECT * FROM T_P WHERE ID = 2;

        ID F
---------- -
         2 0

很不幸,这种方式也是行不通的。因为Oracle中是无法看到其他会话没有提交的信息的,也就是说,会话一看不到会话二的插入,会话二也看不到会话一的修改,二者的检查结果都是没有冲突可以提交,但是提交后就会出现上面所说的违背业务逻辑的情况。

显然没有了主外键的帮助,仅靠检查是没有办法避免并发问题的,现在只能通过自己来构造这种情况下的并发控制了。

 

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/4227/viewspace-162817/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
暂无介绍

注册时间:2007-12-29

  • 博文量
    1955
  • 访问量
    10350871