2012-07-05 7 views
6

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

私が使用して検討しました:SELECT FOR UPDATEとの最初の手順とは異なり、ロックがないように、その同時接続で安全であるかどう

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

は、しかし、私は確認されませんでした。ここで

はテーブルです:

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は、読み取りコミットトランザクション分離のためにセットアップしました:ここで私は、私は戻って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) 

から取得しています何

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

です誰かが何が起こっているのか、どのように回避できるのかを説明できると感謝しています。

トランザクション1:デッドロックがこの状況で発生し

+0

詳細については、この単純なシーケンスを使用している場合でもデッドロックが発生します: 'START TRANSACTION; uid_dataからFROM uidを選択してください。 UPDATE uid_data SET uid = uid +1 [[ここでデッドロックが発生する可能性があります]]; COMMIT; '(したがって、' ON DUPLICATE'節とは関係ありません)。ただし、分離レベルが 'REPEATABLE READ;'でデッドロックは発生しません。私はまだこの点から何を結論するか分からない。 – RandomSeed

答えて

0

は、ロック(SELECT...FOR UPDATE)を要求し、それが

トランザクション2が取得:ロック(SELECT...FOR UPDATE)を要求し、

トランザクション1を待つ必要があります。 (INSERT...ON DUPLICATE KEY UPDATE)=>デッドロック

私は再についてはあまり確信していませんason、それは 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 columを落とすことができます。唯一の欠点は、uid_dataを1行で初期化する必要があることです。

+0

改訂されたストアドプロシージャは、どのような種類のロックでも並行性を処理しますか? – Sencha

+0

@Senchaはい、UPDATEはアトミックであり、トランザクションの終了まで行をロックします。しかし、私はまだあなたの元のシーケンスで死んでロックの理由について非常に興味があります(私のコメントもあなたの質問に参照してください)。 – RandomSeed

0

あなたはこのスレッドセーフであり、それは(実際の更新ステートメント中を除く)のMyISAMの場合は、テーブルをロックしません

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

のようにテーブルの上に

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

を使用して試すことができます。

2

これを実行してください:あなたは、このアプローチで任意のデッドロックが発生するべきではありません

uid BIGINT(20) UNSIGNED NOT NULL PRIMARY KEY auto_increment 

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

uidのシリアルは同等です:

CREATE TABLE tickets 
(
    uid serial 
) 

は、その後、次のUIDを取得しますあなたが好きなだけ多くの接続をそれに投げることができます。

+0

LAST_INSERT_ID()はスコープが制限されていることを明確にするために追加する必要があります。これらのクエリのうち1000件が同時に実行されている場合は、別の接続に対して間違った番号を取得するリスクはありません。 –

関連する問題