2017-04-25 8 views
1

mysql 5.7をストレージエンジンと共にinnodbとして使用する。私は製品情報を格納するテーブルを持っています。テーブルには、商品コードに一意のキーで次のようになりますデッドロックにつながる行のMysql同時更新

| Field  | Type   | Null | Key | Default   | Extra      | 
+-----------+--------------+------+-----+-------------------+-----------------------------+ 
| id  | bigint(20) | NO | PRI | NULL    | auto_increment    | 
| productId | varchar(50) | NO | UNI | NULL    |        | 
| seller | varchar(100) | NO | MUL | NULL    |        | 
| updatedAt | timestamp | NO | MUL | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP | 
| status | varchar(100) | NO | MUL | NULL    |        | 
| data  | longtext  | NO |  | NULL    |        | 
+-----------+--------------+------+-----+-------------------+-----------------------------+ 

私はこれのmysqlに接続されたJavaアプリを経由して2つの操作があります(製品の変更に関する情報が含まれています)
1.新規の着信イベントへのProductID必要性についてそれらが既存のイベントよりも大きなバージョンを持っている場合に挿入されます。バージョンはデータ列にjson blobとして保存されます
2.ステータスを変更するproductIdの行を更新します。

私の隔離レベルは読み込みコミットされています。 私は2つのアプローチを試みたが、両者がデッドロックにつながるされています

Approach1:

Transaction1 starts 
Insert ignore into products where productId='X' values(); // Takes a S lock on the row 
select * from products where productId='X' for update ; // Take a X lock on the row to prevent new writes and compare the incoming event with the current event 
Insert into products values on duplicate key update values // insert into row and on duplicate key update values 
commit 

同時更新が別のトランザクションを開きます:

Transaction2 starts 
select * from products where productId='X' for update ; // Take a X lock on the row to prevent new writes and compare the incoming event with the current event 
Insert into products values on duplicate key update values // insert into row and on duplicate key update values 
commit; 

この状況でデッドロックにつながっています次の場合:
1.トランザクション1 - 挿入無視ステートメントが行をSロックしました。
トランザクション2 - 行のXロックを取るために更新ステートメントが選択されています。
3.トランザクション1 - 更新ステートメントの選択は、行のXロックを取得しようとします。

これは、Sロックがトランザクション1によって保持され、トランザクション2がXロックを待機しており、トランザクション1がXロックを取得しようとするとデッドロックにつながるため、デッドロックになります。

アプローチ2:この状況でデッドロックにつながっている

Transaction 1 starts: 
select * from products where productId='X' for update ; // If a row exists then it will be locked else I know it does not exist 
Insert ignore into products where productId='X' values(); 
commit 

Transaction 2 starts: 
select * from products where productId='X' for update ; // If a row exists then it will be locked else I know it does not exist 
commit 


1.トランザクション1 - SELECT FOR UPDATE文は行のXロックを取ります。
トランザクション2 - 行のXロックを取るために更新ステートメントが選択されています。
3.トランザクション1 - 挿入文を無視するには、行にSロックを取るしようとしますが、トランザクション1のXロックがすでにそう

デッドロックにつながるロックを待っている、私は、同時処理する方法を知りたいです更新とデッドロックにつながることなく、新しいイベント(行の更新ではなく)を私のテーブルに挿入する。
1.ロックの順序はどうすればよいですか?
2.同時更新と新しい行挿入がデッドロックなしで機能することを確認する方法。

任意の助けをいただければ幸いです:)

答えて

1

を私はいくつかの実験の後にそれを解決するために管理し、コアの問題は、Sの配列にした後、Xの1つのトランザクションで撮影したロックおよびXは、他に取らロック。基本的には、開始時に取られたSロックは、デッドロックを持つすべてのケースにつながっていました。
したがって、最初の文としてトランザクション外でinsert ignore文を移動しました。トランザクションは現在、Xロックを取るだけです。つまり、トランザクションの1つがXロックを取る別のトランザクションを待機します。

イベント1:新規イベントを挿入

result = Insert ignore into products where productId='X' values(); 
if result == null 
return 
end 
Transaction start 
select * from products where productId='X' for update ; // Take a X lock on the row to prevent new writes and compare the incoming event with the current event 
Insert into products values on duplicate key update values // insert into row and on duplicate key update values 
commit 

イベント2:両方のイベントは、私は避ける助けただけでXロックを競うトランザクションを、持っている、

Transaction start 
    select * from products where productId='X' for update ; // If a row exists then it will be locked else I know it does not exist 
    commit 

だから、既存のイベントを更新デッドロック。

+0

また、削除されたトランザクションを再度適用してください。トランザクションにデッドロックやその他のエラーが発生する可能性があるため、これを行う準備が必要です。 –

+0

あなたの提案に感謝@RickJames。はい、トランザクションの再開はオプションでした。しかし、これは繰り返しパターンであり、上記の解決策が問題を解決しました。一般的に、デッドロックには何がありますか?避けてはいけませんか? –

+0

デッドロックを回避できます。そして私はそれらを避けるために_trying_に同意します。しかし、このフォーラムでは、すべてのデッドロックを回避できると思う人が多く、そうしようとすると時間がかかりすぎるように思えます。私はあなた自身の問題を解決してうれしいです。 –

関連する問題