6

「チケット」テーブルから 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;

...
... 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)

誰かが何が起こっているのか、どうすれば回避できるのかを説明できれば幸いです。

4

3 に答える 3

2

これを行う:

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

このアプローチではデッドロックが発生することはなく、好きなだけ接続を投げることができます。

于 2012-09-08T02:16:32.723 に答える
0

使ってみることができます

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の場合はテーブルをロックしません(実際の更新ステートメント中を除く)。

于 2012-07-20T21:38:57.437 に答える
0

次のシナリオでは、デッドロックが発生します。

トランザクション 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 を 1 行で初期化する必要があることです。

于 2012-07-05T15:07:00.200 に答える