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

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

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

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

无法利用主外键时控制主子表的并发访问(一):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/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/4227/viewspace-162817/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值