

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

# Lock:advisory
<a name="apg-waits.lockadvisory"></a>

`Lock:advisory` 事件表示 PostgreSQL 應用程式使用鎖定來協調多個工作階段的活動。

**Topics**
+ [相關的引擎版本](#apg-waits.lockadvisory.context.supported)
+ [Context](#apg-waits.lockadvisory.context)
+ [原因](#apg-waits.lockadvisory.causes)
+ [動作](#apg-waits.lockadvisory.actions)

## 相關的引擎版本
<a name="apg-waits.lockadvisory.context.supported"></a>

此等待事件資訊與 Aurora PostgreSQL 9.6 版及更新版本有關。

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

PostgreSQL 諮詢鎖定是應用程式層級的合作式鎖定，由使用者的應用程式碼明確鎖定和解除鎖定。應用程式可以使用 PostgreSQL 諮詢鎖定來協調多個工作階段的活動。不同於物件層級或資料列層級的一般鎖定，應用程式完全控制鎖定的生命週期。如需詳細資訊，請參閱 PostgreSQL 文件中的[諮詢鎖定](https://www.postgresql.org/docs/12/explicit-locking.html#ADVISORY-LOCKS)。

諮詢鎖定可以在交易結束之前釋放，或由工作階段跨交易保留。但系統強制的隱含鎖定不是如此，例如 `CREATE INDEX` 陳述式在資料表上取得的存取獨佔鎖定。

如需用來取得 (鎖定) 和釋放 (解除鎖定) 諮詢鎖定的函數描述，請參閱 PostgreSQL 說明文件中的[諮詢鎖定函數](https://www.postgresql.org/docs/current/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS)。

諮詢鎖定是在一般 PostgreSQL 鎖定系統上實作，在 `pg_locks` 系統檢視表中可見。

## 原因
<a name="apg-waits.lockadvisory.causes"></a>

明確使用此鎖定類型的應用程式可完全控制鎖定。如果查詢為每個資料列都取得諮詢鎖定，可能會造成鎖定激增或長期累積。

當查詢取得的鎖定比查詢傳回的資料列更多時，就會出現這些現象。應用程式最終必須釋放每個鎖定，但如果是在未傳回的資料列上取得鎖定，則應用程式無法找齊全部的鎖定。

下列範例來自 PostgreSQL 文件中的[諮詢鎖定](https://www.postgresql.org/docs/12/explicit-locking.html#ADVISORY-LOCKS)。

```
SELECT pg_advisory_lock(id) FROM foo WHERE id > 12345 LIMIT 100;
```

在此範例中，只有在內部選取資料列並鎖定其 ID 值之後，`LIMIT` 子句才能停止查詢的輸出。突然發生這種情況表示資料量不斷增加，導致規劃工具選擇另一個未經過開發期間測試的執行計劃。在此情況下，發生累積起因於應用程式對每個已鎖定的 ID 值,明確呼叫 `pg_advisory_unlock`。但是，在此情況下，找不到在未傳回的資料列上取得的鎖定集。因為是在工作階段層級取得鎖定，交易結束時不會自動釋放鎖定。

鎖定嘗試受阻次數激增的另一個可能原因是意外衝突。在這些衝突中，應用程式的無關聯部分不慎共用相同的鎖定 ID 空間。

## 動作
<a name="apg-waits.lockadvisory.actions"></a>

檢閱諮詢鎖定的應用程式用量，並詳述在應用程式流程中何處和何時取得和釋放每一種諮詢鎖定。

查明究竟是工作階段取得太多鎖定，還是長時間執行的工作階段未及早釋放鎖定，導致鎖定逐漸堆積。您可以使用 `pg_terminate_backend(pid)` 結束工作階段，以免工作階段層級鎖定逐漸堆積。

等待諮詢鎖定的用戶端以 `wait_event_type=Lock` 和 `wait_event=advisory` 出現在 `pg_stat_activity` 中。您可以在 `pg_locks` 系統檢視表中查詢相同的 `pid`，尋找 `locktype=advisory` 和 `granted=f`，以取得特定的鎖定值。

然後，您可以在 `pg_locks` 中查詢 `granted=t` 的同一個諮詢鎖定，以識別引起封鎖的工作階段，如下列範例所示。

```
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;
```

所有諮詢鎖定 API 函數都有兩組引數，可能是一個 `bigint` 引數或兩個 `integer` 引數：
+ 如果 API 函數有一個 `bigint` 引數，則前段 32 位元在 `pg_locks.classid` 中，後段 32 位元在 `pg_locks.objid` 中。
+ 如果 API 函數有兩個 `integer` 引數，則第一個引數為 `pg_locks.classid`，第二個引數為 `pg_locks.objid`。

`pg_locks.objsubid` 值表示使用何種 API 形式：`1` 表示一個 `bigint` 引數；`2` 表示二個 `integer` 引數。