4

階層構造で共通の属性を持つモデルのセットを同時に操作するためにclosure_treeを使用するときに、データベースのデッドロックを回避するにはどうすればよいですか?

それらは次のフレーバーで存在します:

発行する場合#append/prepend_sibling

Mysql2::Error: Deadlock found when trying to get lock; try restarting transaction:
  UPDATE `elements` SET `sort_order` = `sort_order` + 1 WHERE (`parent_id` = 28035 AND `sort_order` >= 1)

Mysql2::Error: Deadlock found when trying to get lock; try restarting transaction:
  UPDATE `elements` SET `sort_order` = `sort_order` - 1 WHERE (`parent_id` = 21168 AND `sort_order` <= -1)

クロージャーテーブルを再構築する場合

Mysql2::Error: Deadlock found when trying to get lock; try restarting transaction:
  DELETE FROM `element_hierarchies`
   WHERE descendant_id IN (
     SELECT DISTINCT descendant_id
     FROM ( SELECT descendant_id
       FROM `element_hierarchies`
       WHERE ancestor_id = 16332
     ) AS x )
     OR descendant_id = 16332

Mysql2::Error: Deadlock found when trying to get lock; try restarting transaction: 
  INSERT INTO `element_hierarchies` (`ancestor_id`, `descendant_id`, `generations`) VALUES (30910, 30910, 0)

with_advisory_lockは有望に見えます。何かご意見は?

4

2 に答える 2

3

こちらのclosure_treeの作者:

Heal 氏のアドバイスは、おおむね正しいものです。デッドロックを防ぐために、同じ順序でテーブル ロックを取得する必要があります。ただし、この場合、デッドロックは階層テーブル内の行レベルのロックが原因です。

with_advisory_lockを使用することをお勧めします。私はちょうど Closure_tree 用のライブラリを作成しました。バージョン >= 3.7.0 を使用している場合は、クラス レベル#rebuild#find_or_create_by_pathメソッドを保護するアドバイザリ ロックが既に存在します。

(少なくとも MySQL と PostgreSQL では) アドバイザリ ロックの問題は、トランザクションの境界を尊重しないことです。アドバイザリ ロックを取得しようとするため、ここでは注意が必要です。書き込みのために階層テーブルにテーブル ロックを追加する必要があるかもしれませんが、それは最悪のケースです。

issue 41をオープンしました。そこで追跡できます。最初に行うことは、並列テストで確実にデッドロックを再現することです。と に対してそれを行うテストが既にあり#rebuildます#find_or_create_by_path

于 2013-02-10T18:09:22.517 に答える
2

あるトランザクションが別のトランザクションとどのように連携するかを考慮する必要があります。最善の策は、最初に読み取り (選択) を行い、その後に SQL を記述することです。また、書き込み SQL が同じ順序でテーブルを使用することを確認します。つまり、どちらの場合も、テーブル A に書き込み、次に B に書き込みます。これにより、他のトランザクションによるロックを必要とする別のトランザクションによって保持されている間にロックが必要になるのを防ぐことができます。

または、デッドロックを検出して適切なアクションを実行することもできます。最初から避けることをお勧めします。

于 2013-02-07T15:19:04.947 に答える