9.3. 明示的ロック

PostgreSQLは、テーブル内のデータに対する同時アクセスを制御するためにさまざまな種類のロックモードを備えています。 これらのモードは、MVCC によって必要な動作を得ることができない場合、アプリケーションのロック制御に使用することができます。 また、ほとんどの PostgreSQL コマンドでは、参照されるテーブルがそのコマンドの実行中に別の方法で削除もしくは変更されていないことを確実にするために、適切なモードのロックを自動的に獲得します。 (たとえば、ALTER TABLE コマンドは、同じテーブルに対する他の操作とは同時に実行できません。)

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

以下のリストに、PostgreSQL で自動的に制御される、使用可能なロックモードおよびこれらが使用されるコンテキストを示します。 これらのロックモードは、たとえその名前に "row" (行) という言葉がついていても、すべてテーブルレベルのロックであることに注意してください。 ロックモードの名前は歴史的なものです。 これらの名前は、各ロックモードの代表的な使用方法をある程度表しています。しかし、意味的にはすべて同じです。 ロックモード間における唯一の実質的な差異は、それぞれが競合するロックモードのセットです。 2 つのトランザクションで、競合するモードのロックを同時に同一テーブル上に保持することはできません。 (しかし、トランザクションは自分自身とは決して競合しません。たとえば、ACCESS EXCLUSIVE ロックを獲得し、その後同じテーブルに ACCESS SHARE ロックを獲得することができます。) 競合しないロックモードは、多くのトランザクションで同時に保持することが可能です。 特に、ロックモードには、自己競合するもの (たとえば、ACCESS EXCLUSIVE は同時に複数のトランザクションで保持することは不可能) と、自己競合しないもの (たとえば、ACCESS SHARE は複数のトランザクションで保持可能) があることに注意してください。 一旦獲得されると、ロックモードはトランザクションが終了するまで保持されます。

データベースサーバにおいて、現在未解決のロックのリストを調べるには、pg_locks システムビューを使用してください。 ロックマネージャサブシステムの状態の表示に関する詳細は、PostgreSQL 7.3 管理者用ガイドを参照してください。

テーブルレベルロックモード

ACCESS SHARE

ACCESS EXCLUSIVE ロックモードとのみ競合します。

SELECT コマンドにより、参照されるテーブルに対してこのモードのロックが獲得されます。 通常、テーブルの読み取りのみで変更を行なわない問い合わせであればすべて、このロックモードを獲得します。

ROW SHARE

EXCLUSIVE および ACCESS EXCLUSIVE ロックモードと競合します。

SELECT FOR UPDATE コマンドにより、(参照はされているが、FOR UPDATE として選択はされていない他のテーブルに対する ACCESS SHARE ロックに加えて) 対象となるテーブル上にこのモードのロックを獲得します。

ROW EXCLUSIVE

SHARESHARE ROW EXCLUSIVEEXCLUSIVE、および ACCESS EXCLUSIVE ロックモードと競合します。

UPDATEDELETE、および INSERT コマンドは、(参照される他のすべてのテーブルに対する ACCESS SHARE ロックに加えて) 対象となるテーブル上にこのモードのロックを獲得します。 通常、このロックモードは、テーブルのデータを変更する問い合わせにより獲得されます。

SHARE UPDATE EXCLUSIVE

SHARE UPDATE EXCLUSIVESHARESHARE ROW EXCLUSIVEEXCLUSIVE、および ACCESS EXCLUSIVE ロックモードと競合します。 このモードにより、同時実行中のスキーマによる変更および VACUUM コマンドの実行から、テーブルを保護します。

VACUUM コマンドによって獲得されます (FULL オプション無し)。

SHARE

ROW EXCLUSIVESHARE UPDATE EXCLUSIVESHARE ROW EXCLUSIVEEXCLUSIVE、および ACCESS EXCLUSIVE ロックモードと競合します。 このモードにより、同時実行されるデータ変更からテーブルを保護します。

CREATE INDEX によって獲得されます。

SHARE ROW EXCLUSIVE

ROW EXCLUSIVESHARE UPDATE EXCLUSIVESHARESHARE ROW EXCLUSIVEEXCLUSIVE、および ACCESS EXCLUSIVE ロックモードと競合します。

このロックモードを自動的に獲得する PostgreSQL コマンドはありません。

EXCLUSIVE

