web-dev-qa-db-ja.com

トランザクションレベルをREAD UNCOMMITTEDに設定した後、MySQLが共有ロックを要求する理由

複数の並列バッチで処理ジョブを実行し、基本的には行の大きなチャンクを読み取っていくつかを更新します-このタスクの実行中に関連する値が更新されないことがわかっているため、ここで各トランザクションを呼び出す前に、低いトランザクションレベルを使用しても問題ありません。私が実行するプロシージャ:

set session transaction isolation level read uncommitted

次に、処理するIDのサブセットを取得するストアドプロシージャを呼び出します。全体的な操作のSQLFiddle: http://sqlfiddle.com/#!9/192d62 (やや工夫されていますが、元のクエリの構造を維持しています)

私が尋ねる理由は、デッドロックが引き続き発生し、モニター出力を調べると、別のスレッドが共有ロックを要求している間に、別のスレッドが同じスペースで排他ロックを保持している(またはその逆)ことです-そのトランザクションレベルを設定して、ニーズを回避しないでください共有ロックのために? repeatable-read以外の共有ロックを取得する理由はありますか?

InnoDBの使用。

show engine innodb statusからの関連するロック情報(SQLFiddleのテーブル名と一致するように編集):

*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 481628 page no 24944 n bits 112 index `PRIMARY` of table `events` trx id 27740892 lock mode S locks rec but not gap waiting

そして

*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 481628 page no 24944 n bits 112 index `PRIMARY` of table `events` trx id 27740898 lock_mode X locks rec but not gap waiting

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 481628 page no 25267 n bits 112 index `PRIMARY` of table `events` trx id 27740898 lock_mode X locks rec but not gap waiting

SQLFiddleのクエリ1と2は、エンジン出力の最初と2番目のエントリに一致することに注意してください。 PRIMARYキーはid列の上にのみあります(フィドルで確認できます)。

7
Michoel

「チャンクを読み取るが、更新するのは一部のみ」...

_SELECT a chunk -- separate transaction, don't care about locking, etc
munch on that chunk to figure out the 'few' ids to update
BEGIN;
    SELECT ... WHERE id IN (list of the few) FOR UPDATE;
    minimal other work
    UPDATE them;
COMMIT;   -- plus checks for deadlocks, etc, and restart the code.
_

このアイデアは、時間のかかるコードout ofを可能な限り多く引き、トランザクションを高速化して、競合の可能性を最小限に抑えることです。 (いいえ、おそらくallデッドロックは解消されませんが、役立つはずです。)

別のメモ:可能であれば、すべてのスレッドで同じ順序でテーブルを確認してください。デッドロックは、1つのスレッドがid IN (1,2)で再生するときに発生しますが、別のスレッドはIN (2,1)で再生します。両方がIN (1,2)を実行すると、デッドロックではなく遅延が発生します。

別のヒント:チャンクを小さくします。

1
Rick James