Lock:advisory
Lock:advisory
이벤트는 PostgreSQL 애플리케이션이 잠금을 사용하여 여러 세션에서 활동을 조정할 때 발생합니다.
관련 엔진 버전
이 대기 이벤트 정보는 Aurora PostgreSQL 버전 9.6 이상과 관련이 있습니다.
컨텍스트
PostgreSQL 권고 잠금은 사용자의 애플리케이션 코드에 의해 명시적으로 잠기거나 잠금 해제된 애플리케이션 수준의 협력 잠금입니다. 애플리케이션은 PostgreSQL 권고 잠금을 사용하여 여러 세션에서 활동을 조정할 수 있습니다. 일반, 객체 또는 행 레벨 잠금과는 달리 애플리케이션은 잠금 수명을 완벽하게 제어할 수 있습니다. 자세한 내용은 PostgreSQL 설명서의 권고 잠금
권고 잠금은 트랜잭션이 종료되기 전에 해제되거나 트랜잭션 간에 세션에 의해 유지될 수 있습니다. CREATE INDEX
문에 의해 획득한 테이블에 대한 액세스 독점 잠금과 같은 암시적 시스템 적용 잠금에는 해당되지 않습니다.
권고 잠금을 획득(잠금) 및 릴리즈(잠금 해제) 하는 데 사용되는 함수에 대한 설명은 PostgreSQL 설명서의 권고 잠금 함수
명시적인 잠금은 일반 PostgreSQL 잠금 시스템에 구현되며 pg_locks
시스템 뷰에서 볼 수 있습니다.
원인
이 잠금 유형은 명시적으로 사용하는 애플리케이션에 의해서만 제어됩니다. 쿼리의 일부로 각 행에 대해 획득된 권고 사항 잠금은 잠금이 급증하거나 장기 축적을 일으킬 수 있습니다.
이러한 효과는 쿼리에서 반환되는 것보다 많은 행에 대한 잠금을 획득하는 방식으로 쿼리가 실행될 때 발생합니다. 애플리케이션은 결국 모든 잠금을 해제해야 하지만 반환되지 않은 행에 잠금을 획득하면 애플리케이션에서 모든 잠금을 찾을 수 없습니다.
다음 예는 PostgreSQL 설명서의 권고 잠금
SELECT pg_advisory_lock(id) FROM foo WHERE id > 12345 LIMIT 100;
이 예에서, LIMIT
절은 행이 이미 내부적으로 선택되고 ID 값이 잠긴 후에만 쿼리의 출력을 중지할 수 있습니다. 이는 데이터 볼륨이 증가함에 따라 플래너가 개발 중에 테스트되지 않은 다른 실행 계획을 선택하게 되면 갑자기 발생할 수 있습니다. 이 경우 빌드업은 애플리케이션이 잠긴 모든 ID 값에 대해 pg_advisory_unlock
을 명시적으로 호출하기 때문에 발생합니다. 그러나 이 경우 반환되지 않은 행에서 획득한 잠금 집합을 찾을 수 없습니다. 잠금은 세션 수준에서 획득되기 때문에 트랜잭션이 끝날 때 자동으로 해제되지 않습니다.
차단된 잠금 시도에서 스파이크가 발생할 수 있는 또 다른 원인은 의도하지 않은 충돌입니다. 이러한 충돌에서 애플리케이션의 관련 없는 부분은 실수로 동일한 잠금 ID 공간을 공유합니다.
작업
권고 잠금의 애플리리케이션 사용을 검토하고 애플리케이션 흐름에서 각 유형의 권고 잠금을 획득하고 해제하는 위치와 시기를 자세히 검토합니다.
세션이 너무 많은 잠금을 획득하고 있는지 아니면 장기 실행 세션에서 잠금을 조기에 해제하지 않아 잠금이 느리게 축적되는지 확인합니다. 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
인수를 의미합니다.