2016-11-15 13 views
0

まれなデッドロックエラーが発生しました。私はデッドロックが2つのクエリの仕事がお互いの結果に依存するので、MySQLがそれらの1つをロールバックすることを理解します。しかし私の状況では、MySQLは自動コミットモードにあり、私はトリガを起動する新しいレコードを挿入しています。だから私は死んだロックの状況を作り出す理由はない。ここで MySQLデッドロックで挿入をトリガーにする

は、私のテーブルのスキーマです:

----ユーザー表----

CREATE TABLE `users` (
`insta_id` bigint(20) unsigned NOT NULL, 
`name` varchar(50) NOT NULL, 
`password` varchar(60) NOT NULL, 
`gem` int(10) unsigned DEFAULT '20', 
`coin` int(10) unsigned DEFAULT '20', 
PRIMARY KEY (`insta_id`), 
UNIQUE KEY `insta_id` (`insta_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1 

---表をlike_requests ---

CREATE TABLE `like_requests` (
`req_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 
`insta_id` bigint(20) unsigned NOT NULL, 
`media_id` varchar(50) NOT NULL, 
`remaining_like` int(10) unsigned NOT NULL, 
`active` tinyint(1) NOT NULL DEFAULT '1', 
`count` int(10) unsigned NOT NULL, 
PRIMARY KEY (`req_id`), 
KEY `insta_id` (`insta_id`), 
KEY `media_id` (`media_id`), 
CONSTRAINT `like_requests_ibfk_1` FOREIGN KEY (`insta_id`) REFERENCES `users`(`insta_id`) 
) ENGINE=InnoDB AUTO_INCREMENT=103902 DEFAULT CHARSET=latin1 

---好き表---

CREATE TABLE `likes` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 
`insta_id` bigint(20) unsigned NOT NULL, 
`media_id` varchar(50) NOT NULL, 
`req_id` bigint(20) unsigned DEFAULT NULL, 
`date` timestamp NULL DEFAULT CURRENT_TIMESTAMP, 
PRIMARY KEY (`id`), 
UNIQUE KEY `id` (`id`), 
KEY `req_id` (`req_id`), 
KEY `insta_id` (`insta_id`), 
KEY `media_id` (`media_id`), 
CONSTRAINT `likes_ibfk_1` FOREIGN KEY (`req_id`) REFERENCES `like_requests`(`req_id`), 
CONSTRAINT `likes_ibfk_2` FOREIGN KEY (`insta_id`) REFERENCES `users`(`insta_id`) 
) ENGINE=InnoDB AUTO_INCREMENT=1704209 DEFAULT CHARSET=latin1 

私はトリガーを持っています私は、次の死んだロック・エラー・ログを取得

$sql = "INSERT INTO likes (insta_id,media_id,req_id) VALUES (?,?,?);"; 
    $pdo = $this->db; 

    $statement = $pdo->prepare($sql); 
    $statement->bindValue(1,$data['id'],PDO::PARAM_INT); 
    $statement->bindValue(2,$data['media_id']); 
    $statement->bindValue(3,$data['req_id'],PDO::PARAM_INT); 

    try 
    { 
     $statement->execute(); 
     return GetOkResponseWithMessage($response,"Like was submitted"); 
    } 
    catch (PDOException $exc) 
    { 
     return GetErrorResponseWithMessage($response,$exc->getMessage(),500); 
    } 

