

# Lock:tuple
<a name="apg-waits.locktuple"></a>

`Lock:tuple` イベントは、バックエンドプロセスがタプルのロックを取得するのを待っているときに発生します。

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

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

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

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

イベント`Lock:tuple`は、バックエンドがタプルのロック取得を待っている間、別のバックエンドが同じタプルで競合するロックを保持していることを示します。次の表では、セッションが`Lock:tuple`イベントを生成するシナリオを示します。


|  時間  |  セッション 1  |  セッション 2  |  セッション 3  | 
| --- | --- | --- | --- | 
| t1 | トランザクションをスタートします。 |  |  | 
| t2 | 行 1 を更新します。 |  |  | 
| t3 |  | 行 1 を更新します。セッションは、タプルの排他ロックを取得してから、セッション1がコミットまたはロールバックによってロックを解放するのを待機します。 |  | 
| t4 |  |  | 行 1 を更新します。セッションは、セッション 2 がタプルの排他ロックを解放するのを待機します。 | 

または、ベンチマークツール`pgbench`を使用してこの待機イベントをシミュレートすることができます。多数の同時セッションを構成して、カスタムSQLファイルでテーブル内の同じ行を更新します。

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

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

このイベントが通常よりも頻繁に表示される場合、パフォーマンスの問題を示している可能性があり、典型的な原因は次のとおりです。
+ `UPDATE`または`DELETE`ステートメントの実行により、同じタプルへの競合するロックを取得しようとしている同時セッションが多数あります。
+ 同時実行数の多いセッションでは、`FOR UPDATE`または`FOR NO KEY UPDATE`ロックモードを使用して`SELECT`ステートメントを実行します。
+ さまざまな要因により、アプリケーションまたは接続プールが同じオペレーションを実行するため、より多くのセッションを開きます。新しいセッションが同じ行を変更しようとすると、DB ロードのスパイクが発生して`Lock:tuple`が表示されることがあります。

