

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

当 PostgreSQL 应用程序使用锁定来协调多个会话之间的活动时，会发生 `Lock:advisory` 事件。

**Topics**
+ [相关引擎版本](#apg-waits.lockadvisory.context.supported)
+ [上下文](#apg-waits.lockadvisory.context)
+ [原因](#apg-waits.lockadvisory.causes)
+ [操作](#apg-waits.lockadvisory.actions)

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

此等待事件信息与 Aurora PostgreSQL 版本 9.6 及更高版本相关。

## 上下文
<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;
```

在此示例中，`LIMIT` 子句只能在内部选择行并锁定其 ID 值后停止查询的输出。当数据量不断增长导致计划人员选择在开发过程中未测试的其他执行计划时，可能会突然发生这种情况。在这种情况下，由于应用程序会为锁定的每个 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` 中。您可以通过查询相同 `pid` 的 `pg_locks` 系统视图来获取特定的锁定值，以寻找 `locktype=advisory` 和 `granted=f`。

然后您可以通过查询具有 `granted=t` 的相同咨询锁的 `pg_locks` 来识别阻止的会话，如以下示例所示。

```
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` 参数：
+ 对于具有一个 `bigint` 参数的 API 函数，上面的 32 位在 `pg_locks.classid` 中，下面的 32 位在 `pg_locks.objid` 中。
+ 对于具有两个 `integer` 参数的 API 函数，第一个参数是 `pg_locks.classid`，第二个参数是 `pg_locks.objid`。

`pg_locks.objsubid` 值表示使用了哪个 API 表单：`1` 表示一个 `bigint` 参数；`2` 表示两个 `integer` 参数。