ROW SHAREROW EXCLUSIVESHARE UPDATE EXCLUSIVESHARESHARE ROW EXCLUSIVEEXCLUSIVE、および ACCESS EXCLUSIVE ロックモードと競合します。 このモードは、同時実行される ACCESS SHARE のみを許可します。たとえば、このロックモードを保持するトランザクションと並行して実行できる処理は、テーブルの読み取りだけです。

このロックモードを自動的に獲得する PostgreSQL コマンドはありません。

ACCESS EXCLUSIVE

すべてのロックモード (ACCESS SHAREROW SHAREROW EXCLUSIVESHARE UPDATE EXCLUSIVESHARESHARE ROW EXCLUSIVEEXCLUSIVE、および ACCESS EXCLUSIVE) と競合します。 このモードにより、その保持者以外にテーブルにアクセスするトランザクションがないことが保証されます。

ALTER TABLEDROP TABLE、および VACUUM FULL コマンドによって獲得されます。 また、明示的にモードを指定しなければ、これが LOCK TABLE 文を使用する際のデフォルトのロックモードです。

Note: ACCESS EXCLUSIVE ロックのみが、SELECT (FOR UPDATE 無し) 文をブロックします。

9.3.2. 行レベルロック

テーブルレベルロックに加えて、行レベルのロックもあります。 指定された行に対する行レベルロックは、行が更新 (または削除、または更新対象としてマーク) されると、自動的に獲得されます。 このロックは、トランザクションがコミットまたはロールバックするまで保持されます。 行レベルロックは、データの問い合わせには影響を与えません。行レベルロックは、同じ行に対する書き込みのみをブロックします。 実際に行を変更せずに行に対して行レベルロックを獲得するには、該当する行を SELECT FOR UPDATE で選択してください。 いったん特定の行レベルロックが獲得されると、競合のおそれ無しに、そのトランザクションでは何度でも行を変更できることに注意してください。

PostgreSQL では、メモリ上に変更された行の情報を記憶しないため、同時にロックできる行数の上限はありません。しかし、行をロックする際に、ディスクに書き込む作業が発生するかもしれません。 したがって、たとえばSELECT FOR UPDATEは選択された行に、印を付けるように変更させ、ディスクにそのことを書き込むことになります。

テーブルと行ロックに加え、ページレベルの共有/排他ロックがあり、これらは共有バッファプールにあるテーブルページへの読み書きのアクセスを管理するために使用されます。これらのロックはタプルが取得されたときや更新されたときに、即座に解除されます。アプリケーション開発者は特にページレベルロックを考慮する必要はありません。 ロックについて全てを説明したかったためページレベルロックを説明しました。

9.3.3. デッドロック

明示的なロックの使用は、デッドロックの原因となる可能性があります。デッドロックとは、2 つ (もしくはそれ以上) のトランザクションにおいて、それぞれが、他方のトランザクションが必要とするロックを保持していることです。 たとえば、トランザクション 1 がテーブル A に排他ロックを獲得しており、これからテーブル B に排他ロックを獲得しようとしている際に、トランザクション 2 がすでにテーブル B に排他ロックを獲得済みで、これからテーブル A に排他ロックを獲得しようとする場合、どちらのトランザクションも処理ができません。 PostgreSQL では、自動的にデッドロック状況を防ぎ、関係するトランザクションの一方をアボートすることにより、この状況を解決し、もう一方のトランザクションの処理を完了させます。 (どちらのトランザクションをアボートするかを、正確に予期するのは難しく、信頼すべきではありません。)

デッドロックを防ぐ最も良い方法は、データベースを使用するすべてのアプリケーションが、整合性のある順序で複数のオブジェクトに対するロックを獲得することです。 また、トランザクション内のオブジェクトに対して獲得した最初のロックを、そのオブジェクトについて必要な最高位のモードとすることも、デッドロックを防ぐ方法の 1 つです。 上記のように事前に変更することが不可能な場合、デッドロックのためにアボートされたトランザクションを再試行することにより、デッドロックを簡単に処理することができます。

デッドロック状況が検出されなければ、テーブルレベルロックもしくは行レベルロックを要求するトランザクションは、競合するロックが解放されるまで、無期限に待機します。 したがって、アプリケーションで長時間 (たとえば、ユーザが入力するまでの待ち時間) トランザクションをオープンしたまま保持しておくのは、推奨されません。