postgresテーブルロック

5735 ワード

12.3. 明示的なロック


PostgreSQLは、テーブル内のデータへの同時アクセスを制御するための様々なロック・モードを提供します.これらのモードは、MVCCが所望の動作を与えることができない場合に用いることができる.同様に、ほとんどのPostgreSQLコマンドは、参照されたテーブルがコマンド実行時に互換性のない方法で削除または変更されないことを保証するために、適切なロックを自動的に適用します.(例えば、他の同時動作がある場合、ALTER TABLEは同じテーブルでは実行できません.)
現在のデータベース・サーバに保持されているロックのリストを確認するには、システム・ビューpg_を使用します.locks.

12.3.1. テーブルレベルロック


次のリストには、使用可能なロックモードとPostgreSQLによって自動的に使用される環境が表示されます.コマンドLOCKでこれらのロックを明確に取得することもできます.これらのロックモードはすべて表レベルのロックであり、名前に単語「row」が含まれていても注意してください.これらのロックモードの名前は履歴によるものです.ある角度から言えば、これらの名前は各ロックモードの典型的な使い方を反映しているが、意味は同じである.2つのロックモードの間の本当の違いは、異なる衝突ロックの集合を持っていることです.2つのトランザクションは、同じ時点で同じテーブルに競合するロックを保持できません.(ただし、トランザクションは決して自身と競合しません.たとえば、テーブルでACCESS EXCLUSIVEを要求し、後でACCESS SHAREを要求できます.)非衝突ロックモードは、多くのトランザクションが同時に保持できます.特に、一部のロック・モードは自己衝突的であり(例えば、任意の時点でACCESS EXCLUSIVEモードは複数のトランザクションによって所有できない)、他の場所は自己衝突的ではないことに注意してください(例えば、ACCESS SHAREは複数のトランザクションによって所有できます).ロックが要求されると、トランザクションが終了するまでロックモードが継続します.
表レベルのロック・モード
ACCESS SHARE
のみ
ACCESS EXCLUSIVE競合.
SELECTとANALYZEコマンドは、参照されるテーブルにこのロックを要求します.通常、このロックモードは、変更せずに読み取り専用のテーブルを取得するコマンドによって要求されます.
ROW SHARE

EXCLUSIVEと
ACCESS EXCLUSIVEモード競合.
SELECT FOR UPDATEコマンドとSELECT FOR SHAREコマンドは、ターゲットテーブルにこのようなモードのロック(FOR UPDATE/FOR SHAREが参照されているすべてのテーブルにあるACCESS SHAREロック)を必要とします.
ROW EXCLUSIVE

SHARE,
SHARE ROW EXCLUSIVE,
EXCLUSIVEと
ACCESS EXCLUSIVEモード競合.
コマンドUPDATE、DELETE、INSERTはこのロックモードを自動的に要求します.(他のすべての参照されたテーブルのACCESS SHAREロックを加えます).通常、このロックは、テーブル内のデータを変更するクエリーによって要求されます.
SHARE UPDATE EXCLUSIVE
および
SHARE UPDATE EXCLUSIVE,
SHARE,
SHARE ROW EXCLUSIVE,
EXCLUSIVE,
および
ACCESS EXCLUSIVEモード競合.
このモードは、同時モードによってテーブルが変更されないことを保護します.
VACUUM.
VACUM(FULLオプションなし)は、このようなロックを要求します.
SHARE

ROW EXCLUSIVE,
SHARE UPDATE EXCLUSIVE,
SHARE ROW EXCLUSIVE,
EXCLUSIVE
および
ACCESS EXCLUSIVEモード競合.
このモードでは、テーブルの同時データ変更を回避します.
CREATE INDEX文では、このようなロックモードが要求されます.
SHARE ROW EXCLUSIVE

ROW EXCLUSIVE,
SHARE UPDATE EXCLUSIVE,
SHARE,
SHARE ROW EXCLUSIVE,
EXCLUSIVE,
および
ACCESS EXCLUSIVEモード競合.
どのPostgreSQLコマンドも、このようなロックモードを自動的に要求しません.
EXCLUSIVE

ROW SHARE,
ROW EXCLUSIVE,
SHARE UPDATE EXCLUSIVE,
SHARE,
SHARE ROW EXCLUSIVE,
EXCLUSIVEと
ACCESS EXCLUSIVE
パターンの競合.
このモードでは同時実行のみが許可されます
ACCESS SHAREロック、つまり、
このロック・モードを持つトランザクションと並列に実行できるのは、テーブルの読み取りのみです.
どのPostgreSQLコマンドも、ユーザー・テーブルでこのようなロック・モードを自動的に要求しません.ただし、一部の操作では、一部のシステム・テーブルで要求されます.
ACCESS EXCLUSIVE
すべてのモードと競合します.
(
ACCESS SHARE,
ROW SHARE,
ROW EXCLUSIVE,
SHARE UPDATE EXCLUSIVE,
SHARE,
SHARE ROW EXCLUSIVE,
EXCLUSIVE、および
ACCESS EXCLUSIVE).
このモードは、所有者(トランザクション)がテーブルに任意の方法でアクセスできる唯一のトランザクションであることを保証します.
ALTER TABLE、DROP TABLE、REINDEX、CLUSTER、VACUM FULLコマンドは、このようなロックを要求します.LOCK TABLEコマンドが必要なロックモードを明示的に宣言していない場合も、デフォルトのロックモードです.
ヒント:ACCESS EXCLUSIVEのみがSELECTをブロックします(FOR UPDATE/FOR SHARE文はありません).

