デッドロックはトランザクション データベースの中ではよく知られている問題ですが、ある特定のトランザクションを全く起動できないほど頻繁に起きる訳ではないのならば危険では有りません。 通常は、トランザクションがデッドロックの為にロールバックされたらそれを再発行できる準備が常にできているように、アプリケーションを書き込まなければいけません。
InnoDB
は自動行レベル
ロックを利用します。単一行を挿入または削除したばかりのトランザクションの場合でもデッドロックを得る事ができます。これは、これらの操作は実際は
「アトミック」
ではないからです。それらは自動的に挿入または削除された行の(可能であればいくつかの)インデックス
レコード上にロックを設定します。
次のテクニックを利用して、デッドロックに対処し、それらの発生の可能性を減らす事ができます:
SHOW ENGINE INNODB STATUS
を利用して最新のデッドロックの原因を究明してください。それで、アプリケーションがデッドロックを防ぐ様に調整する事ができます。
トランザクションがデッドロックのせいで失敗したら再発行できるように常に準備しておいてください。デッドロックは危険ではありません。もう一度やってみてください。
トランザクションを頻繁にコミットしてください。小さいトランザクションはコリジョンの傾向が少ないです。
もしロック読み取り(SELECT ... FOR
UPDATE
or ... LOCK IN SHARE
MODE
)を利用しているなら、READ
COMMITTED
のような低分離レベルを利用するように試みてください。
決まった順番でテーブルと行にアクセスしてください。するとトランザクションは明確な列になりデッドロックしません。
テーブルに適切なインデックスを追加してください。するとクエリがスキャンしなければいけないインデックス
レコードが減り、その結果ロックの設定が減ります。MySQL
サーバが、クエリにとってどのインデックスが最適だと認識するのかを究明する為に
EXPLAIN SELECT
を利用してください。
ロックの利用を少なくしてください。もし
SELECT
が古いスナップショットからデータを返す事を許容できるなら、それに条項
FOR UPDATE
か LOCK IN SHARE
MODE
を追加しないでください。同じトランザクション内のそれぞれの一貫した読み取りは、それ自体の新鮮なスナップショットから読み取りをするので、READ
COMMITTED
分離レベルを利用する事は良い事です。
もし他に方法がなければ、テーブル レベル
ロックを利用してトランザクションを直列化してください。LOCK
TABLES
を InnoDB
テーブルのようなトランザクション
テーブルと共に利用する正しい方法は、AUTOCOMMIT
= 0
を設定し、トランザクションを明示的にコミットするまでは
UNLOCK TABLES
をコールしないという方法です。例えば、もしテーブル
t1
に書き込み、テーブル
t2
から読み取る必要があれば、これを行う事ができます:
SET AUTOCOMMIT=0;
LOCK TABLES t1 WRITE, t2 READ, ...;
... do something with tables t1 and t2 here ...
COMMIT;
UNLOCK TABLES;
テーブル child_codes
内にテーブル レベル
ロックはトランザクションの列を整え、デッドロックを防ぎます。
トランザクションを直列化する別の方法は、単一行だけを含む補助
「セマフォ」
テーブルを作成する事です。各トランザクションが別のテーブルにアクセスする前にその行を更新させてください。そうすると、全てのトランザクションは連続で起こります。直列化ロックは行レベル
ロックなので、InnoDB
インスタント
デッドロック検出アルゴリズムもこの場合機能するという事に注意してください。MySQL
テーブル レベル
ロックでは、デッドロックを解決する為にタイムアウト法を利用しなければいけません。
LOCK TABLES
コマンドを利用するアプリケーション内では、AUTOCOMMIT=1
であれば MySQL は InnoDB
デーブル ロックを設定しません。