

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

`Lock:advisory` 이벤트는 PostgreSQL 애플리케이션이 잠금을 사용하여 여러 세션에서 활동을 조정할 때 발생합니다.

**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)`를 사용하는 세션을 종료하여 세션 수준 잠금의 느린 축적을 수정할 수 있습니다.

권고 잠금을 대기하는 클라이언트는 `pg_stat_activity`에서 `wait_event_type=Lock` 및 `wait_event=advisory`와 표시됩니다. `locktype=advisory` 및 `granted=f`를 탐색하며 동일한 `pid`를 위해 `pg_locks` 시스템 뷰를 쿼리하여 특정 잠금 값을 얻을 수 있습니다.

그런 다음, 예에 표시된 대로 `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` 인수입니다. 
+ 하나의 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` 인수를 의미합니다.