主子表的并发控制,一般是通过主外键的约束来实现。通过这种方式,保证子表插入数据的情况下,主表不能将记录删除。
目前碰到一个问题,无法通过主外键的约束来控制并发访问。
这篇描述无法利用主外键时,如何进行并发控制。
无法利用主外键时控制主子表的并发访问(一):http://yangtingkun.itpub.net/post/468/453295
无法利用主外键时控制主子表的并发访问(二):http://yangtingkun.itpub.net/post/468/453397
仍然是下面的表结构:
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> INSERT INTO T_P VALUES (2, 0);
已创建 1 行。
SQL> COMMIT;
提交完成。
会话二设置SQLPROMPT来区别会话一:
SQL> SET SQLP 'SQL2> '
SQL2> SELECT * FROM T_P;
ID F
---------- -
1 0
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;
提交完成。
那么会话二将无法获取到锁资源:
未选定行
SQL2>
这个时候,会话二就不能在继续执行下面的操作了,从而避免了并发修改造成的错误。下面看看先执行插入的情况:
SQL> SELECT * FROM T_P WHERE ID = 2 AND FLAG = 0 FOR UPDATE;
ID F
---------- -
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 F
---------- -
1 1
2 1
SQL> SELECT * FROM T_F;
ID FID F
---------- ---------- -
2 2 1
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/4227/viewspace-163523/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/4227/viewspace-163523/