并发和强制使用视图和触发器的伪唯一约束?

时间:2014-03-06 00:28:49

标签: mysql sql triggers

我的应用程序要求数据库中只存在一个具有4个值的特定组合的业务对象。例如:

prop_a prop_b prop_c prop_d
a      b      c      x
a      b      c      x <-- OK
a      b      c      x <-- OK
a      b      c      y <-- OK
a      b      c      y <-- FAIL
f      g      h      x
f      g      h      x <-- OK
f      g      h      y <-- OK
f      g      h      y <-- FAIL

这些属性在多个表上标准化,prop_a是计算值。在当前数据库模式下,UNIQUE约束不适用于此。

架构的简化版本是:

TBL_A
-----------
a_id PRIMARY KEY
prop_a

TBL_B
-----------
b_id PRIMARY KEY
a_id NULLABLE FOREIGN KEY to TBL_A.a_id
prop_b UNIQUE


TBL_C
-----------
c_id PK
a_id NULLABLE FOREIGN KEY to TBL_A.a_id
b_id NOT NULL FOREIGN KEY to TBL_B.b_id
prop_c
prop_d

查看VW_MYOBJECT将TBL_A,TBL_B和TBL_C中的数据聚合为example_above中给出的格式。

由于MySQL不支持CHECK约束,我在TBL_C上创建了BEFORE INSERT / BEFORE UPDATE触发器,后者又调用查询VW_MYOBJECT的函数来查看INSERT / UPDATE是否违反伪唯一性要求并且如果INSERT / UPDATE失败则INSERT / UPDATE失败所以。这大部分时间都有效,但是有允许重复行的实例。查看有问题的行显示它们具有相同的更新时间戳(低至第二个),所以我猜这是一个并发问题。

确保数据库始终强制执行伪唯一性要求的最佳方法是什么?让客户端获取TBL_A,TBL_B和TBL_C的写锁定?添加和/或非规范化模式,以便可以使用真正的UNIQUE约束强制执行此要求?还有别的吗?

以下是导致违规行的客户会话的记录:

140228 17:19:19 client_1 Connect  user@server on database                
                client_2 Connect  user@server on database
                client_1 Prepare  select * from `TBL_C`
                client_1 Execute  select * from `TBL_C` 
                client_2 Prepare  select * from `TBL_C` 
                client_1 Query    START TRANSACTION
                client_2 Close stmt       
                client_2 Query    START TRANSACTION
                client_1 Prepare  select * from `TBL_B` where `b_id` = ? limit 1
                client_1 Execute  select * from `TBL_B` where `b_id` = '158' limit 1
                client_2 Prepare  select * from `TBL_B` where `b_id` = ? limit 1
                client_1 Close stmt       
                client_2 Close stmt       
                client_1 Prepare  insert into TBL_A (`a_id`, `prop_a`) values (?)
                client_2 Prepare  insert into TBL_A (`a_id`, `prop_a`) values (?)
                client_1 Execute  insert into TBL_A (`prop_a`) values (1, 'FOO')
                client_2 Execute  insert into TBL_A (`prop_a`) values (2, 'FOO')
                client_1 Close stmt       
                client_2 Close stmt       
                client_1 Prepare  insert into TBL_C (`prop_c`, `a_id`, `b_id`, `prop_d`) values (?, ?, ?, ?)
                client_2 Prepare  insert into TBL_C (`prop_c`, `a_id`, `b_id`, `prop_d`) values (?, ?, ?, ?)
                client_1 Execute  insert into TBL_C (`prop_c`, `a_id`, `b_id`, `prop_d`) values ('1234', 1, 100, 'x')
                client_1 Close stmt       
                client_1 Query    commit
                ####### THIS INSERT SHOULD HAVE FAILED BUT WAS ALLOWED
                client_2 Execute  insert into TBL_C (`prop_c`, `a_id`, `b_id`, `prop_d`) values ('1234', 2, 100, 'x') 
                client_2 Close stmt       
                client_2 Query    commit
                client_1 Quit     
                client_2 Close stmt       
                client_2 Quit     

0 个答案:

没有答案