

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# Lock:tuple
<a name="wait-event.locktuple"></a>

`Lock:tuple` 事件表示後端程序正等待在元組上取得鎖定。

**Topics**
+ [支援的引擎版本](#wait-event.locktuple.context.supported)
+ [Context](#wait-event.locktuple.context)
+ [等待變多的可能原因](#wait-event.locktuple.causes)
+ [動作](#wait-event.locktuple.actions)

## 支援的引擎版本
<a name="wait-event.locktuple.context.supported"></a>

所有 RDS for PostgreSQL 版本都支援此等待事件資訊。

## Context
<a name="wait-event.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="wait-event.locktuple.causes"></a>

此事件比平時更常出現時，可能表示有效能問題，典型原因包括：
+ 大量並行工作階段執行 `UPDATE` 或 `DELETE` 陳述式，嘗試取得同一個元組的衝突鎖定。
+ 高度並行工作階段使用 `FOR UPDATE` 或 `FOR NO KEY UPDATE` 鎖定模式來執行 `SELECT` 陳述式。
+ 各種因素迫使應用程式或連線集區開啟更多工作階段來執行相同的操作。由於新的工作階段嘗試修改相同的資料行，資料庫負載會激增，並出現 `Lock:tuple`。

如需詳細資訊，請參閱 PostgreSQL 文件中的[資料列層級鎖定](https://www.postgresql.org/docs/current/explicit-locking.html#LOCKING-ROWS)。

## 動作
<a name="wait-event.locktuple.actions"></a>

根據等待事件的原因，我們會建議不同的動作。

**Topics**
+ [調查應用程式邏輯](#wait-event.locktuple.actions.problem)
+ [尋找引起封鎖的工作階段](#wait-event.locktuple.actions.find-blocker)
+ [減少高度並行](#wait-event.locktuple.actions.concurrency)
+ [瓶頸疑難排解](#wait-event.locktuple.actions.bottlenecks)

### 調查應用程式邏輯
<a name="wait-event.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 文件中的[用戶端連線預設值](https://www.postgresql.org/docs/current/runtime-config-client.html#GUC-IDLE-IN-TRANSACTION-SESSION-TIMEOUT)。
+ 盡可能使用自動遞交。如需詳細資訊，請參閱 PostgreSQL 文件中的 [SET AUTOCOMMIT](https://www.postgresql.org/docs/current/ecpg-sql-set-autocommit.html)。

### 尋找引起封鎖的工作階段
<a name="wait-event.locktuple.actions.find-blocker"></a>

`Lock:tuple` 等待事件發生時，請查明哪些鎖定彼此相依，以找出引起封鎖和被封鎖的工作階段。如需詳細資訊，請參閱 PostgreSQL Wiki 中的[鎖定相依性資訊](https://wiki.postgresql.org/wiki/Lock_dependency_information)。

下列範例查詢所有工作階段，篩選 `tuple` 並依據 `wait_time` 排序。

```
SELECT blocked_locks.pid AS blocked_pid,
         blocking_locks.pid AS blocking_pid,
         blocked_activity.usename AS blocked_user,
         blocking_activity.usename AS blocking_user,
         now() - blocked_activity.xact_start AS blocked_transaction_duration,
         now() - blocking_activity.xact_start AS blocking_transaction_duration,
         concat(blocked_activity.wait_event_type,':',blocked_activity.wait_event) AS blocked_wait_event,
         concat(blocking_activity.wait_event_type,':',blocking_activity.wait_event) AS blocking_wait_event,
         blocked_activity.state AS blocked_state,
         blocking_activity.state AS blocking_state,
         blocked_locks.locktype AS blocked_locktype,
         blocking_locks.locktype AS blocking_locktype,
         blocked_activity.query AS blocked_statement,
         blocking_activity.query AS blocking_statement
    FROM pg_catalog.pg_locks blocked_locks
    JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_activity.pid = blocked_locks.pid
    JOIN pg_catalog.pg_locks blocking_locks
        ON blocking_locks.locktype = blocked_locks.locktype
        AND blocking_locks.DATABASE IS NOT DISTINCT FROM blocked_locks.DATABASE
        AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation
        AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page
        AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple
        AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid
        AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid
        AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid
        AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid
        AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid
        AND blocking_locks.pid != blocked_locks.pid
    JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid
    WHERE NOT blocked_locks.GRANTED;
```

### 減少高度並行
<a name="wait-event.locktuple.actions.concurrency"></a>

`Lock:tuple` 事件可能持續發生，尤其在忙碌的工作負載時段。在此情況下，對於非常忙碌的資料列，請考慮減少高度並行。通常只有少數資料列控制佇列或布林邏輯，使得這些資料列非常忙碌。

您可以根據商業需求、應用程式邏輯和工作負載類型，使用不同方法來減少並行。例如，您可以執行下列動作：
+ 重新設計資料表和資料邏輯來減少高度並行。
+ 變更應用程式邏輯來減少資料列層級的高度並行。
+ 善用並重新設計含有資料列層級鎖定的查詢。
+ 對重試操作使用 `NOWAIT` 子句。
+ 考慮使用樂觀和混合鎖定邏輯並行控制。
+ 考慮變更資料庫隔離層級。

### 瓶頸疑難排解
<a name="wait-event.locktuple.actions.bottlenecks"></a>

`Lock:tuple` 可能發生瓶頸，例如 CPU 不足或 Amazon EBS 頻寬耗盡。若要減少瓶頸，請考慮下列方法：
+ 擴充執行個體類別類型的規模。
+ 將消耗大量資源的查詢最佳化。
+ 變更應用程式邏輯。
+ 封存不常存取的資料。