

# Lock:Relation
<a name="apg-waits.lockrelation"></a>

`Lock:Relation`イベントは、別のトランザクションによって現在ロックされているテーブルまたはビュー (リレーション) のロックを取得するためにクエリが待っているときに発生します。

**Topics**
+ [サポート対象エンジンバージョン](#apg-waits.lockrelation.context.supported)
+ [Context](#apg-waits.lockrelation.context)
+ [待機時間が増加する原因の可能性](#apg-waits.lockrelation.causes)
+ [アクション](#apg-waits.lockrelation.actions)

## サポート対象エンジンバージョン
<a name="apg-waits.lockrelation.context.supported"></a>

この待機イベント情報は、Aurora PostgreSQL のすべてのバージョンでサポートされています。

## Context
<a name="apg-waits.lockrelation.context"></a>

ほとんどの PostgreSQL コマンドは、テーブル内のデータへの同時アクセスを制御するために、暗黙のうちにロックを使用します。また、これらのロックは、アプリケーションコード内で`LOCK`コマンドによって明示的に使用することもできます。多くのロックモードは互いに互換性がないため、同じオブジェクトにアクセスしようとしているときにトランザクションをブロックすることがあります。これが起こると、Aurora PostgreSQL は`Lock:Relation`イベントを生成します。一般的な例をいくつか以下に示します。
+ `ACCESS EXCLUSIVE`のような排他的なロックは、すべての同時アクセスをブロックできます。`DROP TABLE`、`TRUNCATE`、`VACUUM FULL`、`CLUSTER`などのデータ定義言語 (DDL) オペレーションは、暗黙のうちに`ACCESS EXCLUSIVE`ロックを取得します。`ACCESS EXCLUSIVE` は、明示的にモードを指定しない `LOCK TABLE` ステートメントのデフォルトのロックモードでもあります。
+ テーブル上で`CREATE INDEX (without CONCURRENT)`を使用すると、`ROW EXCLUSIVE`ロックを取得するデータ操作言語 (DML) ステートメント`UPDATE`、`DELETE`、`INSERT`と競合します。

テーブルレベルのロックと競合するロックモードの詳細については、PostgreSQL ドキュメントの「[明示的なロック](https://www.postgresql.org/docs/13/explicit-locking.html)」を参照してください。

ブロックされたクエリとトランザクションは、通常、次のいずれかの方法でブロックを解除します。
+ クエリのブロック: アプリケーションがクエリをキャンセルするか、ユーザーがプロセスを終了できます。また、セッションのステートメントタイムアウトやデッドロック検出メカニズムによって、エンジンがクエリを強制終了させることもできます。
+ トランザクションのブロック: トランザクションが `ROLLBACK` または `COMMIT` を実行すると、トランザクションはブロックを停止します。ロールバックは、クライアントまたはネットワークの問題によってセッションが切断されたり、終了したときにも自動的に行われます。セッションは、データベースエンジンがシャットダウンされたり、システムがメモリ不足になったりしたときに終了できます。

## 待機時間が増加する原因の可能性
<a name="apg-waits.lockrelation.causes"></a>

`Lock:Relation` イベントが通常よりも頻繁に発生する場合、パフォーマンスの問題を示している可能性があります。代表的な原因としては、以下が挙げられます。

**テーブルロックの競合による同時セッションの増加**  
競合するロックモードで同じテーブルをロックするクエリによる同時セッションの数が増加する可能性があります。

**メンテナンスオペレーション**  
`VACUUM`や`ANALYZE`のようなヘルスメンテナンスオペレーションは、競合するロックの数を大幅に増加させる可能性があります。`VACUUM FULL`は`ACCESS EXCLUSIVE`のロックを、`ANALYZE`は`SHARE UPDATE EXCLUSIVE`のロックを取得します。どちらのタイプのロックも、`Lock:Relation`待機イベントを引き起こすことがあります。また、マテリアライズドビューのリフレッシュなどのアプリケーションデータのメンテナンスオペレーションも、ブロックされたクエリとトランザクションを増加することもあります。

**リーダーインスタンスをロックする**  
ライターとリーダーが保持しているリレーションロックの間に矛盾がある可能性があります。現在は、`ACCESS EXCLUSIVE` リレーションロックのみが、リーダーインスタンスにレプリケートされます。ただし、`ACCESS EXCLUSIVE` リレーションロックは、リーダーが保持する `ACCESS SHARE` リレーションロックと競合します。これにより、リーダーのロックリレーション待機イベントが増加する可能性があります。

## アクション
<a name="apg-waits.lockrelation.actions"></a>

待機イベントの原因に応じたさまざまなアクションをお勧めします。

**Topics**
+ [SQL ステートメントのブロックによる影響を軽減](#apg-waits.lockrelation.actions.reduce-blocks)
+ [メンテナンスオペレーションの影響を最小限に抑える](#apg-waits.lockrelation.actions.maintenance)
+ [リーダーロックをチェックする](#apg-waits.lockrelation.actions.readerlocks)

### SQL ステートメントのブロックによる影響を軽減
<a name="apg-waits.lockrelation.actions.reduce-blocks"></a>

SQL ステートメントのブロックによる影響を軽減するには、可能なところではアプリケーションコードを修正します。ブロックを減らすための 2 つの一般的な方法は以下のとおりです。
+ `NOWAIT` オプションを使用する: `SELECT` や `LOCK` ステートメントなど、一部の SQL コマンドはこのオプションをサポートしています。`NOWAIT`指示文は、ロックをすぐに取得できない場合、ロックへのクエリをキャンセルします。この方法は、ブロックされたセッションが、その後ろにあるブロックされたセッションが積み重なるのを防ぐのに役立ちます。

  例えば、トランザクション A がトランザクション B に保持されているロックを待っているとします。ここで、B がトランザクション C によってロックされているテーブルのロックをリクエストすると、トランザクション C が完了するまでトランザクション A がブロックされる可能性があります。ただし、トランザクション B が C のロックを要求するときに`NOWAIT`を使用する場合、トランザクションBは迅速に失敗し、トランザクション A が無期限に待機する必要がないことを保証できます。
+ `SET lock_timeout` を使用する: `lock_timeout` 値を設定して、SQL ステートメントがリレーションでロックを取得するのを待機する時間を制限します。指定したタイムアウト時間内にロックが取得されなかった場合、ロックを要求したトランザクションはキャンセルされます。この値はセッションレベルで設定します。

### メンテナンスオペレーションの影響を最小限に抑える
<a name="apg-waits.lockrelation.actions.maintenance"></a>

`VACUUM`や`ANALYZE`のようなメンテナンスオペレーションは重要です。これらのメンテナンス作業に関連する`Lock:Relation`待機イベントを見つけても、それらをオフにしないことをお勧めします。次のようなアプローチにより、これらの操作の影響を最小限に抑えることができます。
+ オフピーク時にメンテナンス操作をマニュアルで実行します。
+ オートバキュームタスクによる`Lock:Relation`待機をへらすには、必要なオートバキュームチューニングを実行します。オートバキュームのチューニングについては、*Amazon RDS ユーザーガイド*の「[Amazon RDS での PostgreSQL オートバキュームの使用](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Appendix.PostgreSQL.CommonDBATasks.Autovacuum.html)」を参照してください。

### リーダーロックをチェックする
<a name="apg-waits.lockrelation.actions.readerlocks"></a>

ライターとリーダーの同時セッションが、お互いをブロックするロックを保持しているかどうかを確認できます。これを行う 1 つの方法は、ロックのタイプとリレーションを返すクエリを実行することです。この表では、ライターセッションとリーダーセッションという 2 つの同時セッションに対する一連のクエリを見つけることができます。

再生プロセスは、リーダークエリをキャンセルする前に `max_standby_streaming_delay` の期間待機します。例に示すように、100 ms のロックタイムアウトは、デフォルトの `max_standby_streaming_delay` である30 秒をはるかに下回っています。ロックは問題になる前にタイムアウトします。


| 一連のイベント | Session | コマンドまたは出力 | 
| --- | --- | --- | 
| 指定された値で READER という環境変数を設定し、このエンドポイントで DB インスタンスに接続しようとします。 | リーダーセッション | CLI コマンド:<pre>export READER=aurorapg2.12345678910.us-west-1.rds.amazonaws.com<br /><br />psql -h $READER</pre><br />出力:

```
psql (15devel, server 10.14)
Type "help" for help.
``` | 
| WRITER という環境変数を設定し、このエンドポイントを使用して DB インスタンスに接続しようとします。 | ライターセッション | CLI コマンド:<pre>export WRITER=aurorapg1.12345678910.us-west-1.rds.amazonaws.com<br />psql -h $WRITER</pre><br />出力:

```
psql (15devel, server 10.14) 
Type "help" for help.
``` | 
| ライターセッションはライターインスタンスにテーブル t1 を作成します。 | ライターセッション | PostgreSQL クエリ:<pre>postgres=> CREATE TABLE t1(b integer);<br />CREATE TABLE</pre> | 
| ライターに競合するクエリがない場合、ACCESS EXCLUSIVE ロックはライターですぐに取得されます。 | ライターセッション | `ACCESS EXCLUSIVE` ロックの有効化 | 
| リーダーセッションは 100 ミリ秒のロックタイムアウトインターバルを設定します。 | リーダーセッション | PostgreSQL クエリ:<pre>postgres=> SET lock_timeout=100;<br />SET</pre> | 
| リーダーセッションはリーダーインスタンスでテーブル t1 からデータを読み込もうとします。 | リーダーセッション | PostgreSQL クエリ:<pre>postgres=> SELECT * FROM t1;</pre><br />サンプル出力:

```
b
---
(0 rows)
``` | 
| ライターセッションが t1 をドロップします。 | ライターセッション | PostgreSQL クエリ:<pre>postgres=> BEGIN;<br />BEGIN<br />postgres=> DROP TABLE t1;<br />DROP TABLE<br />postgres=></pre> | 
| クエリがタイムアウトし、リーダーでキャンセルされます。 | リーダーセッション | PostgreSQL クエリ:<pre>postgres=> SELECT * FROM t1;</pre><br />サンプル出力:

```
ERROR:  canceling statement due to lock timeout
LINE 1: SELECT * FROM t1;
                      ^
``` | 
| リーダーセッションが `pg_locks` および `pg_stat_activity` へのクエリを実行し、エラーの原因を特定します。 | リーダーセッション | PostgreSQL クエリ:<pre>postgres=> SELECT locktype, relation, mode, backend_type<br />postgres=> FROM pg_locks l, pg_stat_activity t1<br />postgres=> WHERE l.pid=t1.pid AND relation = 't1'::regclass::oid;</pre> | 
| 結果は、`aurora wal replay` プロセスがテーブル t1 で `ACCESS EXCLUSIVE` ロックをホールドしていることを示します。 | リーダーセッション | クエリ結果:<pre> locktype | relation |        mode         |   backend_type<br />----------+----------+---------------------+-------------------<br /> relation | 68628525 | AccessExclusiveLock | aurora wal replay<br />(1 row)</pre> | 