web-dev-qa-db-ja.com

単純な削除クエリでのMysql / innodbデッドロック

Mysql/innodb 8.0.16、コミットされたトランザクションの読み取り、トランザクション内のいくつかのステートメントにより、非インターセプト行の削除時にデッドロックが発生します。何が起こっているのかを理解しようとしています:

------------------------
LATEST DETECTED DEADLOCK
------------------------
2019-05-14 21:57:44 0x7fe9546c6700
*** (1) TRANSACTION:
TRANSACTION 2852, ACTIVE 0 sec fetching rows
mysql tables in use 1, locked 1
LOCK WAIT 8 lock struct(s), heap size 1136, 14 row lock(s), undo log entries 25
MySQL thread id 146, OS thread handle 140640122267392, query id 1586 localhost 127.0.0.1 oc5z updating
DELETE FROM deal_product_rows_tmp WHERE batch_no=7533
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 11 page no 4 n bits 88 index PRIMARY of table `db1`.`deal_product_rows_tmp` trx id 2852 lock_mode X locks rec but not gap waiting
Record lock, heap no 14 PHYSICAL RECORD: n_fields 11; compact format; info bits 0
 0: len 4; hex 8000650e; asc   e ;;
 1: len 6; hex 000000000b25; asc      %;;
 2: len 7; hex 81000001340110; asc     4  ;;
 3: len 4; hex 80001d6e; asc    n;;
 4: len 4; hex 8010a626; asc    &;;
 5: len 4; hex 8000253a; asc   %:;;
 6: len 4; hex 8000986e; asc    n;;
 7: len 4; hex 80000002; asc     ;;
 8: len 30; hex 415254455820d184d0bed180d0bcd18b20d0bfd180d18fd0bcd0bed183d0; asc WRX                         ; (total 81 bytes);
 9: len 8; hex 0000000000208c40; asc        @;;
 10: len 1; hex 4d; asc M;;

*** (2) TRANSACTION:
TRANSACTION 2853, ACTIVE 0 sec starting index read
mysql tables in use 1, locked 1
8 lock struct(s), heap size 1136, 3 row lock(s), undo log entries 12
MySQL thread id 147, OS thread handle 140640120497920, query id 1594 localhost 127.0.0.1 oc5z updating
DELETE FROM deal_product_rows_tmp WHERE batch_no=7534
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 11 page no 4 n bits 88 index PRIMARY of table `db1`.`deal_product_rows_tmp` trx id 2853 lock_mode X locks rec but not gap
Record lock, heap no 14 PHYSICAL RECORD: n_fields 11; compact format; info bits 0
 0: len 4; hex 8000650e; asc   e ;;
 1: len 6; hex 000000000b25; asc      %;;
 2: len 7; hex 81000001340110; asc     4  ;;
 3: len 4; hex 80001d6e; asc    n;;
 4: len 4; hex 8010a626; asc    &;;
 5: len 4; hex 8000253a; asc   %:;;
 6: len 4; hex 8000986e; asc    n;;
 7: len 4; hex 80000002; asc     ;;
 8: len 30; hex 415254455820d184d0bed180d0bcd18b20d0bfd180d18fd0bcd0bed183d0; asc WRX                         ; (total 81 bytes);
 9: len 8; hex 0000000000208c40; asc        @;;
 10: len 1; hex 4d; asc M;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 11 page no 4 n bits 96 index PRIMARY of table `db1`.`deal_product_rows_tmp` trx id 2853 lock_mode X locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 11; compact format; info bits 32
 0: len 4; hex 80006502; asc   e ;;
 1: len 6; hex 000000000b24; asc      $;;
 2: len 7; hex 0200000128012d; asc     ( -;;
 3: len 4; hex 80001d6d; asc    m;;
 4: len 4; hex 80109d40; asc    @;;
 5: len 4; hex 800023ba; asc   # ;;
 6: len 4; hex 800098e6; asc     ;;
 7: len 4; hex 80000007; asc     ;;
 8: len 9; hex 466f696c20676c7565; asc Foil glue;;
 9: len 8; hex 0000000000005940; asc       Y@;;
 10: len 1; hex 4d; asc M;;

*** WE ROLL BACK TRANSACTION (2)

テーブル構造:

    CREATE TABLE `deal_product_rows_tmp` (
    `batch_no` int(11) NOT NULL,
    `bitrix_id` int(11) NOT NULL,
    `deal_id` int(11) NOT NULL,
    `product_id` int(11) NOT NULL,
    `quantity` int(11) NOT NULL,
    `product_name` varchar(1000) NOT NULL,
    `price` double DEFAULT NULL,
    `status` varchar(50) NOT NULL,
    `tmp_id` int(11) NOT NULL AUTO_INCREMENT,
    PRIMARY KEY (`tmp_id`),
    KEY `idx_deal_row_tmp_deal` (`deal_id`),
    KEY `idx_deal_row_tmp_batchno` (`batch_no`)
    ) ENGINE=InnoDB AUTO_INCREMENT=7500 DEFAULT CHARSET=utf8 

1)クエリは分離された行を削除しますが、同じ行でどのようにRECORD LOCKSが可能ですか?