12.3.2. 行レベルロック


表レベルのロックのほかに、行レベルのロックもあります.彼らは彼を並べたり共有したりすることができます.特定の行の排他行レベルロックは、行が更新されたときに自動的に要求されます.このロックは、トランザクションのコミットまたはロールバックまで保持されます.行レベルのロックはデータのクエリーに影響しません.同じローへの書き込みのみがブロックされます.
行を変更せずに行の排他的な行レベルのロックを要求するには、SELECT FOR UPDATEで行を選択します.特定のロー・レベル・ロックを要求すると、トランザクションは競合を心配することなく、ローを複数回更新できることに注意してください.
共有行レベルロックを1行に要求するには、SELECT FOR SHAREで行を選択します.共有ロックは、他のトランザクションが同じ共有ロックを要求することを阻止しません.ただし、他のトランザクションでは、更新、削除、または他のトランザクションが共有ロックを持っているローのロックは許可されません.このような企みはすべてブロックされ、共有ロックの解放を待っています.
PostgreSQLは、変更されたローに関する情報をメモリに保存しないため、一度にロックされたローの数に制限はありません.ただし、1行をロックするとディスクの書き込みが発生します.したがって、SELECT FOR UPDATEのように、選択した行を変更してロックされたことをマークするため、ディスク書き込みが発生します.
テーブル・レベルとロー・レベルのロックに加えて、ページ・レベルの共有/排他ピンは、共有バッファ内のテーブル・ページへの読み取り/書き込みアクセスを制御するためにも使用されます.これらのロックは、ローをキャプチャまたは更新した後、すぐに解放されます.アプリケーション・プログラマは、通常、ページ・レベルのロックに関心を持つ必要はありません.ここでは、完全なためだけに言及します.

12.3.3. デッドロック


明示的なロックの使用は、デッドロックの可能性を高める可能性があります.デッドロックとは、2つ(または複数)のトランザクションが互いに相手の期待を持つロックです.たとえば、トランザクション1がテーブルAに排他ロックを持ち、テーブルBに排他ロックを要求しようとし、トランザクション2がテーブルBの排他ロックを持っているのに、テーブルAに排他ロックを要求している場合、両方のトランザクションは実行できません.PostgreSQLは、デッドロック条件を自動的に検出し、この問題を解決するために当事者のトランザクションを終了し、他のトランザクションの完了を許可します.△具体的にどの事務が脱退されるかは予測しにくいし、このような予想に頼るべきではない.
デッドロックは行レベルのロックによっても発生する可能性があります(したがって、明確なロックが使用されていなくても発生する可能性があります).このような状況を考慮すると、2つの同時トランザクションが1つのテーブルを変更しています.最初のトランザクションが実行されました.
UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 11111;

これにより、指定したアカウントの行に行レベルロックが要求されます.次に、2番目のトランザクションを実行します.
UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 22222;

UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 11111;

最初のUPDATE文は、指定されたローでロー・レベル・ロックを要求することに成功し、ローの更新に成功しました.しかし、2番目のUPDATE文は、更新しようとしたローがロックされていることを発見し、ロックを保持しているトランザクションの終了を待っています.トランザクション2は、トランザクションが終了するのを待ってから実行されます.次に、トランザクションを実行します.
UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 22222;

トランザクション1は、指定したローにロー・レベルのロックを要求しようとしますが、トランザクション2がすでにこのようなロックを持っています.トランザクション2が完了するのを待っていますしたがって、トランザクションはトランザクション2によってブロックされ、トランザクション2もトランザクション1によってブロックされます.これがデッドロック条件です.PostgreSQLは、このような条件を検出し、トランザクションの1つを終了します.
デッドロックを防止する最善の方法は、通常、1つのデータベースを使用するすべてのアプリケーションが、複数のオブジェクトで一貫した順序でロックを要求することを保証することです.上記の例では、2つのトランザクションが同じ順序でローを更新すると、デッドロックは発生しません.また、1つのオブジェクトで要求される最初のロックが、そのオブジェクトに必要な最も高いロックモードであることを保証します.これらの問題を事前に確認できない場合は、デッドロックによって終了したトランザクションを現場で再試行することで処理できます.
デッドロック条件が検出されない限り、テーブル・レベル・ロックまたはロー・レベル・ロックを待機するトランザクションは、競合するロックの解放が不確定な時間を待機します.これは、アプリケーションが開いているトランザクションを持つ時間が長すぎると、ロック、ユーザー入力の待機などの良いことではありません.