*** (1) TRANSACTION: 
TRANSACTION 29031910, ACTIVE 1 sec starting index read 
mysql tables in use 4, locked 4 
LOCK WAIT 7 lock struct(s), heap size 1184, 3 row lock(s), undo log entries 1 
MySQL thread id 264238, OS thread handle 0x7f6522c6eb00, query id 753506  localhost xxxx updating 
UPDATE users SET users.coin=users.coin+1 WHERE users.insta_id=NEW.insta_id LIMIT 1 
*** (1) WAITING FOR THIS LOCK TO BE GRANTED: 
RECORD LOCKS space id 14 page no 1560 n bits 128 index `PRIMARY` of table `insta_star`.`users` trx table locks 4 total table locks 4 trx id 29031910 lock_mode X locks rec but not gap waiting lock hold time 0 wait time before grant 0 
*** (2) TRANSACTION: 
TRANSACTION 29031909, ACTIVE 1 sec starting index read 
mysql tables in use 4, locked 4 
7 lock struct(s), heap size 1184, 3 row lock(s), undo log entries 1 
MySQL thread id 264237, OS thread handle 0x7f65209f8b00, query id 753507 localhost xxxx updating 
UPDATE users SET users.coin=users.coin+1 WHERE users.insta_id=NEW.insta_id LIMIT 1 
*** (2) HOLDS THE LOCK(S): 
RECORD LOCKS space id 14 page no 1560 n bits 128 index `PRIMARY` of table `insta_star`.`users` trx table locks 4 total table locks 4 trx id 29031909 lock mode S 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 14 page no 1560 n bits 128 index `PRIMARY` of table `insta_star`.`users` trx table locks 4 total table locks 4 trx id 29031909 lock_mode X locks rec but not gap waiting lock hold time 0 wait time before grant 0 
*** WE ROLL BACK TRANSACTION (2) 

が、これは終わるべきではない

CREATE TRIGGER `after_insert_likes` AFTER INSERT ON `likes` 
    FOR EACH ROW BEGIN 
     UPDATE users SET users.coin=users.coin+1 
      WHERE users.insta_id = NEW.insta_id LIMIT 1; 
     IF NEW.req_id IS NOT NULL THEN 
      UPDATE like_requests 
       SET like_requests.remaining_like = like_requests.remaining_like-1 
       WHERE like_requests.req_id = NEW.req_id 
        AND like_requests.remaining_like > 0 
       LIMIT 1; 
     END IF; 
    END 

をいくつかの簡単な挿入を行うことで、次のように定義、テーブルを好きNデッドロックの代わりにロック待機中に?

トランザクションを再開せずにこの問題を克服するにはどうすればよいですか?

+0

これを引き起こした挿入を追加できますか?トランザクションの周りにラップした場合は、フルボックを投稿してください – e4c5

+0

@ e4c5挿入コードはただ1つのクエリとして実行されます。私は挿入を行うPHPのコードを追加しました – BlackBrain

答えて

1

likesで、(insta_id, media_id)はユニークですか?あるいは(insta_id, req_id) ??または、おそらくすべて3?その場合は、PRIMARY KEYとし、id all together. If you must keep id , get rid of UNIQUE(ID), since PRIMARY KEY(id) `を削除します。

同様に、取り除く値はUNIQUE(insta_id)です。いくつかのコマンドで構成トランザクションであることを組み合わせるautocommitTRIGGER

は思う:

BEGIN; 
INSERT INTO likes... -- Includes 2 uniqueness checks, 1 FK check 
UPDATE users ... 
if... UPDATE like_requests ... 
COMMIT; 

私は、屈折率変化は、これにより、デッドロックの可能性を減らす、いくつかの物事をスピードアップすることが示唆されました。変更はでもデッドロックを待たせるかもしれないが、私はそれを疑う。

デッドロックに対するあなたの最良の防御は、彼らと一緒に暮らしており、それらを捕まえてトランザクション(この場合はINSERT)を再生することです。

(関連性がありません:) media_idは重複して格納されているようです。

2

スレッド2は、ユーザーテーブルの行に共有ロックを保持します。

次に、スレッド1は同じ行で排他ロックを取得しようとし、ロック待ちになります。

スレッド1は、スレッド2が自分のロックを排他的にエスカレートしようとしているので、タイムアウトする機会はありませんが、ロック待機中のスレッド1を待たなければなりませんスレッド2を待っています。

これらはそれぞれ別のスレッドをブロックしています。

これはデッドロックです。

サーバーは、不要にブロックすることがないように、強制終了するトランザクションを選択します。

デッドロック検出では、一方のスレッドがすぐに他方のスレッドを犠牲にして成功することができます。さもなければ、彼らはどちらかが待たずに死んでしまうまで、ロック待ちで立ち往生していました。


あなたは自動コミットモードですが、それはあなたがトランザクションに参加していないわけではありません。 InnoDBを使用するすべてのクエリは、トランザクション内で引き続き処理されますが、自動コミットでは、クエリは実行が開始されるときに暗黙的に開始され、成功した場合には暗黙的にコミットされます。

+0

あなたはトランザクションを再起動せずにこれを修正する方法を知っていますか? – BlackBrain