我有一个包含3列复合主键的表:key1,key2和key3
我有另一个名为'Order'的列,它跟踪数据库中项目的顺序。每次将项目添加到数据库时,我都必须确保“Order”列具有最高值(对于key1和key2定义的同一组)。例如:
key1 | key2 | key3 | order
--------------------------
1 | 2 | 10 | 0
1 | 2 | 11 | 10
1 | 2 | 12 | 20
1 | 2 | 13 | 30
现在,如果我必须添加一个新项目(1,2,14),那么订单必须大于30,以便该项目添加列表的末尾。 用户还可以对项目进行重新排序,这意味着我可以修改(1,2,12)的“订单”列,使其值为-10,并将其置于列表的顶部。
在我们同时向表中添加项目之前,这一切都很好。我必须确保同一组中的项目的“Order”值不重复(key1和key2的值相等的项目)。
因此,我添加新项目的SQL代码如下所示:
START TRANSACTION;
INSERT INTO table_name VALUES (1, 2, 14, null);
UPDATE table_name SET `Order`=
(
SELECT * FROM (SELECT COALESCE((SELECT MAX(`Order)
FROM table_name
WHERE `key1`=1 AND `key2`=2), 0)+10) a
) where `key1`=1 AND `key2`=2 AND `key3`=14;
COMMIT;
上述事务使用C#代码运行。在插入和更新之间,C#代码检查是否需要计算Order
列。例如,对于key1 = 1和key2 = 2的组合,我们可能需要计算Order而不是组合key = 1和key2 = 3并将其保留为null。这就是为什么insert sql不计算Order列并将其保留为null的原因。
现在的问题是,它在我尝试同时添加大量记录之前一直很安静。我有一个同时发送20个项目的程序,并不是所有项目都成功添加到数据库中,因为它在update语句中死锁。
任何人都可以建议我一些方法来防止僵局,或者更好的设计。
由于
修改
在插入时尝试计算订单,但我现在在插入时遇到死锁。 我的插入基本上看起来像:
INSERT INTO table_name VALUES (1, 2, 14, (SELECT * FROM (SELECT COALESCE((SELECT MAX(Order) FROM table_name WHERE key1=1 AND key2`=2), 0)+10) a));
修改
所以这是我做的日志:SHOW ENGINE INNODB STATUS;
2016-08-19 09:38:08 15fc
*** (1) TRANSACTION:
TRANSACTION 2764458, ACTIVE 0 sec starting index read
mysql tables in use 3, locked 3
LOCK WAIT 9 lock struct(s), heap size 2936, 6 row lock(s), undo log entries 1
MySQL thread id 1780, OS thread handle 0x5508, query id 85256 localhost ::1 root Sending data
UPDATE table_name SET `Order`=
(
SELECT * FROM (SELECT COALESCE((SELECT MAX(`Order`)
FROM table_name
WHERE `key1`=164420 AND `key2`=2), 0)+4294967296) c
)
WHERE `key1`=164420 AND `key2`=2 AND `key3`=3368
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 167 page no 1939 n bits 352 index `PRIMARY` of table `table_name` trx table locks 3 total table locks 2 trx id 2764458 lock mode S waiting lock hold time 0 wait time before grant 0
*** (2) TRANSACTION:
TRANSACTION 2764459, ACTIVE 0 sec fetching rows
mysql tables in use 3, locked 3
11 lock struct(s), heap size 2936, 23 row lock(s), undo log entries 1
MySQL thread id 1782, OS thread handle 0x15fc, query id 85259 localhost ::1 root Sending data
UPDATE table_name SET `Order`=
(
SELECT * FROM (SELECT COALESCE((SELECT MAX(`Order`)
FROM table_name
WHERE `key1`=164420 AND `key2`=2), 0)+4294967296) c
)
WHERE `key1`=164420 AND `key2`=2 AND `key3`=3338
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 167 page no 1939 n bits 352 index `PRIMARY` of table `table_name` trx table locks 3 total table locks 2 trx id 2764459 lock_mode X locks rec but not gap lock hold time 0 wait time before grant 0
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 167 page no 1939 n bits 352 index `PRIMARY` of table `table_name` trx table locks 3 total table locks 2 trx id 2764459 lock mode S waiting lock hold time 0 wait time before grant 0
*** WE ROLL BACK TRANSACTION (1)