2)同じ行にロックを適用するトランザクションがある場合でも、なぜデッドロックになるのですか?なぜ最初のトランザクションは2番目のロックが解放されるまで待てないのですか?

UPDATE1:アプリのロジック-Spring BootアプリがCRMシステムからRESTコールを受信し、クライアントの注文(取引)製品の行をフェッチして、マイクロdwhでデータを同期しようとしています。 RCトランザクションには複数のステートメントがあり、このアプリに実装されているトランザクションのタイプは1つだけです。最初のステートメントREPLACE INTO取引テーブル。これにより、同じDeal_idでのバッチの並列処理が防止されます(現時点ではFKキーは使用されません)。次に、トランザクションは、受信した製品行を「tmp」テーブルに挿入し、ターゲットの「dwh」テーブルと比較します。欠落している取引商品の挿入、「削除された」商品行のステータスの更新。 Deal_product_rows_tmp(デッドロックのポイント)から「tmp」データを削除する次のステップと最後のステップ-バッチに完了のマークを付ける(バッチセットのステータスを更新する= ..テーブルの場所Batch_no =?)

3
Triffids

通常、人々は ギャップロック と、テーブル全体のインデックスの不良または予想よりも多くのレコードによる余分なロックについて混乱していますが、それはあなたのケースではありません。

  • 適切なセカンダリインデックスがある
  • ロックはギャップロックが不要であることを宣言します(期待どおり)
  • ロック/必要なレコードが異なります
  • idx_deal_row_tmp_batchnoインデックスを削除しないと、問題を再現できません
  • レコードは同じページにありますが、理論的にはお互いに影響を与えるべきではありません

限られた情報から、私が推測できるのは:

  • 複数のレコードをバッチで削除しています(1つは3つの行ロックを保持し、他の14つは保持しているため)。
  • 競合状態が発生し、トランザクション1はbatch_no=7533レコードをロックでき、トランザクション2はbatch_no=7534をロックできますが、別々のクエリ(ただし、同じトランザクション)で更新することもできます。その他の記録
  • 依存関係が検出され、InnoDBが最新のものを強制終了して、無限ループ(デッドロック)を防止します

それ以外の場合は無限に待機するため、彼らはただ待つことはできません(相互に依存しているため)。サイクルでない場合、2番目のトランザクションは実際に最大innodb_lock_wait_timeout秒待機します。

あなたができるいくつかの戦略があります:

  • デッドロックが一般的でない場合は監視し、何もしない(アプリケーションで再試行する)。エラーが頻繁に発生しない限り、大きなペナルティにはなりません
  • 衝突を防ぐために更新戦略を変更します(たとえば、レイテンシが増加する可能性がありますが、単一の大きなバッチで削除します)、トランザクションごとに1行を削除し、アプリケーションの削除を調整します。
  • 新しい8.0機能SKIP LOCKEDおよびNOWAITを使用して、すぐにロックされた行を無視します。特に、バッチプロセスの場合は、次のように機能します。 https://mysqlserverteam.com/mysql-8-0-1-using -skip-locked-and-nowait-to-handle-hot-rows /
4
jynus