詳細については、「PostgreSQL のドキュメント」の「[行レベルのロック](https://www.postgresql.org/docs/current/explicit-locking.html#LOCKING-ROWS)」を参照してください。

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

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

**Topics**
+ [アプリケーションロジックを調査する](#apg-waits.locktuple.actions.problem)
+ [ブロッカーセッションを見つける](#apg-waits.locktuple.actions.find-blocker)
+ [同時実行数が多いときにそれを減らす](#apg-waits.locktuple.actions.concurrency)
+ [ボトルネックのトラブルシューティング](#apg-waits.locktuple.actions.bottlenecks)

### アプリケーションロジックを調査する
<a name="apg-waits.locktuple.actions.problem"></a>

ブロッカーセッションが長い間`idle in transaction`ステートメントにあったかどうかを確認します。その場合は、短期的な解決策としてブロッカーセッションの終了を検討してください。`pg_terminate_backend` 関数を使用することもできます。この関数の詳細については、「PostgreSQL のドキュメント」の「[サーバーシグナリング関数](https://www.postgresql.org/docs/13/functions-admin.html#FUNCTIONS-ADMIN-SIGNAL)」を参照してください。

長期的な解決策としては、以下を実行してください。
+ アプリケーションロジックを調整します。
+ `idle_in_transaction_session_timeout` パラメータを使用します。このパラメータは、指定された時間より長くアイドル状態であったオープントランザクションを持つセッションを終了させます。詳細については、PostgreSQL ドキュメントの 「[Client Connection Defaults (クライアント接続のデフォルト)](https://www.postgresql.org/docs/current/runtime-config-client.html#GUC-IDLE-IN-TRANSACTION-SESSION-TIMEOUT)」 を参照してください。
+ 可能な限りオートコミットを使用します。詳細については、PostgreSQL ドキュメントの 「[Client authentication (クライアント認証)](https://www.postgresql.org/docs/current/ecpg-sql-set-autocommit.html)」 を参照してください。

### ブロッカーセッションを見つける
<a name="apg-waits.locktuple.actions.find-blocker"></a>

`Lock:tuple`の待機イベントが発生している間に、どのロックが互いに依存しているかを探して、ブロッカーとブロックされたセッションを特定します。詳細については、PostgreSQL wiki にの「[依存関係情報のロック](https://wiki.postgresql.org/wiki/Lock_dependency_information)」を参照してください。過去の`Lock:tuple`イベントを分析するには、Aurora 関数`aurora_stat_backend_waits`を使用します。

次の例では、`tuple`でフィルタリングして`wait_time`で順序付けし、すべてのセッションへのクエリを実行しています。

```
--AURORA_STAT_BACKEND_WAITS
      SELECT a.pid, 
             a.usename, 
             a.app_name, 
             a.current_query,
             a.current_wait_type, 
             a.current_wait_event, 
             a.current_state, 
             wt.type_name AS wait_type, 
             we.event_name AS wait_event, 
             a.waits, 
             a.wait_time
        FROM (SELECT pid, 
                     usename, 
                     left(application_name,16) AS app_name,
                     coalesce(wait_event_type,'CPU') AS current_wait_type,
                     coalesce(wait_event,'CPU') AS current_wait_event, 
                     state AS current_state,
                     left(query,80) as current_query,
                     (aurora_stat_backend_waits(pid)).* 
                FROM pg_stat_activity 
               WHERE pid <> pg_backend_pid()
                 AND usename<>'rdsadmin') a
NATURAL JOIN aurora_stat_wait_type() wt 
NATURAL JOIN aurora_stat_wait_event() we
WHERE we.event_name = 'tuple'
    ORDER BY a.wait_time;

  pid  | usename | app_name |                 current_query                  | current_wait_type | current_wait_event | current_state | wait_type | wait_event | waits | wait_time
-------+---------+----------+------------------------------------------------+-------------------+--------------------+---------------+-----------+------------+-------+-----------
 32136 | sys     | psql     | /*session3*/ update tab set col=1 where col=1; | Lock              | tuple              | active        | Lock      | tuple      |     1 |   1000018
 11999 | sys     | psql     | /*session4*/ update tab set col=1 where col=1; | Lock              | tuple              | active        | Lock      | tuple      |     1 |   1000024
```

### 同時実行数が多いときにそれを減らす
<a name="apg-waits.locktuple.actions.concurrency"></a>

`Lock:tuple`イベントは、特にワークロードの多い時間帯に、コンスタントに発生する可能性があります。このような状況では、非常にビジーな行への同時実行数を減らすことを検討してください。多くの場合、キューまたはブールロジックを制御する行の数行だけで、これらの行は非常にビジーになります。

ビジネス要件、アプリケーションロジック、およびワークロードタイプに応じて、さまざまなアプローチを使用することで、競合を減らすことができます。例えば、次のオペレーションを実行できます。
+ テーブルとデータロジックを再設計し、競合を減らします。
+ アプリケーションロジックを変更して、行レベルで高い競合を減らします。
+ 行レベルのロックを活用してクエリを再設計します。
+ `NOWAIT`節はリトライオペレーションで使用します。
+ 楽観的かつハイブリッドロックロジックの同時実行制御の使用を検討します。
+ データベースの隔離レベルの変更を検討してください。

### ボトルネックのトラブルシューティング
<a name="apg-waits.locktuple.actions.bottlenecks"></a>

`Lock:tuple`は、CPU の枯渇や Amazon EBS 帯域幅の最大使用率などのボトルネックを発生させることがあります。ボトルネックを減らすには、次のアプローチを検討します。
+ インスタンスクラスタイプをスケールアップします。
+ リソースを大量に消費するクエリを最適化します。
+ アプリケーションロジックを変更します。
+ ほとんどアクセスされないデータをアーカイブします。