MySQL deadlocks with stored procedure generating UID

六眼飞鱼酱① 提交于 2020-01-01 05:23:13

问题


I have a stored procedure generating UID's from a "ticket" table, but under load I'm getting lots of deadlocks. I'm calling this procedure many times from multiple concurrent connections whenever my task needs a new 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

I did consider using:

BEGIN
    REPLACE INTO uid_data (stub) VALUES ('a');
    SELECT LAST_INSERT_ID();
END

However I wasn't sure if that would be safe with concurrent connections as there's no locking, unlike the first procedure with the SELECT FOR UPDATE.

Here's the table:

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    |                |
+-------+---------------------+------+-----+---------+----------------+

I've setup for read-committed transaction isolation:

mysql> SHOW VARIABLES LIKE 'tx_isolation';
+---------------+-----------------+
| Variable_name | Value           |
+---------------+-----------------+
| tx_isolation  | READ-COMMITTED  |
+---------------+-----------------+

Here's what I'm getting back from 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)

I'd be grateful if someone could explain what's happening and how they can be avoided.


回答1:


Do this:

CREATE TABLE tickets
(
    uid serial
)

Then to get the next uid:

BEGIN
  INSERT INTO tickets VALUES (NULL);
  SELECT LAST_INSERT_ID();
END

uid serial is equivalent to

uid BIGINT(20) UNSIGNED NOT NULL PRIMARY KEY auto_increment

You shouldn't experience any deadlocks with this approach and can throw as many connections at it as you like.




回答2:


A deadlock occurs in this scenario:

Transaction 1 : requests a lock (SELECT...FOR UPDATE) and acquires it

Transaction 2 : requests a lock (SELECT...FOR UPDATE) and must wait

Transaction 1 : tries to insert, hits a duplicate, therefore updates (INSERT...ON DUPLICATE KEY UPDATE) => deadlock

I am not too sure about the reason, I suspect it has something to do with the ON DUPLICATE KEY UPDATE. I'm still investigating and will come back if I find out.

[Edit] A deadlock occurs even with:

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

What about this:

BEGIN
    START TRANSACTION;    
    UPDATE uid_data SET uid = uid +1;
    SELECT uid FROM uid_data;
    COMMIT;
END

You could drop your stub colum altogether. The only drawback is that you must initialise your uid_data with one row.




回答3:


You can try using

UPDATE uid_data SET uid = LAST_INSERT_ID(uid+1);
SELECT LAST_INSERT_ID();

on a table like

CREATE TABLE `uid_data` (
    `uid` BIGINT(20) UNSIGNED NOT NULL
)
COLLATE='utf8_general_ci'
ENGINE=MyISAM;

This is thread safe and will not lock the table if it is MyISAM (except during the actual update statement).



来源:https://stackoverflow.com/questions/11346256/mysql-deadlocks-with-stored-procedure-generating-uid

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!