[英]MySQL deadlocks with stored procedure generating UID
我有一个存储过程从“票证”表生成UID,但在负载下我遇到了很多死锁。 每当我的任务需要新的UID时,我就会从多个并发连接中多次调用此过程。
BEGIN
DECLARE a_uid BIGINT(20) UNSIGNED;
START TRANSACTION;
SELECT uid INTO a_uid FROM uid_data FOR UPDATE; # Lock
INSERT INTO uid_data (stub) VALUES ('a') ON DUPLICATE KEY UPDATE uid=uid+1;
SELECT a_uid+1 AS `uid`;
COMMIT;
END
我考虑过使用:
BEGIN
REPLACE INTO uid_data (stub) VALUES ('a');
SELECT LAST_INSERT_ID();
END
但是我不确定并发连接是否安全,因为没有锁定,这与使用SELECT FOR UPDATE
的第一个过程不同。
这是表格:
mysql> DESCRIBE uid_data;
+-------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------------------+------+-----+---------+----------------+
| uid | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| stub | char(1) | NO | UNI | NULL | |
+-------+---------------------+------+-----+---------+----------------+
我已经设置了read-committed事务隔离:
mysql> SHOW VARIABLES LIKE 'tx_isolation';
+---------------+-----------------+
| Variable_name | Value |
+---------------+-----------------+
| tx_isolation | READ-COMMITTED |
+---------------+-----------------+
这是我从SHOW ENGINE INNODB STATUS;
回来的内容SHOW ENGINE INNODB STATUS;
...
... dozens and dozens of the following record locks...
Record lock, heap no 1046 PHYSICAL RECORD: n_fields 2; compact format; info bits 32
0: len 1; hex 61; asc a;;
1: len 8; hex 00000000000335f2; asc 5 ;;
Record lock, heap no 1047 PHYSICAL RECORD: n_fields 2; compact format; info bits 32
0: len 1; hex 61; asc a;;
1: len 8; hex 00000000000335f1; asc 5 ;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 13 page no 4 n bits 1120 index `stub` of table `my_db`.`uid_data` trx id 13AA89 lock_mode X waiting
Record lock, heap no 583 PHYSICAL RECORD: n_fields 2; compact format; info bits 32
0: len 1; hex 61; asc a;;
1: len 8; hex 00000000000334a8; asc 4 ;;
*** WE ROLL BACK TRANSACTION (1)
如果有人能够解释正在发生的事情以及如何避免这些问题,我将不胜感激。
做这个:
CREATE TABLE tickets
(
uid serial
)
然后得到下一个uid:
BEGIN
INSERT INTO tickets VALUES (NULL);
SELECT LAST_INSERT_ID();
END
uid序列相当于
uid BIGINT(20) UNSIGNED NOT NULL PRIMARY KEY auto_increment
您不应该遇到任何使用此方法的死锁,并且可以根据需要添加任意数量的连接。
在这种情况下发生死锁:
事务1:请求锁定( SELECT...FOR UPDATE
)并获取它
事务2:请求锁定( SELECT...FOR UPDATE
)并且必须等待
事务1:尝试插入,命中重复,因此更新( INSERT...ON DUPLICATE KEY UPDATE
)=>死锁
我不太确定原因,我怀疑它与
ON DUPLICATE KEY UPDATE
。
我还在调查,如果我发现我会回来。
[编辑]即使出现以下情况也会发生死锁:
BEGIN
START TRANSACTION;
SELECT uid FROM uid_data FOR UPDATE;
UPDATE uid_data SET uid = uid +1; -- here, a deadlock would be detected in a blocked, concurrent connection
COMMIT;
END
那这个呢:
BEGIN
START TRANSACTION;
UPDATE uid_data SET uid = uid +1;
SELECT uid FROM uid_data;
COMMIT;
END
你可以完全放弃你的stub
柱。 唯一的缺点是你必须用一行初始化你的uid_data。
你可以尝试使用
UPDATE uid_data SET uid = LAST_INSERT_ID(uid+1);
SELECT LAST_INSERT_ID();
在桌子上
CREATE TABLE `uid_data` (
`uid` BIGINT(20) UNSIGNED NOT NULL
)
COLLATE='utf8_general_ci'
ENGINE=MyISAM;
这是线程安全的,如果它是MyISAM,则不会锁定表(实际更新语句除外)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.