

# RDS for PostgreSQL의 대기 이벤트를 사용한 튜닝
<a name="PostgreSQL.Tuning"></a>

대기 이벤트는 RDS for PostgreSQL의 중요한 튜닝 도구입니다. 세션이 리소스를 기다리는 이유와 어떤 작업을 수행하는지 알 수 있다면 병목 현상을 더 잘 줄일 수 있습니다. 이 섹션의 정보를 통해 가능한 원인과 해결 조치를 찾을 수 있습니다. 이 섹션에서는 기본적인 PostgreSQL 튜닝 개념에 대해서도 설명합니다.

이 섹션에서 다루는 대기 이벤트는 RDS for PostgreSQL에만 해당됩니다.

**Topics**
+ [RDS for PostgreSQL 튜닝을 위한 필수 개념](PostgreSQL.Tuning.concepts.md)
+ [RDS for PostgreSQL 대기 이벤트](PostgreSQL.Tuning.concepts.summary.md)
+ [Client:ClientRead](wait-event.clientread.md)
+ [Client:ClientWrite](wait-event.clientwrite.md)
+ [CPU](wait-event.cpu.md)
+ [IO:BufFileRead 및 IO:BufFileWrite](wait-event.iobuffile.md)
+ [IO:DataFileRead](wait-event.iodatafileread.md)
+ [IO:WALWrite](wait-event.iowalwrite.md)
+ [IPC:parallel 대기 이벤트](rpg-ipc-parallel.md)
+ [IPC:ProcArrayGroupUpdate](apg-rpg-ipcprocarraygroup.md)
+ [Lock:advisory](wait-event.lockadvisory.md)
+ [Lock:extend](wait-event.lockextend.md)
+ [Lock:Relation](wait-event.lockrelation.md)
+ [Lock:transactionid](wait-event.locktransactionid.md)
+ [Lock:tuple](wait-event.locktuple.md)
+ [LWLock:BufferMapping (LWLock:buffer\$1mapping)](wait-event.lwl-buffer-mapping.md)
+ [LWLock:BufferIO(IPC:BufferIO)](wait-event.lwlockbufferio.md)
+ [LWLock:buffer\$1content (BufferContent)](wait-event.lwlockbuffercontent.md)
+ [LWLock:lock\$1manager (LWLock:lockmanager)](wait-event.lw-lock-manager.md)
+ [LWLock:pg\$1stat\$1statements](apg-rpg-lwlockpgstat.md)
+ [LWLock:SubtransSLRU (LWLock:SubtransControlLock)](wait-event.lwlocksubtransslru.md)
+ [Timeout:PgSleep](wait-event.timeoutpgsleep.md)
+ [Timeout:VacuumDelay](wait-event.timeoutvacuumdelay.md)

# RDS for PostgreSQL 튜닝을 위한 필수 개념
<a name="PostgreSQL.Tuning.concepts"></a>

RDS for PostgreSQL 데이터베이스를 튜닝하기 전에 대기 이벤트가 무엇인지, 왜 발생하는지 확인해 보세요. 또한 RDS for PostgreSQL의 기본 메모리 및 디스크 아키텍처도 검토하세요. 유용한 아키텍처 다이어그램은 [PostgreSQL](https://en.wikibooks.org/wiki/PostgreSQL/Architecture) 위키북을 참조하세요.

**Topics**
+ [RDS for PostgreSQL 대기 이벤트](PostgreSQL.Tuning.concepts.waits.md)
+ [RDS for PostgreSQL 메모리](PostgreSQL.Tuning.concepts.memory.md)
+ [RDS for PostgreSQL 프로세스](PostgreSQL.Tuning.concepts.processes.md)

# RDS for PostgreSQL 대기 이벤트
<a name="PostgreSQL.Tuning.concepts.waits"></a>

*대기 이벤트*는 세션이 리소스를 대기 중임을 나타냅니다. 예를 들어 대기 이벤트 `Client:ClientRead`는 RDS for PostgreSQL이 클라이언트로부터 데이터를 수신하기 위해 대기 중일 때 발생합니다. 세션은 일반적으로 대기하는 리소스는 다음과 같습니다.
+ 버퍼에 대한 단일 스레드 액세스(예: 세션이 버퍼 수정을 시도하는 경우)
+ 현재 다른 세션에 의해 잠겨 있는 행
+ 데이터 파일 읽기
+ 로그 파일 쓰기

예를 들어 쿼리를 충족하기 위해 세션에서 전체 테이블 스캔을 수행할 수 있습니다. 데이터가 아직 메모리에 없는 경우 세션은 디스크 I/O가 완료될 때까지 기다립니다. 버퍼를 메모리로 읽으면 다른 세션이 동일한 버퍼에 액세스하기 때문에 세션을 기다려야 할 수 있습니다. 데이터베이스는 미리 정의된 대기 이벤트를 사용하여 대기를 기록합니다. 이러한 이벤트는 카테고리로 그룹화됩니다.

대기 이벤트 자체는 성능 문제를 나타내지 않습니다. 예를 들어 요청된 데이터가 메모리에 없으면 디스크에서 데이터를 읽어야 합니다. 한 세션이 업데이트를 위해 행을 잠그면 다른 세션은 해당 행의 잠금이 해제될 때까지 기다려 업데이트할 수 있습니다. 커밋을 수행하려면 로그 파일에 대한 쓰기가 완료될 때까지 대기해야 합니다. 대기는 데이터베이스의 정상적인 기능에 필수적입니다.

반면 많은 수의 대기 이벤트는 일반적으로 성능 문제를 나타냅니다. 이러한 경우 대기 이벤트 데이터를 사용하여 세션이 시간을 소비하는 위치를 결정할 수 있습니다. 예를 들어 일반적으로 실행하는 데 몇 분 정도 걸리는 보고서가 몇 시간 동안 실행되는 경우 총 대기 시간에 가장 많이 기여하는 대기 이벤트를 식별할 수 있습니다. 최상위 대기 이벤트의 원인을 확인할 수 있는 경우 성능을 향상시키는 변경 작업을 수행할 수 있습니다. 예를 들어 세션이 다른 세션에 의해 잠긴 행에서 대기 중인 경우 잠금 세션을 종료할 수 있습니다.

# RDS for PostgreSQL 메모리
<a name="PostgreSQL.Tuning.concepts.memory"></a>

RDS for PostgreSQL 메모리는 공유 메모리와 로컬 메모리로 구분됩니다.

**Topics**
+ [RDS for PostgreSQL의 공유 메모리](#PostgreSQL.Tuning.concepts.shared)
+ [RDS for PostgreSQL의 로컬 메모리](#PostgreSQL.Tuning.concepts.local)

## RDS for PostgreSQL의 공유 메모리
<a name="PostgreSQL.Tuning.concepts.shared"></a>

RDS for PostgreSQL은 인스턴스가 시작될 때 공유 메모리를 할당합니다. 공유 메모리는 여러 하위 영역으로 나뉩니다. 다음 섹션에서는 가장 중요한 항목에 대해 설명합니다.

**Topics**
+ [공유 버퍼](#PostgreSQL.Tuning.concepts.buffer-pool)
+ [미리 쓰기 로그(WAL) 버퍼](#PostgreSQL.Tuning.concepts.WAL)

### 공유 버퍼
<a name="PostgreSQL.Tuning.concepts.buffer-pool"></a>

*공유 버퍼 풀*은 애플리케이션 연결에서 사용 중이거나 사용 중인 모든 페이지를 보관하는 RDS for PostgreSQL 메모리 영역입니다. *페이지*는 디스크 블록의 메모리 버전입니다. 공유 버퍼 풀은 디스크에서 읽은 데이터 블록을 캐시합니다. 이 풀은 디스크에서 데이터를 다시 읽어야 할 필요성을 줄여 데이터베이스가 더 효율적으로 연산하게 합니다.

모든 테이블과 인덱스는 고정된 크기의 페이지 배열로 저장됩니다. 각 블록에는 행에 해당하는 여러 튜플이 포함되어 있습니다. 튜플은 모든 페이지에 저장할 수 있습니다.

공유 버퍼 풀에는 유한 메모리가 있습니다. 새 요청에 메모리에 없는 페이지가 필요하고 메모리가 더 이상 존재하지 않는 경우 RDS for PostgreSQL은 요청을 수용하기 위해 자주 사용되지 않는 페이지를 삭제합니다. 제거 정책은 클럭 스윕 알고리즘에 의해 구현됩니다.

`shared_buffers` 파라미터는 서버가 데이터 캐싱에 전념하는 메모리 양을 결정합니다. 기본값은 DB 인스턴스에 사용 가능한 메모리를 기반으로 `{DBInstanceClassMemory/32768}`바이트로 설정됩니다.

### 미리 쓰기 로그(WAL) 버퍼
<a name="PostgreSQL.Tuning.concepts.WAL"></a>

*미리 쓰기 로그(WAL) 버퍼*는 RDS for PostgreSQL이 나중에 영구 스토리지에 쓰는 트랜잭션 데이터를 보유합니다. WAL 메커니즘을 사용하여 RDS for PostgreSQL이 다음을 수행할 수 있습니다.
+ 장애 발생 후 데이터 복구
+ 디스크에 빈번한 쓰기를 방지하여 디스크 I/O 감소

클라이언트가 데이터를 변경하면 RDS for PostgreSQL이 변경 사항을 WAL 버퍼에 기록합니다. 클라이언트가 `COMMIT`을 발행하면 WAL 라이터 프로세스는 트랜잭션 데이터를 WAL 파일에 씁니다.

`wal_level` 파라미터는 WAL에 기록되는 정보의 양을 결정하며, 가능한 값은 `minimal`, `replica`, `logical`입니다.

## RDS for PostgreSQL의 로컬 메모리
<a name="PostgreSQL.Tuning.concepts.local"></a>

모든 백엔드 프로세스는 쿼리 처리를 위해 로컬 메모리를 할당합니다.

**Topics**
+ [작업 메모리 영역](#PostgreSQL.Tuning.concepts.local.work_mem)
+ [유지 관리 작업 메모리 영역](#PostgreSQL.Tuning.concepts.local.maintenance_work_mem)
+ [임시 버퍼 영역](#PostgreSQL.Tuning.concepts.temp)

### 작업 메모리 영역
<a name="PostgreSQL.Tuning.concepts.local.work_mem"></a>

*작업 메모리 영역*은 정렬 및 해시를 수행하는 쿼리에 대한 임시 데이터를 보유합니다. 예를 들어, 다음을 포함하는 쿼리 `ORDER BY` 절은 정렬을 수행합니다. 쿼리는 해시 조인 및 집계에서 해시 테이블을 사용합니다.

`work_mem` 파라미터는 임시 디스크 파일에 쓰기 전에 내부 정렬 작업 및 해시 테이블에서 사용할 메모리 양이며, 메가바이트 단위로 측정됩니다. 기본값은 4MB입니다. 여러 세션을 동시에 실행할 수 있으며 각 세션은 유지 관리 작업을 병렬로 실행할 수 있습니다. 이러한 이유로 사용되는 총 작업 메모리는 `work_mem` 설정의 배수가 될 수 있습니다.

### 유지 관리 작업 메모리 영역
<a name="PostgreSQL.Tuning.concepts.local.maintenance_work_mem"></a>

*유지 관리 작업 메모리 영역*은 유지 관리 작업을 위해 데이터를 캐시합니다. 이러한 작업에는 베큠, 인덱스 생성 및 외래 키 추가가 포함됩니다.

`maintenance_work_mem` 파라미터는 유지 관리 작업에서 사용할 최대 메모리 양을 지정하며, 메가바이트 단위로 측정됩니다. 기본값은 64MB입니다. 데이터베이스 세션은 한 번에 하나의 유지 관리 작업만 실행할 수 있습니다.

### 임시 버퍼 영역
<a name="PostgreSQL.Tuning.concepts.temp"></a>

*임시 버퍼 영역*에서는 각 데이터베이스 세션에 대한 임시 테이블을 캐시합니다.

각 세션은 사용자가 지정한 한도까지 필요에 따라 임시 버퍼를 할당합니다. 세션이 끝나면 서버는 버퍼를 지웁니다.

`temp_buffers` 파라미터는 각 세션에서 사용하는 임시 버퍼의 최대 수를 설정하며, 메가바이트 단위로 측정됩니다. 기본값은 8MB입니다. 세션 내에서 임시 테이블을 처음 사용하기 전에 `temp_buffers` 값을 변경할 수 있습니다.

# RDS for PostgreSQL 프로세스
<a name="PostgreSQL.Tuning.concepts.processes"></a>

RDS for PostgreSQL은 여러 프로세스를 사용합니다.

**Topics**
+ [Postmaster 프로세스](#PostgreSQL.Tuning.concepts.postmaster)
+ [백엔드 프로세스](#PostgreSQL.Tuning.concepts.backend)
+ [백그라운드 프로세스](#PostgreSQL.Tuning.concepts.vacuum)

## Postmaster 프로세스
<a name="PostgreSQL.Tuning.concepts.postmaster"></a>

*Postmaster 프로세스*는 RDS for PostgreSQL을 시작할 때 시작되는 첫 번째 프로세스입니다. 포스트마스터 프로세스는 다음과 같은 주요 책임이 있습니다.
+ 백그라운드 프로세스 포크 및 모니터링
+ 클라이언트 프로세스에서 인증 요청을 수신하고 데이터베이스에서 요청을 처리하도록 허용하기 전에 인증 요청을 인증합니다.

## 백엔드 프로세스
<a name="PostgreSQL.Tuning.concepts.backend"></a>

Postmaster가 클라이언트 요청을 인증하면 postmaster는 postgres 프로세스라고도 하는 새 백엔드 프로세스를 생성합니다. 하나의 클라이언트 프로세스가 정확히 하나의 백엔드 프로세스에 연결됩니다. 클라이언트 프로세스와 백엔드 프로세스는 포스트마스터 프로세스의 개입 없이 직접 통신합니다.

## 백그라운드 프로세스
<a name="PostgreSQL.Tuning.concepts.vacuum"></a>

Postmaster 프로세스는 서로 다른 백엔드 작업을 수행하는 여러 프로세스를 생성합니다. 몇 가지 중요한 사항은 다음과 같습니다.
+ WAL 라이터

  RDS for PostgreSQL 미리 쓰기 로깅(WAL) 버퍼의 데이터를 로그 파일에 씁니다. 미리 쓰기 로깅의 원칙은 데이터베이스가 변경 사항을 설명하는 로그 레코드를 디스크에 기록하기 전까지는 데이터베이스가 데이터 파일에 변경 사항을 쓸 수 없다는 것입니다. WAL 메커니즘은 디스크 I/O를 줄이고, RDS for PostgreSQL 장애 후 로그를 사용하여 데이터베이스를 복구할 수 있도록 합니다.
+ 백그라운드 라이터

  이 프로세스는 메모리 버퍼에서 데이터 파일로 더티(수정된) 페이지를 주기적으로 씁니다. 백엔드 프로세스가 메모리에서 페이지를 수정하면 페이지가 더티(dirty) 상태가 됩니다.
+ Autovacuum 데몬

  데몬은 다음 구성 요소로 이루어져 있습니다.
  + Autovacuum 시작 관리자
  + Autovacuum 작업자 프로세스

  Autovacuum은 삽입되고 업데이트되거나 삭제된 튜플 수가 많은 테이블이 있는지 확인합니다. 데몬에는 다음과 같은 책임이 있습니다.
  + 업데이트되거나 삭제된 행이 차지하는 디스크 공간 복구 또는 재사용
  + 플래너가 사용하는 통계 업데이트
  + 트랜잭션 ID 랩어라운드로 인해 이전 데이터 손실 방지

  Autovacuum 기능은 `VACUUM`과 `ANALYZE` 명령을 자동화합니다. `VACUUM`에는 다음과 같은 표준 및 전체 변형이 있습니다. 스탠다드 베큠은 다른 데이터베이스 연산과 병행하여 실행됩니다. `VACUUM FULL`에는 작업 중인 테이블에 독점적인 잠금이 필요합니다. 따라서 동일한 테이블에 액세스하는 연산과 병렬로 실행할 수 없습니다. `VACUUM`은 상당한 양의 I/O 트래픽을 생성하여 다른 활성 세션의 성능이 저하될 수 있습니다.

# RDS for PostgreSQL 대기 이벤트
<a name="PostgreSQL.Tuning.concepts.summary"></a>

다음 표에는 성능 문제를 가장 일반적으로 나타내는 RDS for PostgreSQL에 대한 대기 이벤트가 나열되어 있으며 가장 일반적인 원인과 해결 조치가 요약되어 있습니다.


| 대기 이벤트 | 정의 | 
| --- | --- | 
|  [Client:ClientRead](wait-event.clientread.md)  |  이 이벤트는 RDS for PostgreSQL이 클라이언트에서 데이터를 수신하기 위해 대기 중일 때 발생합니다.  | 
|  [Client:ClientWrite](wait-event.clientwrite.md)  |  이 이벤트는 RDS for PostgreSQL이 클라이언트에 데이터를 쓰기 위해 대기 중일 때 발생합니다.  | 
|  [CPU](wait-event.cpu.md)  | 이 이벤트는 스레드가 CPU에서 활성 상태이거나 CPU를 대기 중일 때 발생합니다. | 
|  [IO:BufFileRead 및 IO:BufFileWrite](wait-event.iobuffile.md)  |  이러한 이벤트는 RDS for PostgreSQL이 임시 파일을 만들 때 발생합니다.  | 
|  [IO:DataFileRead](wait-event.iodatafileread.md)  |  이 이벤트는 공유 메모리에서 페이지를 사용할 수 없기 때문에 연결이 백엔드 프로세스에서 저장소에서 필요한 페이지를 읽도록 대기할 때 발생합니다.  | 
| [IO:WALWrite](wait-event.iowalwrite.md)  | 이 이벤트는 RDS for PostgreSQL이 미리 쓰기 로그(WAL) 버퍼가 WAL 파일에 쓰여지기를 기다리는 동안 발생합니다.  | 
|  [Lock:advisory](wait-event.lockadvisory.md)  |  이 이벤트는 PostgreSQL 애플리케이션이 잠금을 사용하여 여러 세션에서 활동을 조정할 때 발생합니다.  | 
|  [Lock:extend](wait-event.lockextend.md) |  이 이벤트는 백엔드 프로세스가 릴레이션을 확장하기 위해 릴레이션을 잠그기를 기다리는 동안 다른 프로세스에서 동일한 목적으로 해당 릴레이션에 대한 잠금이 있는 경우에 발생합니다.  | 
|  [Lock:Relation](wait-event.lockrelation.md)  |  이 이벤트는 쿼리가 현재 다른 트랜잭션에 의해 잠긴 테이블 또는 뷰에 대한 잠금을 얻기 위해 대기 중일 때 발생합니다.  | 
|  [Lock:transactionid](wait-event.locktransactionid.md)  | 이 이벤트는 트랜잭션이 행 수준 잠금을 대기 중일 때 발생합니다. | 
|  [Lock:tuple](wait-event.locktuple.md)  |  이 이벤트는 백엔드 프로세스가 튜플에 대한 잠금 획득을 대기 중일 때 발생합니다.  | 
|  [LWLock:BufferMapping (LWLock:buffer\$1mapping)](wait-event.lwl-buffer-mapping.md)  |  이 이벤트는 세션이 데이터 블록을 공유 버퍼 풀의 버퍼와 연결하기 위해 대기 중일 때 발생합니다.  | 
|  [LWLock:BufferIO(IPC:BufferIO)](wait-event.lwlockbufferio.md)  |  이 이벤트는 RDS for PostgreSQL이 페이지에 동시에 액세스하려고 할 때 다른 프로세스가 입출력(I/O) 작업을 완료할 때까지 기다리는 경우에 발생합니다.  | 
|  [LWLock:buffer\$1content (BufferContent)](wait-event.lwlockbuffercontent.md)  |  이 대기 이벤트는 다른 세션에서 특정 데이터 페이지에 대해 쓰기 잠금을 설정한 동안 세션에서 메모리 내 해당 페이지 읽기 또는 쓰기를 대기 중일 때 발생합니다.  | 
|  [LWLock:lock\$1manager (LWLock:lockmanager)](wait-event.lw-lock-manager.md)  | 이 이벤트는 RDS for PostgreSQL 엔진이 빠른 경로 잠금이 불가능할 때 잠금을 할당, 확인 및 할당 해제하기 위해 공유 잠금 메모리 영역을 유지 관리하는 경우에 발생합니다. | 
|  [LWLock:SubtransSLRU (LWLock:SubtransControlLock)](wait-event.lwlocksubtransslru.md)  |  이 이벤트는 프로세스가 하위 트랜잭션에 대해 가장 오래전에 사용된 단순(SLRU) 캐시에 액세스하기 위해 대기 중일 때 발생합니다.  | 
|  [Timeout:PgSleep](wait-event.timeoutpgsleep.md)  |  이 이벤트는 서버 프로세스가 `pg_sleep` 함수 및 절전 시간 초과가 만료될 때까지 대기할 때 발생합니다.  | 
|  [Timeout:VacuumDelay](wait-event.timeoutvacuumdelay.md)  | 이 이벤트는 예상 비용 한도에 도달하여 진공 프로세스가 휴면 상태임을 나타냅니다. | 

# Client:ClientRead
<a name="wait-event.clientread"></a>

`Client:ClientRead` 이벤트는 RDS for PostgreSQL이 클라이언트에서 데이터를 수신하기 위해 대기 중일 때 발생합니다.

**Topics**
+ [지원되는 엔진 버전](#wait-event.clientread.context.supported)
+ [컨텍스트](#wait-event.clientread.context)
+ [대기 증가의 가능한 원인](#wait-event.clientread.causes)
+ [작업](#wait-event.clientread.actions)

## 지원되는 엔진 버전
<a name="wait-event.clientread.context.supported"></a>

이 대기 이벤트 정보는 RDS for PostgreSQL 버전 10 이상에서 지원됩니다.

## 컨텍스트
<a name="wait-event.clientread.context"></a>

RDS for PostgreSQL DB 인스턴스가 클라이언트로부터 데이터를 수신하기 위해 대기 중입니다. RDS for PostgreSQL DB 인스턴스는 클라이언트로부터 데이터를 수신해야 클라이언트에 더 많은 데이터를 보낼 수 있습니다. 클라이언트에서 데이터를 수신하기 전에 인스턴스가 대기하는 시간이 `Client:ClientRead` 이벤트입니다.

## 대기 증가의 가능한 원인
<a name="wait-event.clientread.causes"></a>

상위 대기에서 나타나는 `Client:ClientRead` 이벤트의 일반적인 원인은 다음을 포함합니다.

**네트워크 대기 시간 증가**  
RDS for PostgreSQL DB 인스턴스와 클라이언트 간에 네트워크 지연 시간이 늘어날 수 있습니다. 네트워크 지연 시간이 높을수록 DB 인스턴스가 클라이언트에서 데이터를 수신하는 데 필요한 시간이 늘어납니다.

**클라이언트에 대한 로드 증가**  
클라이언트에 CPU 압력 또는 네트워크 포화가 있을 수 있습니다. 클라이언트 로드가 증가하면 클라이언트에서 RDS for PostgreSQL DB 인스턴스로의 데이터 전송이 지연될 수 있습니다.

**과도한 네트워크 왕복**  
RDS for PostgreSQL DB 인스턴스와 클라이언트 간의 네트워크 왕복 횟수가 많으면 클라이언트에서 RDS for PostgreSQL DB 인스턴스로의 데이터 전송을 지연시킬 수 있습니다.

**대량 복사 연산**  
복사 연산 중에 데이터가 클라이언트의 파일 시스템에서 RDS for PostgreSQL DB 인스턴스로 전송됩니다. DB 인스턴스에 대량의 데이터를 전송하면 클라이언트에서 DB 인스턴스로의 데이터 전송이 지연될 수 있습니다.

**유휴 클라이언트 연결**  
클라이언트가 `idle in transaction` 상태의 RDS for PostgreSQL DB 인스턴스에 연결하는 경우 DB 인스턴스는 클라이언트가 더 많은 데이터를 보내거나 명령을 실행할 때까지 기다릴 수 있습니다. 이 상태의 연결은`Client:ClientRead` 이벤트 증가로 이어질 수 있습니다.

**연결 풀링에 사용되는 PGBouncer**  
PGBouncer에는 `pkt_buf`와 같은 낮은 수준의 네트워크 구성 설정이 있으며, 기본적으로 4,096으로 설정됩니다. 워크로드가 PGBouncer를 통해 4,096바이트보다 큰 쿼리 패킷을 보내는 경우, `pkt_buf` 설정을 8,192까지 늘리는 것이 좋습니다. 새 설정으로 인해 `Client:ClientRead` 이벤트의 수가 줄어들지 않는 경우 `pkt_buf` 설정을 16,384 또는 32,768과 같이 더 큰 값으로 설정하는 것이 좋습니다. 쿼리 텍스트가 큰 경우 더 큰 설정이 특히 유용할 수 있습니다.

## 작업
<a name="wait-event.clientread.actions"></a>

대기 이벤트의 원인에 따라 다른 작업을 권장합니다.

**Topics**
+ [인스턴스와 동일한 가용 영역 및 VPC 서브넷에 클라이언트를 배치합니다.](#wait-event.clientread.actions.az-vpc-subnet)
+ [클라이언트 확장](#wait-event.clientread.actions.scale-client)
+ [현재 세대 인스턴스 사용](#wait-event.clientread.actions.db-instance-class)
+ [네트워크 대역폭 향상](#wait-event.clientread.actions.increase-network-bandwidth)
+ [최대 네트워크 성능 모니터링](#wait-event.clientread.actions.monitor-network-performance)
+ ['트랜잭션에서 유휴' 상태의 트랜잭션 모니터링](#wait-event.clientread.actions.check-idle-in-transaction)

### 인스턴스와 동일한 가용 영역 및 VPC 서브넷에 클라이언트를 배치합니다.
<a name="wait-event.clientread.actions.az-vpc-subnet"></a>

네트워크 대기 시간을 줄이고 네트워크 처리량(throughput)을 늘리려면 RDS for PostgreSQL DB 인스턴스와 동일한 가용 영역 및 Virtual Private Cloud(VPC) 서브넷에 클라이언트를 배치합니다. 클라이언트가 가능한 한 DB 인스턴스에 지리적으로 가까이 있는지 확인합니다.

### 클라이언트 확장
<a name="wait-event.clientread.actions.scale-client"></a>

Amazon CloudWatch 또는 기타 호스트 지표를 사용하여 클라이언트가 현재 CPU 또는 네트워크 대역폭 또는 둘 다에 의해 제한되어 있는지 확인합니다. 클라이언트가 제한된 경우 그에 따라 클라이언트를 확장합니다.

### 현재 세대 인스턴스 사용
<a name="wait-event.clientread.actions.db-instance-class"></a>

점보 프레임을 지원하는 DB 인스턴스 클래스를 사용하지 않는 경우도 있습니다. Amazon EC2 애플리케이션을 실행하는 경우 클라이언트에 현재 세대 인스턴스를 사용하는 것이 좋습니다. 또한 클라이언트 운영 체제에서 최대 전송 단위(MTU)를 구성합니다. 이 기술은 네트워크 왕복 수를 줄이고 네트워크 처리량을 늘릴 수 있습니다. 자세한 내용은 Amazon EC2 사용 설명서**의 [점보 프레임(9001 MTU)](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/network_mtu.html#jumbo_frame_instances)을 참조하세요.

DB 인스턴스 클래스에 대한 자세한 내용은 [DB 인스턴스 클래스](Concepts.DBInstanceClass.md) 섹션을 참조하세요. Amazon EC2 인스턴스 유형과 동일한 DB 인스턴스 클래스를 확인하려면 `db.`가 Amazon EC2 인스턴스 유형 이름 앞에 있어야 합니다. 예를 들어, `r5.8xlarge` Amazon EC2 인스턴스는 `db.r5.8xlarge` DB 인스턴스 클래스입니다.

### 네트워크 대역폭 향상
<a name="wait-event.clientread.actions.increase-network-bandwidth"></a>

DB 인스턴스의 수신 및 발신 네트워크 트래픽을 모니터링하는 `NetworkReceiveThroughput` 및 `NetworkTransmitThroughput` Amazon CloudWatch 지표를 사용합니다. 이러한 지표는 네트워크 대역폭이 워크로드에 충분한지 확인하는 데 도움이 될 수 있습니다.

네트워크 대역폭이 충분하지 않으면 늘리세요. 만약 AWS 클라이언트 또는 DB 인스턴스가 네트워크 대역폭 제한에 도달했다면. 대역폭을 늘리는 유일한 방법은 DB 인스턴스 크기를 늘리는 것입니다. 자세한 내용은 [DB 인스턴스 클래스 유형](Concepts.DBInstanceClass.Types.md) 섹션을 참조하세요.

CloudWatch 지표에 대한 자세한 내용은 [Amazon RDS에 대한 Amazon CloudWatch 지표](rds-metrics.md) 섹션을 참조하세요.

### 최대 네트워크 성능 모니터링
<a name="wait-event.clientread.actions.monitor-network-performance"></a>

Amazon EC2 클라이언트를 사용하는 경우 Amazon EC2는 집계 인바운드 및 아웃바운드 네트워크 대역폭을 포함하여 네트워크 성능 지표에 대한 최댓값을 제공합니다. 또한 연결 추적을 제공하여 패킷이 예상대로 반환되고 도메인 이름 시스템(DNS)과 같은 서비스에 대한 링크-로컬 서비스 액세스를 제공합니다. 이러한 최댓값을 모니터링하려면 현재 향상된 네트워킹 드라이버를 사용하고 클라이언트의 네트워크 성능을 모니터링하세요.

자세한 내용은 **Amazon EC2 사용 설명서의 [Amazon EC2 인스턴스의 네트워크 성능 모니터링](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/monitoring-network-performance-ena.html)과 **Amazon EC2 사용 설명서의 [Amazon EC2 인스턴스의 네트워크 성능 모니터링](https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/monitoring-network-performance-ena.html)을 참조하세요.

### '트랜잭션에서 유휴' 상태의 트랜잭션 모니터링
<a name="wait-event.clientread.actions.check-idle-in-transaction"></a>

`idle in transaction` 연결의 수가 점점 늘어나고 있는지 확인하세요. 이를 수행하려면 `state`열의 `pg_stat_activity`테이블을 모니니터링하세요. 다음과 유사한 쿼리를 실행하여 연결 소스를 식별할 수 있습니다.

```
select client_addr, state, count(1) from pg_stat_activity 
where state like 'idle in transaction%' 
group by 1,2 
order by 3 desc
```

# Client:ClientWrite
<a name="wait-event.clientwrite"></a>

`Client:ClientWrite` 이벤트는 RDS for PostgreSQL이 클라이언트에 데이터를 쓰기 위해 대기 중일 때 발생합니다.

**Topics**
+ [지원되는 엔진 버전](#wait-event.clientwrite.context.supported)
+ [컨텍스트](#wait-event.clientwrite.context)
+ [대기 증가의 가능한 원인](#wait-event.clientwrite.causes)
+ [작업](#wait-event.clientwrite.actions)

## 지원되는 엔진 버전
<a name="wait-event.clientwrite.context.supported"></a>

이 대기 이벤트 정보는 RDS for PostgreSQL 버전 10 이상에서 지원됩니다.

## 컨텍스트
<a name="wait-event.clientwrite.context"></a>

클라이언트 프로세스는 클러스터가 더 많은 데이터를 보낼 수 있게 되기 전 반드시 RDS for PostgreSQL DB 클러스터로부터 받은 모든 데이터를 읽어야 합니다. 클라이언트에서 데이터를 수신하기 전에 클러스터가 대기하는 시간은 `Client:ClientWrite` 이벤트를 트리거합니다.

RDS for PostgreSQL DB 인스턴스와 클라이언트 간의 네트워크 처리량(throughput)이 감소하면 이 이벤트가 발생할 수 있습니다. 클라이언트에 대한 CPU 압력 및 네트워크 포화 상태시에도 이 이벤트가 발생할 수 있습니다. *CPU 압력*은 CPU가 완전히 활용되고 CPU 시간을 기다리는 작업이 있을 때입니다. *네트워크 포화*는 데이터베이스와 클라이언트 간의 네트워크가 처리할 수 있는 것보다 많은 데이터를 전달하는 경우입니다.

## 대기 증가의 가능한 원인
<a name="wait-event.clientwrite.causes"></a>

상위 대기에서 나타나는 `Client:ClientWrite` 이벤트의 일반적인 원인은 다음을 포함합니다.

**네트워크 대기 시간 증가**  
RDS for PostgreSQL DB 인스턴스와 클라이언트 간에 네트워크 지연 시간이 늘어날 수 있습니다. 네트워크 대기 시간이 높을수록 클라이언트가 데이터를 수신하는 데 필요한 시간이 늘어납니다.

**클라이언트에 대한 로드 증가**  
클라이언트에 CPU 압력 또는 네트워크 포화가 있을 수 있습니다. 클라이언트에 대한 로드가 증가하면 RDS for MySQL DB 인스턴스에서 데이터 수신이 지연됩니다.

**클라이언트에 전송되는 대용량 데이터**  
RDS for PostgreSQL DB 인스턴스가 많은 양의 데이터를 클라이언트에 전송하고 있을 수 있습니다. 클라이언트는 클러스터가 데이터를 전송하는 만큼 빠르게 데이터를 수신하지 못할 수 있습니다. 큰 테이블 복사와 같은 활동 시 `Client:ClientWrite` 이벤트가 증가할 수 있습니다.

## 작업
<a name="wait-event.clientwrite.actions"></a>

대기 이벤트의 원인에 따라 다른 작업을 권장합니다.

**Topics**
+ [클러스터와 동일한 가용 영역 및 VPC 서브넷에 클라이언트를 배치합니다.](#wait-event.clientwrite.actions.az-vpc-subnet)
+ [현재 세대 인스턴스 사용](#wait-event.clientwrite.actions.db-instance-class)
+ [클라이언트에 전송해야 하는 데이터 양 감소](#wait-event.clientwrite.actions.reduce-data)
+ [클라이언트 확장](#wait-event.clientwrite.actions.scale-client)

### 클러스터와 동일한 가용 영역 및 VPC 서브넷에 클라이언트를 배치합니다.
<a name="wait-event.clientwrite.actions.az-vpc-subnet"></a>

네트워크 대기 시간을 줄이고 네트워크 처리량(throughput)을 늘리려면 RDS for PostgreSQL DB 인스턴스와 동일한 가용 영역 및 Virtual Private Cloud(VPC) 서브넷에 클라이언트를 배치합니다.

### 현재 세대 인스턴스 사용
<a name="wait-event.clientwrite.actions.db-instance-class"></a>

점보 프레임을 지원하는 DB 인스턴스 클래스를 사용하지 않는 경우도 있습니다. Amazon EC2 애플리케이션을 실행하는 경우 클라이언트에 현재 세대 인스턴스를 사용하는 것이 좋습니다. 또한 클라이언트 운영 체제에서 최대 전송 단위(MTU)를 구성합니다. 이 기술은 네트워크 왕복 수를 줄이고 네트워크 처리량을 늘릴 수 있습니다. 자세한 내용은 Amazon EC2 사용 설명서**의 [점보 프레임(9001 MTU)](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/network_mtu.html#jumbo_frame_instances)을 참조하세요.

DB 인스턴스 클래스에 대한 자세한 내용은 [DB 인스턴스 클래스](Concepts.DBInstanceClass.md) 섹션을 참조하세요. Amazon EC2 인스턴스 유형과 동일한 DB 인스턴스 클래스를 확인하려면 `db.`가 Amazon EC2 인스턴스 유형 이름 앞에 있어야 합니다. 예를 들어, `r5.8xlarge` Amazon EC2 인스턴스는 `db.r5.8xlarge` DB 인스턴스 클래스입니다.

### 클라이언트에 전송해야 하는 데이터 양 감소
<a name="wait-event.clientwrite.actions.reduce-data"></a>

가능하면 애플리케이션을 조정하여 RDS for PostgreSQL DB 인스턴스가 클라이언트에 보내는 데이터의 양을 줄이세요. 이러한 조정을 수행하면 클라이언트에서 CPU 및 네트워크 경합을 줄일 수 있습니다.

### 클라이언트 확장
<a name="wait-event.clientwrite.actions.scale-client"></a>

Amazon CloudWatch 또는 기타 호스트 지표를 사용하여 클라이언트가 현재 CPU 또는 네트워크 대역폭 또는 둘 다에 의해 제한되어 있는지 확인합니다. 클라이언트가 제한된 경우 그에 따라 클라이언트를 확장합니다.

# CPU
<a name="wait-event.cpu"></a>

이 이벤트는 스레드가 CPU에서 활성 상태이거나 CPU를 대기 중일 때 발생합니다.

**Topics**
+ [지원되는 엔진 버전](#wait-event.cpu.context.supported)
+ [컨텍스트](#wait-event.cpu.context)
+ [대기 증가의 가능한 원인](#wait-event.cpu.causes)
+ [작업](#wait-event.cpu.actions)

## 지원되는 엔진 버전
<a name="wait-event.cpu.context.supported"></a>

이 대기 이벤트 정보는 모든 버전의 RDS for PostgreSQL과 관련이 있습니다.

## 컨텍스트
<a name="wait-event.cpu.context"></a>

*중앙 처리 장치*는 명령을 실행하는 컴퓨터의 구성 요소입니다. 예를 들어 CPU 명령은 연산 연산을 수행하고 메모리에서 데이터를 교환합니다. 쿼리가 데이터베이스 엔진을 통해 수행하는 명령 수를 늘리면 쿼리 실행 시간이 늘어납니다. *CPU 스케줄링*은 프로세스에 CPU 시간을 부여합니다. 스케줄링은 운영 체제 커널에 의해 조정됩니다.

**Topics**
+ [이 대기 시간이 언제 발생하는지 확인하는 방법](#wait-event.cpu.when-it-occurs)
+ [DBloadCPU 지표](#wait-event.cpu.context.dbloadcpu)
+ [os.cpuUtilization 지표](#wait-event.cpu.context.osmetrics)
+ [CPU 스케줄링의 원인](#wait-event.cpu.context.scheduling)

### 이 대기 시간이 언제 발생하는지 확인하는 방법
<a name="wait-event.cpu.when-it-occurs"></a>

`CPU` 대기 이벤트는 백엔드 프로세스가 CPU에서 활성 상태이거나 CPU를 기다리고 있음을 나타냅니다. 쿼리에 다음 정보가 표시될 때 발생한다는 것을 알고 있습니다.
+ `pg_stat_activity.state` 열이 `active` 값을 갖고 있습니다.
+ `pg_stat_activity`의 `wait_event_type`과 `wait_event` 열은 모두 `null`입니다.

CPU를 사용하거나 대기 중인 백엔드 프로세스를 보려면 다음 쿼리를 실행하세요.

```
SELECT * 
FROM   pg_stat_activity
WHERE  state = 'active'
AND    wait_event_type IS NULL
AND    wait_event IS NULL;
```

### DBloadCPU 지표
<a name="wait-event.cpu.context.dbloadcpu"></a>

CPU 성능 개선 도우미의 지표는 `DBLoadCPU`입니다. `DBLoadCPU`의 값은 Amazon CloudWatch 지표 `CPUUtilization`의 값과 다를 수 있습니다. 후자의 지표는 데이터베이스 인스턴스에 대한 HyperVisor에서 수집됩니다.

### os.cpuUtilization 지표
<a name="wait-event.cpu.context.osmetrics"></a>

성능 개선 도우미 운영 시스템 지표는 CPU 사용률에 대한 자세한 정보를 제공합니다. 예를 들어 다음 지표가 표시될 수 있습니다.
+ `os.cpuUtilization.nice.avg`
+ `os.cpuUtilization.total.avg`
+ `os.cpuUtilization.wait.avg`
+ `os.cpuUtilization.idle.avg`

성능 개선 도우미는 데이터베이스 엔진의 CPU 사용량을 `os.cpuUtilization.nice.avg`와 같이 보고합니다.

### CPU 스케줄링의 원인
<a name="wait-event.cpu.context.scheduling"></a>

 운영 체제(OS) 커널은 CPU의 스케줄링을 처리합니다. CPU가 *활성 상태*이면 프로세스는 스케줄링될 때까지 기다려야 할 수 있습니다. CPU는 계산을 수행하는 동안 활성 상태입니다. 실행 중이 아닌 유휴 스레드, 즉 메모리 I/O에서 대기하는 유휴 스레드가 있는 경우에도 CPU는 활성 상태입니다. 일반적인 데이터베이스 워크로드에서는 이 유형의 I/O가 지배적입니다.

다음 조건이 충족되면 프로세스가 CPU에 스케줄링될 때까지 대기할 수 있습니다.
+ CloudWatch `CPUUtilization` 지표가 100%에 가깝습니다.
+ 평균 로드가 vCPU 수보다 크므로 부하가 많음을 나타냅니다. 성능 개선 도우미의 OS 지표 섹션에서 `loadAverageMinute` 지표를 찾을 수 있습니다.

## 대기 증가의 가능한 원인
<a name="wait-event.cpu.causes"></a>

이 이벤트가 정상 상태보다 많이 발생하여 성능 문제를 나타낼 수 있는 경우 일반적인 원인은 다음과 같습니다.

**Topics**
+ [갑작스러운 스파이크의 원인](#wait-event.cpu.causes.spikes)
+ [장기 고주파의 원인](#wait-event.cpu.causes.long-term)
+ [코너 케이스](#wait-event.cpu.causes.corner-cases)

### 갑작스러운 스파이크의 원인
<a name="wait-event.cpu.causes.spikes"></a>

갑작스런 스파이크의 가장 큰 원인은 다음과 같습니다.
+ 애플리케이션이 데이터베이스에 대한 동시 연결을 너무 많이 열었습니다. 이 시나리오를 '연결 폭풍'이라고 합니다.
+ 다음 방법 중 하나로 애플리케이션 워크로드가 변경되었습니다.
  + 새 쿼리
  + 데이터 집합 크기 증가
  + 인덱스 유지 관리 또는 생성
  + 새로운 함수
  + 새로운 연산자
  + 병렬 쿼리 실행의 증가
+ 쿼리 실행 계획이 변경되었습니다. 경우에 따라 변경으로 인해 버퍼가 증가할 수 있습니다. 예를 들어, 쿼리는 이전에 인덱스를 사용했을 때의 순차적 스캔을 사용하고 있습니다. 이 경우 쿼리는 동일한 목표를 달성하기 위해 더 많은 CPU가 필요합니다.

### 장기 고주파의 원인
<a name="wait-event.cpu.causes.long-term"></a>

장기간에 걸쳐 재발하는 이벤트의 가장 큰 원인은 다음과 같습니다.
+ 너무 많은 백엔드 프로세스가 CPU에서 동시에 실행되고 있습니다. 이러한 프로세스는 병렬 작업자가 될 수 있습니다.
+ 쿼리는 많은 수의 버퍼가 필요하기 때문에 차선적으로 수행됩니다.

### 코너 케이스
<a name="wait-event.cpu.causes.corner-cases"></a>

실제 원인으로 판명될 가능성이 있는 원인이 없다면 다음과 같은 상황이 발생할 수 있습니다.
+ CPU가 프로세스를 안팎으로 교환하고 있습니다.
+ *방대한 페이지* 기능이 해제된 경우 CPU가 페이지 테이블 항목을 관리하고 있을 수 있습니다. 이 메모리 관리 기능은 마이크로, 스몰, 미디엄 DB 인스턴스 클래스를 제외한 모든 DB 인스턴스 클래스에 대해 기본적으로 사용 설정되어 있습니다. 자세한 내용은 [RDS for PostgreSQL용 방대한 페이지](PostgreSQL.Concepts.General.FeatureSupport.HugePages.md) 섹션을 참조하세요.

## 작업
<a name="wait-event.cpu.actions"></a>

`CPU` 대기 이벤트가 데이터베이스 활동을 지배하는 경우 반드시 성능 문제를 나타내지는 않습니다. 성능이 저하되는 경우에만 이 이벤트에 응답하세요.

**Topics**
+ [데이터베이스로 인해 CPU 증가가 발생하는지 조사합니다.](#wait-event.cpu.actions.db-CPU)
+ [연결 수 증가 여부 확인](#wait-event.cpu.actions.connections)
+ [워크로드 변경에 대응](#wait-event.cpu.actions.workload)

### 데이터베이스로 인해 CPU 증가가 발생하는지 조사합니다.
<a name="wait-event.cpu.actions.db-CPU"></a>

성능 개선 도우미의 `os.cpuUtilization.nice.avg` 지표를 검토합니다. 이 값이 CPU 사용량보다 훨씬 작다면 데이터베이스가 아닌 프로세스가 CPU의 주요 기여자입니다.

### 연결 수 증가 여부 확인
<a name="wait-event.cpu.actions.connections"></a>

Amazon CloudWatch의 `DatabaseConnections` 지표를 검토합니다. 작업은 CPU 대기 이벤트 증가 시기 동안 증가 또는 감소 여부에 따라 달라집니다.

#### 연결이 증가했습니다.
<a name="wait-event.cpu.actions.connections.increased"></a>

연결 수가 증가했다면, CPU를 사용하는 백엔드 프로세스 수를 vCPU 수와 비교하세요. 가능한 시나리오는 다음과 같습니다.
+ CPU를 사용하는 백엔드 프로세스 수가 vCPU 수보다 적습니다.

  이 경우 연결 수는 문제가 되지 않습니다. 그러나 계속해서 CPU 사용률을 줄이려고 할 수도 있습니다.
+ CPU를 사용하는 백엔드 프로세스 수가 vCPU 수보다 적습니다.

  이 경우에는 다음 옵션을 고려하세요.
  + 데이터베이스에 연결된 백엔드 프로세스 수를 줄입니다. 예를 들어 RDS 프록시와 같은 연결 풀링 솔루션을 구현합니다. 자세한 내용은 [ Amazon RDS Proxy](rds-proxy.md) 섹션을 참조하세요.
  + 인스턴스 크기를 업그레이드하여 vCPU 수를 늘립니다.
  + 해당하는 경우 일부 읽기 전용 워크로드를 리더 노드로 리디렉션하세요.

#### 연결이 증가하지 않았습니다.
<a name="wait-event.cpu.actions.connections.decreased"></a>

성능 개선 도우미의 `blks_hit` 지표를 검토합니다. `blks_hit`의 증가와 CPU 사용량 사이의 상관관계를 찾습니다. 가능한 시나리오는 다음과 같습니다.
+ CPU 사용량 및 `blks_hit`이 상관관계가 있습니다.

  이 경우 CPU 사용량에 연결된 최상위 SQL 문을 찾아 계획 변경을 찾습니다. 다음과 같은 기술을 사용할 수 있습니다.
  + 계획을 수동으로 설명하고 예상 실행 계획과 비교합니다.
  + 초당 블록 히트와 초당 로컬 블록 히트가 증가하는지 확인합니다. 성능 개선 도우미의 대시보드의 **상위 SQL** 섹션에서 **환경설정**을 선택합니다.
+ CPU 사용량 및 `blks_hit`이 상관관계가 없습니다.

  이 경우 다음 중 한 가지라도 발생하는지 확인합니다.
  + 애플리케이션이 데이터베이스에 급속하게 연결되고 데이터베이스와의 연결이 끊어지고 있습니다.

    `log_connections`와 `log_disconnections`를 켜 이 동작을 진단하고, 그런 다음 PostgreSQL 로그를 분석합니다. `pgbadger` 로그 분석기 사용을 고려하세요. 자세한 내용은 [https://github.com/darold/pgbadger](https://github.com/darold/pgbadger) 섹션을 참조하세요.
  + OS에 과부하가 걸렸습니다.

    이 경우 성능 개선 도우미는 백엔드 프로세스가 평소보다 오랜 시간 동안 CPU를 사용하고 있음을 보여줍니다. 성능 개선 도우미 `os.cpuUtilization` 지표 또는 CloudWatch `CPUUtilization` 지표에서 증거를 찾습니다. 운영 체제에 과부하가 걸린 경우 향상된 모니터링 지표를 살펴보고 더 자세히 진단하세요. 특히 프로세스 목록과 각 프로세스에서 소비되는 CPU 비율을 살펴보세요.
  + 상위 SQL 문이 너무 많은 CPU를 소비하고 있습니다.

    CPU 사용량에 연결된 문을 검사하여 CPU를 더 적게 사용할 수 있는지 확인합니다. `EXPLAIN` 명령을 실행하고 가장 큰 영향을 미치는 계획 노드에 중점을 둡니다. PostgreSQL 실행 계획 비주얼라이저를 사용하는 것이 좋습니다. [http://explain.dalibo.com/](http://explain.dalibo.com/) 섹션을 참조해 이 도구를 사용해 보세요.

### 워크로드 변경에 대응
<a name="wait-event.cpu.actions.workload"></a>

워크로드가 변경된 경우 다음 변경 유형을 찾습니다.

새 쿼리  
새 쿼리가 예상되는지 확인합니다. 그렇다면 실행 계획과 초당 실행 횟수가 예상되는지 확인합니다.

데이터 집합 크기 증가  
파티셔닝이 아직 구현되지 않은 경우 파티셔닝이 도움이 될지 확인합니다. 이 전략은 쿼리가 검색해야 하는 페이지 수를 줄일 수 있습니다.

인덱스 유지 관리 또는 생성  
유지 관리 일정이 예상되는지 확인합니다. 모범 사례는 피크 활동 이외의 유지 관리 활동을 예약하는 것입니다.

새로운 함수  
테스트 중에 이러한 함수가 예상대로 작동하는지 확인합니다. 특히 초당 실행 횟수가 예상되는지 확인합니다.

새로운 연산자  
테스트 중에 이러한 함수가 예상대로 작동하는지 확인합니다.

병렬 쿼리 실행 증가  
다음 상황 중 한 가지라도 발생했는지 확인합니다.  
+ 포함된 관계식 또는 인덱스의 크기가 갑자기 커져 `min_parallel_table_scan_size` 또는 `min_parallel_index_scan_size`와 크게 다릅니다.
+ `parallel_setup_cost` 또는 `parallel_tuple_cost`에 최근 변경 사항이 적용되었습니다.
+ `max_parallel_workers` 또는 `max_parallel_workers_per_gather`에 최근 변경 사항이 적용되었습니다.

# IO:BufFileRead 및 IO:BufFileWrite
<a name="wait-event.iobuffile"></a>

`IO:BufFileRead` 및 `IO:BufFileWrite` 이벤트는 RDS for PostgreSQL이 임시 파일을 만들 때 발생합니다. 연산에 현재 정의된 작업 메모리 파라미터보다 많은 메모리가 필요한 경우 임시 데이터를 영구 스토리지에 씁니다. 이 작업을 *디스크로 유출*이라고도 합니다. 임시 파일 및 사용량에 대한 자세한 내용은 [PostgreSQL을 사용한 임시 파일 관리](PostgreSQL.ManagingTempFiles.md) 섹션을 참조하세요.

**Topics**
+ [지원되는 엔진 버전](#wait-event.iobuffile.context.supported)
+ [컨텍스트](#wait-event.iobuffile.context)
+ [대기 증가의 가능한 원인](#wait-event.iobuffile.causes)
+ [작업](#wait-event.iobuffile.actions)

## 지원되는 엔진 버전
<a name="wait-event.iobuffile.context.supported"></a>

이 대기 이벤트 정보는 모든 RDS for PostgreSQL 버전에서 지원됩니다.

## 컨텍스트
<a name="wait-event.iobuffile.context"></a>

`IO:BufFileRead`와 `IO:BufFileWrite`는 작업 메모리 영역 및 유지 관리 작업 메모리 영역과 관련이 있습니다. 이러한 로컬 메모리 영역에 대한 자세한 내용은 PostgreSQL 설명서에서 [리소스 소비](https://www.postgresql.org/docs/current/runtime-config-resource.html)를 참조하세요.

기본값은 `work_mem` 또는 4MB입니다. 한 세션이 병렬로 연산을 수행하는 경우 병렬 처리를 처리하는 각 작업자는 4MB의 메모리를 사용합니다. 이런 이유로, `work_mem`을 신중하게 설정하세요. 값을 너무 많이 늘리면 많은 세션을 실행하는 데이터베이스가 너무 많은 메모리를 소비할 수 있습니다. 값을 너무 낮게 설정하면 RDS for PostgreSQL이 로컬 스토리지에 임시 파일을 만듭니다. 이러한 임시 파일의 디스크 I/O는 성능을 저하시킬 수 있습니다.

다음과 같은 일련의 이벤트를 관찰하면 데이터베이스에서 임시 파일이 생성될 수 있습니다.

1. 갑작스럽고 급격한 가용성 감소

1. 여유 공간을 위한 신속한 복구

'체인톱' 패턴도 볼 수 있습니다. 이 패턴은 데이터베이스에서 지속적으로 작은 파일을 생성한다는 것을 의미할 수 있습니다.

## 대기 증가의 가능한 원인
<a name="wait-event.iobuffile.causes"></a>

일반적으로 이러한 대기 이벤트는 `work_mem` 또는 `maintenance_work_mem` 파라미터가 할당하는 것보다 더 많은 메모리를 소모하는 연산에 의해 발생합니다. 보정을 위해 연산은 임시 파일에 기록합니다. `IO:BufFileRead`와 `IO:BufFileWrite` 이벤트의 일반적인 원인에는 다음이 포함됩니다.

**작업 메모리 영역에 존재하는 것보다 많은 메모리가 필요한 쿼리**  
다음 특성을 가진 쿼리는 작업 메모리 영역을 사용합니다.  
+ 해시 조인.
+ `ORDER BY` 절
+ `GROUP BY` 절
+ `DISTINCT`
+ 윈도 함수
+ `CREATE TABLE AS SELECT`
+ 구체화 뷰 새로고침

**작업 메모리 영역에 존재하는 것보다 많은 메모리가 필요한 명령문**  
다음 명령문에서는 유지 관리 작업 메모리 영역을 사용합니다.  
+ `CREATE INDEX`
+ `CLUSTER`

## 작업
<a name="wait-event.iobuffile.actions"></a>

대기 이벤트의 원인에 따라 다른 작업을 권장합니다.

**Topics**
+ [문제 식별](#wait-event.iobuffile.actions.problem)
+ [조인 쿼리 검토](#wait-event.iobuffile.actions.joins)
+ [ORDER BY와 GROUP BY 쿼리를 검토합니다.](#wait-event.iobuffile.actions.order-by)
+ [DISTINCT 연산 사용을 피하세요.](#wait-event.iobuffile.actions.distinct)
+ [GROUP BY 함수 대신 윈도 함수를 사용하는 것이 좋습니다.](#wait-event.iobuffile.actions.window)
+ [구체화된 뷰 및 CTAS 명령문 조사](#wait-event.iobuffile.actions.mv-refresh)
+ [인덱스를 다시 구축할 때 pg\$1repack 사용](#wait-event.iobuffile.actions.pg_repack)
+ [테이블을 클러스터링할 때 maintenance\$1work\$1mem 증가](#wait-event.iobuffile.actions.cluster)
+ [메모리를 조정하여 IO:BufFileRead 및 IO:BufFileWrite 방지](#wait-event.iobuffile.actions.tuning-memory)

### 문제 식별
<a name="wait-event.iobuffile.actions.problem"></a>

Performance Insights에서 직접 임시 파일 사용량을 볼 수 있습니다. 자세한 내용은 [성능 개선 도우미를 사용하여 임시 파일 사용량 확인](PostgreSQL.ManagingTempFiles.Example.md) 섹션을 참조하세요. Performance Insights가 비활성화되면 `IO:BufFileRead` 및 `IO:BufFileWrite` 작업이 증가하는 것을 확인할 수 있습니다.

문제의 원인을 식별하기 위해 지정한 임계값 KB를 초과하는 임시 파일을 생성하는 모든 쿼리를 로깅하도록 `log_temp_files` 파라미터를 설정할 수 있습니다. 기본적으로 `log_temp_files`는 `-1`로 설정되어 있어 이 로깅 기능을 해제합니다. 이 파라미터를 `0`로 설정하면 RDS for PostgreSQL이 모든 임시 파일을 로깅합니다. `1024`으로 설정하면 RDS for PostgreSQL은 1MB보다 큰 임시 파일을 생성하는 모든 쿼리를 로깅합니다. `log_temp_files`에 관한 자세한 내용은 PostgreSQL 문서의 [오류 보고 및 로깅](https://www.postgresql.org/docs/current/runtime-config-logging.html)을 참조하세요.

### 조인 쿼리 검토
<a name="wait-event.iobuffile.actions.joins"></a>

쿼리에서 조인이 사용될 가능성이 높습니다. 예를 들어 다음 쿼리는 네 개의 테이블을 조인합니다.

```
SELECT * 
       FROM "order" 
 INNER JOIN order_item 
       ON (order.id = order_item.order_id)
 INNER JOIN customer 
       ON (customer.id = order.customer_id)
 INNER JOIN customer_address 
       ON (customer_address.customer_id = customer.id AND 
           order.customer_address_id = customer_address.id)
 WHERE customer.id = 1234567890;
```

임시 파일 사용량 스파이크의 원인은 쿼리 자체의 문제입니다. 예를 들어, 끊어진 절은 조인을 제대로 필터링하지 않을 수 있습니다. 다음 예에서 두 번째 내부 조인을 고려해 보겠습니다.

```
SELECT * 
       FROM "order"
 INNER JOIN order_item 
       ON (order.id = order_item.order_id)
 INNER JOIN customer 
       ON (customer.id = customer.id)
 INNER JOIN customer_address 
       ON (customer_address.customer_id = customer.id AND 
           order.customer_address_id = customer_address.id)
 WHERE customer.id = 1234567890;
```

위의 쿼리가 실수로 `customer.id`를 `customer.id`에 조인하여 모든 고객과 모든 주문 사이에 데카르트 프로덕트를 생성합니다. 이러한 유형의 우발적인 조인은 큰 임시 파일을 생성합니다. 테이블의 크기에 따라 테카르트 쿼리(Cartesian query)는 스토리지를 채울 수도 있습니다. 다음 조건이 충족되면 애플리케이션에 데카르트 조인(Cartesian join)이 있을 수 있습니다.
+ 스토리지 가용성이 급격히, 크게 줄어들고 복구가 빨라집니다.
+ 인덱스가 생성되지 않습니다.
+ `CREATE TABLE FROM SELECT` 문이 발행되고 있지 않습니다.
+ 구체화 뷰가 새로 고침이 되지 않습니다.

적절한 키를 사용하여 테이블이 조인되고 있는지 확인하려면 쿼리 및 객체 관계형 매핑 지시어를 검사합니다. 애플리케이션의 특정 쿼리가 항상 호출되는 것은 아니며 일부 쿼리는 동적으로 생성됩니다.

### ORDER BY와 GROUP BY 쿼리를 검토합니다.
<a name="wait-event.iobuffile.actions.order-by"></a>

경우에 따라 `ORDER BY` 절에 임시 파일이 과도하게 발생할 수 있습니다. 다음 지침을 참고하세요.
+ 정렬이 필요할 때 `ORDER BY` 절의 열만 포함합니다. 이 지침은 `ORDER BY` 절에서 수천 개의 행을 반환하고 많은 열을 지정하는 쿼리에 특히 중요합니다.
+ 오름차순 또는 내림차순이 동일한 열과 일치할 때 `ORDER BY` 절을 가속하기 위해 인덱스를 생성하는 것이 좋습니다. 부분 인덱스는 작기 때문에 선호됩니다. 작은 인덱스를 더 빠르게 읽고 탐색할 수 있습니다.
+ null 값을 허용할 수 있는 열에 대한 인덱스를 만드는 경우 null 값을 인덱스의 끝에 저장할지 아니면 인덱스의 시작 부분에 저장할지 고려해야 합니다.

  가능한 경우 결과 집합을 필터링하여 정렬해야 하는 행 수를 줄입니다. `WITH` 절의 명령문 또는 하위 쿼리를 사용할 경우, 내부 쿼리가 결과 집합을 생성하여 외부 쿼리로 전달한다는 것을 기억하세요. 쿼리가 필터링할 수 있는 행이 많을수록 쿼리 정렬이 할 일이 줄어듭니다.
+ 전체 결과 집합을 가져올 필요가 없는 경우 `LIMIT` 절을 사용하세요. 예를 들어 상위 5개 행만 원하는 경우 `LIMIT` 절을 사용하는 쿼리는 결과를 계속 생성하지 않습니다. 이렇게 하면 쿼리에 메모리와 임시 파일이 더 적게 필요합니다.

`GROUP BY` 절을 사용하는 쿼리는 임시 파일이 필요할 수도 있습니다. `GROUP BY` 쿼리는 다음과 같은 함수를 사용하여 값을 요약합니다.
+ `COUNT`
+ `AVG`
+ `MIN`
+ `MAX`
+ `SUM`
+ `STDDEV`

`GROUP BY` 쿼리를 튜닝하려면 `ORDER BY` 쿼리의 권장 사항을 따르세요.

### DISTINCT 연산 사용을 피하세요.
<a name="wait-event.iobuffile.actions.distinct"></a>

가능하면 중복된 행을 제거하기 위해 `DISTINCT` 연산을 사용하지 마세요. 쿼리가 반환하는 불필요하고 중복된 행이 많을수록 `DISTINCT` 연산에 시간이 오래 걸리게 됩니다. 가능하면 `WHERE` 절에 필터를 추가합니다. 다른 테이블에 동일한 필터를 사용하는 경우에도 마찬가지입니다. 쿼리를 필터링하고 조인하면 성능이 향상되고 리소스 사용이 줄어듭니다. 또한 잘못된 보고와 결과를 방지할 수 있습니다.

`DISTINCT`를 동일 테이블의 여러 행에서 사용해야 하는 경우 복합 인덱스를 만드는 것이 좋습니다. 인덱스에서 여러 열을 그룹화하면 고유한 행을 평가하는 시간을 단축할 수 있습니다. 또한 RDS for PostgreSQL 버전 10 이상을 사용하는 경우, `CREATE STATISTICS` 명령을 통해 다중 열의 통계를 상호 연관시킬 수 있습니다.

### GROUP BY 함수 대신 윈도 함수를 사용하는 것이 좋습니다.
<a name="wait-event.iobuffile.actions.window"></a>

`GROUP BY`를 사용하여 결과 집합을 변경한 다음 집계된 결과를 검색합니다. 윈도 함수를 사용하면 결과 집합을 변경하지 않고 데이터를 집계할 수 있습니다. 윈도 함수는 `OVER` 절을 통해 다른 행과 상호 연관시키는 쿼리에 의해 정의된 집합에서 계산을 수행합니다. 윈도 함수의 모든 `GROUP BY` 함수를 사용할 수 있으며, 다음과 같은 함수도 사용할 수 있습니다.
+ `RANK`
+ `ARRAY_AGG`
+ `ROW_NUMBER`
+ `LAG`
+ `LEAD`

윈도 함수에 의해 생성된 임시 파일 수를 최소화하려면 두 개의 별개의 집계가 필요할 때 동일한 결과 집합에 대한 중복을 제거합니다. 다음과 같은 쿼리를 가정하겠습니다.

```
SELECT sum(salary) OVER (PARTITION BY dept ORDER BY salary DESC) as sum_salary
     , avg(salary) OVER (PARTITION BY dept ORDER BY salary ASC) as avg_salary
  FROM empsalary;
```

`WINDOW` 절을 사용하여 다음과 같이 쿼리를 다시 작성할 수 있습니다.

```
SELECT sum(salary) OVER w as sum_salary
         , avg(salary) OVER w as_avg_salary
    FROM empsalary
  WINDOW w AS (PARTITION BY dept ORDER BY salary DESC);
```

기본적으로 RDS for PostgreSQL 실행 플래너는 유사한 노드를 통합하므로 연산을 복제하지 않습니다. 그러나 윈도 블록에 대한 명시적 선언을 사용하면 쿼리를 보다 쉽게 유지할 수 있습니다. 또한 중복을 방지하여 성능을 향상시킬 수 있습니다.

### 구체화된 뷰 및 CTAS 명령문 조사
<a name="wait-event.iobuffile.actions.mv-refresh"></a>

구체화된 뷰가 새로 고쳐지면 쿼리가 실행됩니다. 이 쿼리에는 `GROUP BY`, `ORDER BY`, 또는 `DISTINCT` 같은 연산이 포함될 수 있습니다. 새로 고침을 하는 동안 많은 수의 임시 파일과 `IO:BufFileWrite` 및 `IO:BufFileRead` 대기 이벤트를 관찰할 수 있습니다. 마찬가지로, `SELECT` 문을 기반으로 테이블을 생성할 때도 마찬가지입니다. `CREATE TABLE` 문은 쿼리를 실행합니다. 필요한 임시 파일을 줄이려면 쿼리를 최적화하세요.

### 인덱스를 다시 구축할 때 pg\$1repack 사용
<a name="wait-event.iobuffile.actions.pg_repack"></a>

인덱스를 생성하면 엔진이 결과 세트를 정렬합니다. 테이블의 크기가 커지고 인덱싱된 열의 값이 다양해짐에 따라 임시 파일에 더 많은 공간이 필요합니다. 대부분의 경우 유지 관리 작업 메모리 영역을 수정하지 않으면 큰 테이블에 대한 임시 파일이 생성되지 않도록 할 수 없습니다. `maintenance_work_mem`에 대한 자세한 내용은 PostgreSQL 설명서의 [https://www.postgresql.org/docs/current/runtime-config-resource.html](https://www.postgresql.org/docs/current/runtime-config-resource.html)을 참조하십시오.

큰 인덱스를 다시 생성할 때 가능한 해결 방법은 pg\$1repack 확장을 사용하는 것입니다. 자세한 내용은 pg\$1repack 문서의 [최소한의 잠금으로 PostgreSQL 데이터베이스의 테이블 재구성](https://reorg.github.io/pg_repack/)을 참조하세요. RDS for PostgreSQL DB 인스턴스에서 확장을 설정하는 방법은 [pg\$1repack 확장을 사용하여 테이블 및 인덱스에서 부풀림을 줄입니다.](Appendix.PostgreSQL.CommonDBATasks.pg_repack.md) 섹션을 참조하세요.

### 테이블을 클러스터링할 때 maintenance\$1work\$1mem 증가
<a name="wait-event.iobuffile.actions.cluster"></a>

`CLUSTER` 명령은 *index\$1name*에 의해 지정된 기존 인덱스를 기반으로 하는 *table\$1name*에 의해 지정된 테이블을 클러스터링합니다. RDS for PostgreSQL은 지정된 인덱스의 순서와 일치하도록 테이블을 물리적으로 다시 만듭니다.

마그네틱 스토리지가 널리 보급되었을 때 스토리지 처리량이 제한적이었기 때문에 클러스터링이 일반적이었습니다. SSD 기반 스토리지가 일반적이므로 클러스터링은 인기가 덜합니다. 그러나 테이블을 클러스터링하는 경우에도 테이블 크기, 인덱스, 쿼리 등에 따라 성능이 약간 향상될 수 있습니다.

`CLUSTER` 명령을 실행하거나 `IO:BufFileWrite` 및 `IO:BufFileRead` 대기 이벤트를 관찰할 수 있는 경우, `maintenance_work_mem`을 조정합니다. 메모리 크기를 상당히 크게 늘립니다. 값이 높으면 엔진이 클러스터링 작업에 더 많은 메모리를 사용할 수 있음을 의미합니다.

### 메모리를 조정하여 IO:BufFileRead 및 IO:BufFileWrite 방지
<a name="wait-event.iobuffile.actions.tuning-memory"></a>

일부 상황에서는 메모리를 조정해야 합니다. 목표는 다음과 같이 적절한 파라미터를 사용하여 다음 소비 영역에서 메모리 균형을 맞추는 것입니다.
+ `work_mem` 값 
+ `shared_buffers` 값 디스카운트 후 남은 메모리
+ 열리고 사용 중인 최대 연결은 `max_connections`로 제한됩니다.

메모리 조정에 대한 자세한 내용은 PostgreSQL 설명서에서 [리소스 소비](https://www.postgresql.org/docs/current/runtime-config-resource.html)를 참조하세요.

#### 작업 메모리 영역 크기 증가
<a name="wait-event.iobuffile.actions.tuning-memory.work-mem"></a>

경우에 따라 세션에서 사용하는 메모리를 늘리는 것이 유일한 방법입니다. 쿼리가 올바르게 작성되고 조인에 올바른 키를 사용하는 경우 `work_mem` 값을 증가시키는 것이 좋습니다.

쿼리가 생성하는 임시 파일 수를 확인하려면 `log_temp_files`를 `0`에 설정하세요. `work_mem` 값을 로그에서 식별된 최대값으로 늘리면 쿼리가 임시 파일을 생성하지 못하도록 합니다. 하지만 `work_mem`은 각 연결 또는 병렬 워커에 대해 계획 노드당 최대값을 설정합니다. 데이터베이스에 5,000개의 연결이 있고 각 연결이 256MiB 메모리를 사용하는 경우 엔진에 1.2TiB의 RAM이 필요합니다. 따라서 인스턴스의 메모리가 부족할 수 있습니다.

#### 공유 버퍼 풀에 충분한 메모리 예약
<a name="wait-event.iobuffile.actions.tuning-memory.shared-pool"></a>

데이터베이스는 작업 메모리 영역뿐만 아니라 공유 버퍼 풀과 같은 메모리 영역을 사용합니다. `work_mem`을 늘리기 전에 이러한 추가 메모리 영역의 요구 사항을 고려하는 것이 좋습니다.

예를 들어 RDS for PostgreSQL 인스턴스 클래스가 db.r5.2xlarge라고 가정합니다. 이 클래스에는 64GiB의 메모리가 있습니다. 기본적으로 메모리의 25%는 공유 버퍼 풀에 예약되어 있습니다. 공유 메모리 영역에 할당된 양을 빼면 16,384MB가 남아 있습니다. 운영 체제와 엔진에도 메모리가 필요하므로 나머지 메모리를 작업 메모리 영역에만 할당하지 마세요.

`work_mem`에 할당할 수 있는 메모리는 인스턴스 클래스에 따라 달라집니다. 더 큰 인스턴스 클래스를 사용하는 경우 더 많은 메모리를 사용할 수 있습니다. 하지만 앞의 예에서는 16GiB를 초과하여 사용할 수 없습니다. 그렇지 않으면 메모리가 부족할 때 인스턴스를 사용할 수 없게 됩니다. 인스턴스를 사용할 수 없는 상태에서 복구하기 위해 RDS for PostgreSQL 자동화 서비스가 자동으로 다시 시작됩니다.

#### 연결 수 관리
<a name="wait-event.iobuffile.actions.tuning-memory.connections"></a>

데이터베이스 인스턴스에 5,000개의 동시 연결이 있다고 가정합니다. 각 연결은 최소 4MiB의 `work_mem`을 사용합니다. 연결의 메모리 소비량이 많으면 성능이 저하될 수 있습니다. 다음과 같은 옵션이 있습니다.
+ vCPU가 더 많은 대규모 인스턴스 클래스로 업그레이드하세요.
+ 연결 프록시 또는 풀러를 사용하여 동시 데이터베이스 연결 수를 줄이세요.

프록시의 경우 Amazon RDS 프록시, PGBouncer 또는 애플리케이션에 기반한 연결 풀러를 고려하세요. 이 솔루션은 CPU 부하를 완화합니다. 또한 모든 연결에 작업 메모리 영역이 필요할 때 위험을 줄일 수 있습니다. 데이터베이스 연결 수가 적으면 `work_mem` 값을 늘릴 수 있습니다. 이런 방법으로 `IO:BufFileRead`와 `IO:BufFileWrite` 대기 이벤트의 발생을 줄일 수 있습니다. 또한 작업 메모리 영역을 기다리는 쿼리의 속도가 크게 향상됩니다.

# IO:DataFileRead
<a name="wait-event.iodatafileread"></a>

`IO:DataFileRead` 이벤트는 공유 메모리에서 페이지를 사용할 수 없기 때문에 저장소에서 필요한 페이지를 읽기 위해 백엔드 프로세스에서 연결이 대기할 때 발생합니다.

**Topics**
+ [지원되는 엔진 버전](#wait-event.iodatafileread.context.supported)
+ [컨텍스트](#wait-event.iodatafileread.context)
+ [대기 증가의 가능한 원인](#wait-event.iodatafileread.causes)
+ [작업](#wait-event.iodatafileread.actions)

## 지원되는 엔진 버전
<a name="wait-event.iodatafileread.context.supported"></a>

이 대기 이벤트 정보는 모든 RDS for PostgreSQL 버전에서 지원됩니다.

## 컨텍스트
<a name="wait-event.iodatafileread.context"></a>

모든 쿼리 및 데이터 조작(DML) 작업은 버퍼 풀의 페이지에 액세스합니다. 읽기를 유도할 수 있는 명령문은 `SELECT`,`UPDATE` 및 `DELETE`가 있습니다. 예를 들어, 하나의 `UPDATE`는 테이블 또는 인덱스에서 페이지를 읽을 수 있습니다. 요청하거나 업데이트되는 페이지가 공유 버퍼 풀에 없는 경우 이 읽기는 `IO:DataFileRead` 이벤트를 생성합니다.

공유 버퍼 풀은 유한하기 때문에 채워질 수 있습니다. 이 경우 메모리에 없는 페이지에 대한 요청은 데이터베이스가 디스크에서 블록을 읽도록 강제로 합니다. 만약 `IO:DataFileRead` 이벤트가 자주 발생한다면 공유 버퍼 풀이 너무 작아서 워크로드를 수용할 수 없는 것일 수도 있습니다. 이 문제는 버퍼 풀에 맞지 않는 많은 수의 행을 읽는 `SELECT` 쿼리에서 극심하게 발생합니다. 버퍼 풀에 대한 자세한 내용은 PostgreSQL 설명서의 [리소스 소비](https://www.postgresql.org/docs/current/runtime-config-resource.html)를 참조하세요.

## 대기 증가의 가능한 원인
<a name="wait-event.iodatafileread.causes"></a>

`IO:DataFileRead` 이벤트의 일반적인 원인에는 다음이 포함됩니다.

**연결 스파이크**  
동일한 수의 Io:DataFileRead 대기 이벤트를 생성하는 여러 연결을 찾을 수 있습니다. 이 경우 `IO:DataFileRead` 이벤트의 스파이크 갑작스럽고 큰 증가)가 발생할 수 있습니다.

**순차 검사를 수행하는 SELECT 및 DML 문**  
애플리케이션에서 새 작업을 수행하고 있을 수 있습니다. 또는 새 실행 계획으로 인해 기존 작업이 변경될 수 있습니다. 이러한 경우 테이블(특히 큰 테이블)이 더 큰 `seq_scan` 값을 가진 테이블을 찾으세요. `pg_stat_user_tables`를 쿼리하여 탐색 더 많은 읽기 작업을 생성하는 쿼리를 추적하려면 `pg_stat_statements` 확장 프로그램을 사용하세요.

**대용량 데이터 세트를 위한 CTAS 및 CREATE 인덱스**  
*CTAS*는 `CREATE TABLE AS SELECT` 문입니다. 대용량 데이터 세트를 소스로 사용하여 CTAS를 실행하거나 큰 테이블에 인덱스를 만드는 경우 `IO:DataFileRead` 이벤트가 발생할 수 있습니다. 인덱스를 만들 때 데이터베이스는 순차 스캔을 사용하여 전체 객체를 읽어야 할 수 있습니다. CTAS는 페이지가 메모리에 없을 때 `IO:DataFile` 리드를 생성합니다.

**여러 베큠 작업자가 동시에 실행**  
베큠 작업자는 수동 또는 자동으로 트리거될 수 있습니다. 공격적인 베큠 전략을 채택하는 것이 좋습니다. 그러나 테이블에 업데이트되거나 삭제된 행이 많으면 `IO:DataFileRead` 대기가 늘어납니다. 공간을 회수한 후 `IO:DataFileRead`에 소비되는 베큠 시간이 감소합니다.

**대용량 데이터 수집**  
애플리케이션이 대용량 데이터를 수집할 때 `ANALYZE` 연산이 더 자주 발생할 수 있습니다. `ANALYZE` 프로세스는 autovacuum 시작 관리자에 의해 트리거되거나 수동으로 호출될 수 있습니다.  
`ANALYZE` 연산은 테이블의 하위 집합을 읽습니다. 스캔해야 하는 페이지 수는 `default_statistics_target` 값에 30을 곱하여 계산합니다. 자세한 내용은 [PostgreSQL 설명서](https://www.postgresql.org/docs/current/runtime-config-query.html#GUC-DEFAULT-STATISTICS-TARGET)를 참조하세요. `default_statistics_target` 파라미터 1에서 10,000 사이의 값을 허용합니다. 여기서 기본값은 100입니다.

**리소스 부족**  
인스턴스 네트워크 대역폭 또는 CPU가 사용되는 경우 `IO:DataFileRead` 이벤트가 더 자주 발생할 수 있습니다.

## 작업
<a name="wait-event.iodatafileread.actions"></a>

대기 이벤트의 원인에 따라 다른 작업을 권장합니다.

**Topics**
+ [대기를 생성하는 쿼리에 대한 술어 필터 확인](#wait-event.iodatafileread.actions.filters)
+ [유지 보수 작업의 영향 최소화](#wait-event.iodatafileread.actions.maintenance)
+ [많은 연결 수에 대응](#wait-event.iodatafileread.actions.connections)

### 대기를 생성하는 쿼리에 대한 술어 필터 확인
<a name="wait-event.iodatafileread.actions.filters"></a>

`IO:DataFileRead` 대기 이벤트를 생성 중인 특정 쿼리를 식별한다고 가정합니다. 다음 기법을 사용하여 식별할 수 있습니다.
+ 성능 개선 도우미
+ `pg_stat_statements` 확장에서 제공하는 것과 같은 카탈로그 뷰
+ 카탈로그 뷰 `pg_stat_all_tables`, 주기적으로 증가하는 물리적 읽기 횟수가 표시되는 경우
+ `pg_statio_all_tables` 뷰, `_read` 카운터의 증가를 보여주는 경우

이 쿼리의 술어(`WHERE` 절)에 사용되는 필터를 결정하는 것이 좋습니다. 아래 지침을 따르세요.
+ `EXPLAIN` 명령을 실행합니다. 출력에서 어떤 유형의 스캔이 사용되는지 식별합니다. 순차 스캔이 반드시 문제를 의미하지는 않습니다. 순차 스캔을 사용하는 쿼리는 필터를 사용하는 쿼리와 비교할 때 자연스럽게 더 많은 `IO:DataFileRead` 이벤트를 생성합니다.

  `WHERE` 절에 열거된 열이 인덱싱되었는지 확인합니다. 그렇지 않은 경우 이 열에 대한 인덱스를 만드는 것이 좋습니다. 이 접근법은 순차적 스캔을 피하고 `IO:DataFileRead` 이벤트를 줄입니다. 쿼리에 제한적인 필터가 있지만 순차적 스캔을 생성하는 경우 적절한 인덱스가 사용되고 있는지 평가합니다.
+ 쿼리가 매우 큰 테이블에 액세스하고 있는지 확인합니다. 경우에 따라 테이블을 분할하면 성능이 향상되어 쿼리가 필요한 파티션만 읽을 수 있습니다.
+ 조인 작업에서 카디널리티(총 행 수)를 조사합니다. `WHERE` 절을 위해 필터에 전달하는 값이 얼마나 제한적인지 확인하세요. 가능한 경우 쿼리를 조정하여 계획의 각 단계에서 전달되는 행 수를 줄입니다.

### 유지 보수 작업의 영향 최소화
<a name="wait-event.iodatafileread.actions.maintenance"></a>

`VACUUM`과 `ANALYZE` 같은 유지 관리 작업이 중요합니다. 이러한 유지 관리 작업과 관련된 `IO:DataFileRead` 대기 이벤트를 찾을 수 있으므로 끄지 않는 것이 좋습니다. 다음 방법을 사용하면 이러한 작업의 효과를 최소화할 수 있습니다.
+ 사용량이 적은 시간대에 수동으로 유지 관리 작업을 실행합니다. 이 기술은 데이터베이스가 자동 연산의 임계값에 도달하지 못하도록 합니다.
+ 매우 큰 테이블의 경우 테이블을 분할하는 것이 좋습니다. 이 기술은 유지보수 작업의 오버헤드를 줄여줍니다. 데이터베이스는 유지 관리가 필요한 파티션에만 액세스합니다.
+ 대량의 데이터를 수집할 때는 자동 분석 기능을 사용하지 않도록 설정하는 것이 좋습니다.

다음 공식이 true이면 테이블에 대해 autovacuum 기능이 자동으로 트리거됩니다.

```
pg_stat_user_tables.n_dead_tup > (pg_class.reltuples x autovacuum_vacuum_scale_factor) + autovacuum_vacuum_threshold
```

뷰 `pg_stat_user_tables`와 카탈로그 `pg_class`에는 여러 개의 행이 있습니다. 한 행은 테이블의 한 행에 대응할 수 있습니다. 이 공식은 `reltuples`가 특정 테이블을 위한 것이라고 가정합니다. 파라미터 `autovacuum_vacuum_scale_factor`(기본적으로 0.20)와 `autovacuum_vacuum_threshold`(기본적으로 50개의 튜플)는 일반적으로 전체 인스턴스에 대해 전역으로 설정됩니다. 그러나 특정 테이블에 대해 다른 값을 설정할 수 있습니다.

**Topics**
+ [불필요한 공간을 소비하는 테이블 찾기](#wait-event.iodatafileread.actions.maintenance.tables)
+ [불필요하게 공간을 소비하는 인덱스 찾기](#wait-event.iodatafileread.actions.maintenance.indexes)
+ [Autovacuum이 가능한 테이블 찾기](#wait-event.iodatafileread.actions.maintenance.autovacuumed)

#### 불필요한 공간을 소비하는 테이블 찾기
<a name="wait-event.iodatafileread.actions.maintenance.tables"></a>

불필요하게 공간을 소비하는 테이블을 찾으려면 PostgreSQL`pgstattuple` 확장의 함수를 사용할 수 있습니다. 이 확장(모듈)은 모든 RDS for PostgreSQL DB 인스턴스에서 기본적으로 사용할 수 있으며, 다음 명령을 사용하여 인스턴스에서 인스턴스화할 수 있습니다.

```
CREATE EXTENSION pgstattuple;
```

이 확장에 대한 자세한 내용은 PostgreSQL 설명서의 [pgstattuple](https://www.postgresql.org/docs/current/pgstattuple.html)를 참조하세요.

애플리케이션에서 테이블 및 인덱스 팽창을 검사할 수 있습니다. 자세한 내용은 [테이블 및 인덱스 팽창 진단](https://docs.aws.amazon.com//AmazonRDS/latest/AuroraUserGuide/AuroraPostgreSQL.diag-table-ind-bloat.html)을 참조하세요.

#### 불필요하게 공간을 소비하는 인덱스 찾기
<a name="wait-event.iodatafileread.actions.maintenance.indexes"></a>

다음 쿼리를 실행하여 부풀려진 인덱스를 찾고 읽기 권한이 있는 테이블에서 불필요하게 소비되는 공간을 추정할 수 있습니다.

```
-- WARNING: rows with is_na = 't' are known to have bad statistics ("name" type is not supported).
-- This query is compatible with PostgreSQL 8.2 and later.

SELECT current_database(), nspname AS schemaname, tblname, idxname, bs*(relpages)::bigint AS real_size,
  bs*(relpages-est_pages)::bigint AS extra_size,
  100 * (relpages-est_pages)::float / relpages AS extra_ratio,
  fillfactor, bs*(relpages-est_pages_ff) AS bloat_size,
  100 * (relpages-est_pages_ff)::float / relpages AS bloat_ratio,
  is_na
  -- , 100-(sub.pst).avg_leaf_density, est_pages, index_tuple_hdr_bm, 
  -- maxalign, pagehdr, nulldatawidth, nulldatahdrwidth, sub.reltuples, sub.relpages 
  -- (DEBUG INFO)
FROM (
  SELECT coalesce(1 +
       ceil(reltuples/floor((bs-pageopqdata-pagehdr)/(4+nulldatahdrwidth)::float)), 0 
       -- ItemIdData size + computed avg size of a tuple (nulldatahdrwidth)
    ) AS est_pages,
    coalesce(1 +
       ceil(reltuples/floor((bs-pageopqdata-pagehdr)*fillfactor/(100*(4+nulldatahdrwidth)::float))), 0
    ) AS est_pages_ff,
    bs, nspname, table_oid, tblname, idxname, relpages, fillfactor, is_na
    -- , stattuple.pgstatindex(quote_ident(nspname)||'.'||quote_ident(idxname)) AS pst, 
    -- index_tuple_hdr_bm, maxalign, pagehdr, nulldatawidth, nulldatahdrwidth, reltuples 
    -- (DEBUG INFO)
  FROM (
    SELECT maxalign, bs, nspname, tblname, idxname, reltuples, relpages, relam, table_oid, fillfactor,
      ( index_tuple_hdr_bm +
          maxalign - CASE -- Add padding to the index tuple header to align on MAXALIGN
            WHEN index_tuple_hdr_bm%maxalign = 0 THEN maxalign
            ELSE index_tuple_hdr_bm%maxalign
          END
        + nulldatawidth + maxalign - CASE -- Add padding to the data to align on MAXALIGN
            WHEN nulldatawidth = 0 THEN 0
            WHEN nulldatawidth::integer%maxalign = 0 THEN maxalign
            ELSE nulldatawidth::integer%maxalign
          END
      )::numeric AS nulldatahdrwidth, pagehdr, pageopqdata, is_na
      -- , index_tuple_hdr_bm, nulldatawidth -- (DEBUG INFO)
    FROM (
      SELECT
        i.nspname, i.tblname, i.idxname, i.reltuples, i.relpages, i.relam, a.attrelid AS table_oid,
        current_setting('block_size')::numeric AS bs, fillfactor,
        CASE -- MAXALIGN: 4 on 32bits, 8 on 64bits (and mingw32 ?)
          WHEN version() ~ 'mingw32' OR version() ~ '64-bit|x86_64|ppc64|ia64|amd64' THEN 8
          ELSE 4
        END AS maxalign,
        /* per page header, fixed size: 20 for 7.X, 24 for others */
        24 AS pagehdr,
        /* per page btree opaque data */
        16 AS pageopqdata,
        /* per tuple header: add IndexAttributeBitMapData if some cols are null-able */
        CASE WHEN max(coalesce(s.null_frac,0)) = 0
          THEN 2 -- IndexTupleData size
          ELSE 2 + (( 32 + 8 - 1 ) / 8) 
          -- IndexTupleData size + IndexAttributeBitMapData size ( max num filed per index + 8 - 1 /8)
        END AS index_tuple_hdr_bm,
        /* data len: we remove null values save space using it fractionnal part from stats */
        sum( (1-coalesce(s.null_frac, 0)) * coalesce(s.avg_width, 1024)) AS nulldatawidth,
        max( CASE WHEN a.atttypid = 'pg_catalog.name'::regtype THEN 1 ELSE 0 END ) > 0 AS is_na
      FROM pg_attribute AS a
        JOIN (
          SELECT nspname, tbl.relname AS tblname, idx.relname AS idxname, 
            idx.reltuples, idx.relpages, idx.relam,
            indrelid, indexrelid, indkey::smallint[] AS attnum,
            coalesce(substring(
              array_to_string(idx.reloptions, ' ')
               from 'fillfactor=([0-9]+)')::smallint, 90) AS fillfactor
          FROM pg_index
            JOIN pg_class idx ON idx.oid=pg_index.indexrelid
            JOIN pg_class tbl ON tbl.oid=pg_index.indrelid
            JOIN pg_namespace ON pg_namespace.oid = idx.relnamespace
          WHERE pg_index.indisvalid AND tbl.relkind = 'r' AND idx.relpages > 0
        ) AS i ON a.attrelid = i.indexrelid
        JOIN pg_stats AS s ON s.schemaname = i.nspname
          AND ((s.tablename = i.tblname AND s.attname = pg_catalog.pg_get_indexdef(a.attrelid, a.attnum, TRUE)) 
          -- stats from tbl
          OR  (s.tablename = i.idxname AND s.attname = a.attname))
          -- stats from functional cols
        JOIN pg_type AS t ON a.atttypid = t.oid
      WHERE a.attnum > 0
      GROUP BY 1, 2, 3, 4, 5, 6, 7, 8, 9
    ) AS s1
  ) AS s2
    JOIN pg_am am ON s2.relam = am.oid WHERE am.amname = 'btree'
) AS sub
-- WHERE NOT is_na
ORDER BY 2,3,4;
```

#### Autovacuum이 가능한 테이블 찾기
<a name="wait-event.iodatafileread.actions.maintenance.autovacuumed"></a>

Autovacuum이 가능한 테이블을 찾으려면 다음 쿼리를 실행합니다.

```
--This query shows tables that need vacuuming and are eligible candidates.
--The following query lists all tables that are due to be processed by autovacuum. 
-- During normal operation, this query should return very little.
WITH  vbt AS (SELECT setting AS autovacuum_vacuum_threshold 
              FROM pg_settings WHERE name = 'autovacuum_vacuum_threshold')
    , vsf AS (SELECT setting AS autovacuum_vacuum_scale_factor 
              FROM pg_settings WHERE name = 'autovacuum_vacuum_scale_factor')
    , fma AS (SELECT setting AS autovacuum_freeze_max_age 
              FROM pg_settings WHERE name = 'autovacuum_freeze_max_age')
    , sto AS (SELECT opt_oid, split_part(setting, '=', 1) as param, 
                split_part(setting, '=', 2) as value 
              FROM (SELECT oid opt_oid, unnest(reloptions) setting FROM pg_class) opt)
SELECT
    '"'||ns.nspname||'"."'||c.relname||'"' as relation
    , pg_size_pretty(pg_table_size(c.oid)) as table_size
    , age(relfrozenxid) as xid_age
    , coalesce(cfma.value::float, autovacuum_freeze_max_age::float) autovacuum_freeze_max_age
    , (coalesce(cvbt.value::float, autovacuum_vacuum_threshold::float) + 
         coalesce(cvsf.value::float,autovacuum_vacuum_scale_factor::float) * c.reltuples) 
         as autovacuum_vacuum_tuples
    , n_dead_tup as dead_tuples
FROM pg_class c 
JOIN pg_namespace ns ON ns.oid = c.relnamespace
JOIN pg_stat_all_tables stat ON stat.relid = c.oid
JOIN vbt on (1=1) 
JOIN vsf ON (1=1) 
JOIN fma on (1=1)
LEFT JOIN sto cvbt ON cvbt.param = 'autovacuum_vacuum_threshold' AND c.oid = cvbt.opt_oid
LEFT JOIN sto cvsf ON cvsf.param = 'autovacuum_vacuum_scale_factor' AND c.oid = cvsf.opt_oid
LEFT JOIN sto cfma ON cfma.param = 'autovacuum_freeze_max_age' AND c.oid = cfma.opt_oid
WHERE c.relkind = 'r' 
AND nspname <> 'pg_catalog'
AND (
    age(relfrozenxid) >= coalesce(cfma.value::float, autovacuum_freeze_max_age::float)
    or
    coalesce(cvbt.value::float, autovacuum_vacuum_threshold::float) + 
      coalesce(cvsf.value::float,autovacuum_vacuum_scale_factor::float) * c.reltuples <= n_dead_tup
    -- or 1 = 1
)
ORDER BY age(relfrozenxid) DESC;
```

### 많은 연결 수에 대응
<a name="wait-event.iodatafileread.actions.connections"></a>

Amazon CloudWatch를 모니터링할 때 `DatabaseConnections` 지표 스파이크를 확인할 수도 있습니다. 이 증가는 데이터베이스에 대한 연결 수가 증가했음을 나타냅니다. 다음과 같이 하는 것이 좋습니다.
+ 애플리케이션이 각 인스턴스에서 열 수 있는 연결 수를 제한합니다. 애플리케이션에 내장된 연결 풀 기능이 있는 경우 적절한 연결 수를 설정합니다. 인스턴스의 vCPU가 효과적으로 병렬화할 수 있는 항목에 따라 숫자를 기준으로 합니다.

  애플리케이션에서 연결 풀 기능을 사용하지 않는 경우 Amazon RDS 프록시 또는 대안을 사용하는 것이 좋습니다. 이 접근 방식을 사용하면 애플리케이션이 로드 밸런서와 여러 연결을 열 수 있습니다. 그런 다음 밸런서는 데이터베이스와의 제한된 수의 연결을 열 수 있습니다. 병렬로 실행되는 연결 수가 적을수록 DB 인스턴스는 커널에서 컨텍스트 전환을 덜 수행합니다. 쿼리는 더 빠르게 진행되므로 대기 이벤트 수가 줄어듭니다. 자세한 내용은 [ Amazon RDS Proxy](rds-proxy.md) 섹션을 참조하세요.
+ 가능하면 항상 RDS for PostgreSQL용 읽기 전용 복제본을 활용하세요. 애플리케이션이 읽기 전용 작업을 실행할 때는 이러한 요청을 읽기 전용 복제본으로 보내세요. 이 방법은 기본 (라이터) 노드의 I/O 부담을 줄입니다.
+ DB 인스턴스를 확장하는 것이 좋습니다. 대용량 인스턴스 클래스는 더 많은 메모리를 제공하므로 RDS for PostgreSQL에 페이지를 저장할 수 있는 더 큰 공유 버퍼 풀이 제공됩니다. 크기가 클수록 DB 인스턴스에 연결을 처리할 수 있는 vCPU가 늘어납니다. 더 많은 vCPU가 `IO:DataFileRead` 대기 이벤트를 생성 중인 연산이 쓰기를 하고 있을 때 특히 유용합니다.

# IO:WALWrite
<a name="wait-event.iowalwrite"></a>



**Topics**
+ [지원되는 엔진 버전](#wait-event.iowalwrite.context.supported)
+ [컨텍스트](#wait-event.iowalwrite.context)
+ [대기 증가의 가능한 원인](#wait-event.iowalwrite.causes)
+ [작업](#wait-event.iowalwrite.actions)

## 지원되는 엔진 버전
<a name="wait-event.iowalwrite.context.supported"></a>

이 대기 이벤트 정보는 RDS for PostgreSQL 버전 10 이상의 모든 버전에서 지원됩니다.

## 컨텍스트
<a name="wait-event.iowalwrite.context"></a>

미리 쓰기 로그 데이터를 생성하는 데이터베이스의 활동은 먼저 WAL 버퍼를 채운 다음 비동기적으로 디스크에 씁니다. 대기 이벤트 `IO:WALWrite`는 SQL 세션이 트랜잭션의 COMMIT 호출을 해제할 수 있도록 WAL 데이터가 디스크에 쓰기를 완료하기를 기다리고 있을 때 생성됩니다.

## 대기 증가의 가능한 원인
<a name="wait-event.iowalwrite.causes"></a>

이 대기 이벤트가 자주 발생하는 경우 워크로드와 워크로드가 수행하는 업데이트 유형 및 빈도를 검토해야 합니다. 특히 다음 활동 유형을 찾습니다.

**과중한 DML 활동**  
데이터베이스 테이블의 데이터는 즉시 변경되지 않습니다. 한 테이블에 대한 삽입은 다른 클라이언트가 동일한 테이블을 삽입하거나 업데이트할 때까지 기다려야 할 수 있습니다. 데이터 값 변경(INSERT, UPDATE, DELETE, COMMIT, ROLLBACK TRANSACTION)을 위한 데이터 조작 언어(DML) 문으로 인해 경합이 발생하여 미리 쓰기 로그 파일이 버퍼가 플러시되기를 기다리고 있을 수 있습니다. 이 상황은 과중한 DML 활동을 나타내는 다음과 같은 Amazon RDS 성능 개선 도우미 지표에 캡처됩니다.  
+  `tup_inserted`
+ `tup_updated`
+ `tup_deleted`
+ `xact_rollback`
+ `xact_commit`
이러한 지표에 대한 자세한 내용은 [Amazon RDS for PostgreSQL용 성능 개선 도우미 카운터](USER_PerfInsights_Counters.md#USER_PerfInsights_Counters.PostgreSQL) 섹션을 참조하세요.

**잦은 체크포인트 활동**  
체크포인트가 자주 실행되면 WAL 파일의 수가 늘어납니다. RDS for PostgreSQL에서는 전체 페이지 쓰기가 항상 ‘켜져’ 있습니다. 전체 페이지 쓰기는 데이터 손실을 방지하는 데 도움이 됩니다. 하지만 체크포인트가 너무 자주 발생하면 시스템에 전반적인 성능 문제가 발생할 수 있습니다. DML 활동이 많은 시스템에서는 특히 그렇습니다. 경우에 따라 ‘체크포인트가 너무 자주 발생합니다’라는 오류 메시지가 `postgresql.log`에 표시될 수 있습니다.  
체크포인트를 조정할 때는 비정상 종료 시 복구에 필요한 예상 시간에 맞춰 성능을 신중하게 조정하는 것이 좋습니다.

## 작업
<a name="wait-event.iowalwrite.actions"></a>

이 대기 이벤트 수를 줄이려면 다음 작업을 수행하는 것이 좋습니다.

**Topics**
+ [커밋 수 저감](#wait-event.iowalwrite.actions.problem)
+ [체크포인트 모니터링](#wait-event.iowalwrite.actions.monitor)
+ [스케일 업 IO](#wait-event.iowalwrite.actions.scale-io)
+ [전용 로그 볼륨(DLV)](#wait-event.iowalwrite.actions.dlv)

### 커밋 수 저감
<a name="wait-event.iowalwrite.actions.problem"></a>

커밋 수를 줄이려면 명령문을 트랜잭션 블록으로 결합하면 됩니다. Amazon RDS 성능 개선 도우미를 사용하여 실행 중인 쿼리 유형을 검사합니다. 대규모 유지 관리 작업을 사용량이 적은 시간대로 옮길 수도 있습니다. 예를 들어 프로덕션 외 시간에 인덱스를 생성하거나 `pg_repack` 작업을 사용할 수 있습니다.

### 체크포인트 모니터링
<a name="wait-event.iowalwrite.actions.monitor"></a>

RDS for PostgreSQL DB 인스턴스가 체크포인트용 WAL 파일에 얼마나 자주 쓰는지 확인하기 위해 모니터링할 수 있는 두 가지 파라미터가 있습니다.
+ `log_checkpoints` - 이 파라미터는 기본적으로 ‘on’으로 설정되어 있습니다. 그러면 메시지가 각 체크포인트의 PostgreSQL 로그로 전송됩니다. 이러한 로그 메시지에는 기록된 버퍼 수, 쓰기 소요 시간 및 지정된 체크포인트에 대해 추가, 제거 또는 재활용된 WAL 파일 수가 포함됩니다.

  이 파라미터에 대한 자세한 내용은 PostgreSQL 설명서의 [오류 보고 및 로깅](https://www.postgresql.org/docs/current/runtime-config-logging.html#GUC-LOG-CHECKPOINTS)을 참조하세요.
+ `checkpoint_warning` - 이 파라미터는 초과할 경우 경고가 생성되는 체크포인트 빈도의 임계값(초)을 설정합니다. 기본적으로 이 파라미터는 RDS for PostgreSQL에서 설정되지 않습니다. 이 파라미터의 값을 설정하여 RDS for PostgreSQL DB 인스턴스의 데이터베이스 변경 사항이 WAL 파일이 처리할 크기가 아닌 속도로 작성될 때 경고를 받도록 할 수 있습니다. 예를 들어 이 파라미터를 30으로 설정한다고 가정해 보겠습니다. RDS for PostgreSQL 인스턴스가 30초 빈도보다 더 자주 변경 사항을 작성해야 하는 경우 "체크포인트가 너무 자주 발생합니다"라는 경고가 PostgreSQL 로그로 전송됩니다. 이는 `max_wal_size` 값을 늘려야 함을 나타낼 수 있습니다.

  자세한 내용은 PostgreSQL 설명서에서 [Write Ahead Log](https://www.postgresql.org/docs/current/runtime-config-wal.html#RUNTIME-CONFIG-WAL-CHECKPOINTS)를 참조하세요.

### 스케일 업 IO
<a name="wait-event.iowalwrite.actions.scale-io"></a>

이 입출력(IO) 대기 이벤트 유형은 더 빠른 IO를 제공하도록 초당 입출력 작업 처리량(IOPS)을 조정하여 해결할 수 있습니다. CPU를 확장하는 것보다 IO를 확장하는 것이 좋습니다. 늘어난 CPU는 더 많은 작업을 처리할 수 있고 따라서 IO 병목 현상이 더욱 악화될 수 있으므로 CPU를 확장하면 I/O 경합이 더 심해질 수 있기 때문입니다. 일반적으로 크기 조정 작업을 수행하기 전에 워크로드 조정을 고려하는 것이 좋습니다.

### 전용 로그 볼륨(DLV)
<a name="wait-event.iowalwrite.actions.dlv"></a>

Amazon RDS 콘솔, AWS CLI 또는 Amazon RDS API를 사용하여 프로비저닝된 IOPS(PIOPS) 스토리지를 사용하는 DB 인스턴스 전용 로그 볼륨(DLV)을 사용할 수 있습니다. DLV는 데이터베이스 테이블이 들어 있는 볼륨과 분리된 스토리지 볼륨으로 PostgreSQL 데이터베이스 트랜잭션 로그를 옮깁니다. 자세한 내용은 [전용 로그 볼륨(DLV)](CHAP_Storage.md#CHAP_Storage.dlv) 단원을 참조하십시오.

# IPC:parallel 대기 이벤트
<a name="rpg-ipc-parallel"></a>

다음 `IPC:parallel wait events`는 세션이 병렬 쿼리 실행 작업과 관련된 프로세스 간 통신을 기다리고 있음을 나타냅니다.
+ `IPC:BgWorkerStartup` - 병렬 워커 프로세스가 시작 시퀀스를 완료할 때까지 프로세스가 기다리고 있습니다. 병렬 쿼리 실행을 위해 워커를 초기화할 때 발생합니다.
+ `IPC:BgWorkerShutdown` - 병렬 워커 프로세스가 종료 시퀀스를 완료할 때까지 프로세스가 기다리고 있습니다. 병렬 쿼리 실행의 정리 단계에서 발생합니다.
+ `IPC:ExecuteGather` - 프로세스가 쿼리 실행 중에 병렬 워커 프로세스로부터 데이터 수신을 기다리고 있습니다. 리더 프로세스가 워커로부터 결과를 수집해야 할 때 발생합니다.
+ `IPC:ParallelFinish` - 병렬 워커가 실행을 완료하고 최종 결과를 보고할 때까지 프로세스가 기다리고 있습니다. 병렬 쿼리 실행의 완료 단계에서 발생합니다.

**Topics**
+ [지원되는 엔진 버전](#rpg-ipc-parallel-context-supported)
+ [컨텍스트](#rpg-ipc-parallel-context)
+ [대기 증가의 가능한 원인](#rpg-ipc-parallel-causes)
+ [작업](#rpg-ipc-parallel-actions)

## 지원되는 엔진 버전
<a name="rpg-ipc-parallel-context-supported"></a>

이 대기 이벤트 정보는 모든 Aurora PostgreSQL 버전에서 지원됩니다.

## 컨텍스트
<a name="rpg-ipc-parallel-context"></a>

PostgreSQL에서 병렬 쿼리를 실행할 때는 단일 쿼리를 처리하기 위해 여러 프로세스가 함께 작동합니다. 쿼리가 병렬화에 적합한 것으로 확인되면 리더 프로세스는 `max_parallel_workers_per_gather` 파라미터 설정에 따라 하나 이상의 병렬 워커 프로세스와 조정합니다. 리더 프로세스는 워커들을 대상으로 작업을 나누고, 각 워커는 각자가 맡은 데이터를 독립적으로 처리하며, 결과는 리더 프로세스로 다시 수집됩니다.

**참고**  
각 병렬 워커는 전체 사용자 세션과 유사한 리소스 요구 사항이 있는 별도의 프로세스로 작동합니다. 즉, 워커가 4개인 병렬 쿼리는 비병렬 쿼리에 비해 리소스(CPU, 메모리, I/O 대역폭)를 최대 5배까지 소비할 수 있습니다. 리더 프로세스와 각 워커 프로세스 모두 자체 리소스 할당을 유지하기 때문입니다. 예를 들어, `work_mem`과 같은 설정은 각 워커에게 개별적으로 적용되어 모든 프로세스에서 총 메모리 사용량을 크게 증가시킬 수 있습니다.

병렬 쿼리 아키텍처는 세 가지 주요 구성 요소로 구성됩니다.
+ 리더 프로세스: 병렬 작업을 시작하고, 워크로드를 나누고, 워커 프로세스와 조정하는 주요 프로세스입니다.
+ 워커 프로세스: 쿼리의 일부를 병렬로 실행하는 백그라운드 프로세스입니다.
+ 수집/수집 병합: 여러 워커 프로세스의 결과를 리더에게 다시 결합하는 작업입니다.

병렬 실행 중에 프로세스는 프로세스 간 통신(IPC) 메커니즘을 통해 서로 통신해야 합니다. 이러한 IPC 대기 이벤트는 여러 단계에서 발생합니다.
+ 워커 시작: 병렬 워커가 초기화될 때
+ 데이터 교환: 워커가 데이터를 처리하고 리더에게 결과를 보낼 때
+ 워커 종료: 병렬 실행이 완료되고 워커가 종료될 때
+ 동기화 포인트: 프로세스가 다른 프로세스와 조정하거나 다른 프로세스가 작업을 완료하기를 기다려야 할 때

이러한 대기 이벤트를 이해하는 것은 병렬 쿼리 실행과 관련된 성능 문제를 진단하는 데 매우 중요합니다. 특히 여러 병렬 쿼리가 동시에 실행될 수 있는 동시성이 높은 환경에서는 더욱 그렇습니다.

## 대기 증가의 가능한 원인
<a name="rpg-ipc-parallel-causes"></a>

병렬 관련 IPC 대기 이벤트 증가에 여러 요인이 기여할 수 있습니다.

**병렬 쿼리의 높은 동시성**  
많은 병렬 쿼리가 동시에 실행되면 리소스 경합이 발생하고 IPC 작업 대기 시간이 늘어날 수 있습니다. 특히 트랜잭션 볼륨 또는 분석 워크로드가 많은 시스템에서 흔히 발생합니다.

**최적화되지 않은 병렬 쿼리 계획**  
쿼리 플래너가 비효율적인 병렬 계획을 선택하면 불필요한 병렬화 또는 워커 간의 작업 분산 부실이 발생할 수 있습니다. 이로 인해 특히 `IPC:ExecuteGather` 및 `IPC:ParallelFinish` 이벤트의 IPC 대기 시간이 늘어날 수 있습니다. 이러한 계획 문제는 오래된 통계 및 테이블/인덱스 팽창으로 인해 발생하는 경우가 많습니다.

**병렬 워커의 빈번한 시작 및 종료**  
병렬 워커를 자주 시작하고 종료하는 수명이 짧은 쿼리는 `IPC:BgWorkerStartup` 및 `IPC:BgWorkerShutdown` 이벤트를 증가시킬 수 있습니다. 이는 작고 병렬화 가능한 쿼리가 많은 OLTP 워크로드에서 흔히 볼 수 있습니다.

**리소스 제약 조건**  
CPU, 메모리 또는 I/O 용량이 제한되면 병렬 실행에서 병목 현상이 발생하여 모든 IPC 이벤트에서 대기 시간이 늘어날 수 있습니다. 예를 들어 CPU가 포화 상태인 경우 워커 프로세스를 시작하거나 할당된 작업을 처리하는 데 시간이 더 오래 걸릴 수 있습니다.

**복잡한 쿼리 구조**  
여러 수준의 병렬 처리(예: 병렬 조인 후 병렬 집계)가 있는 쿼리는 더 복잡한 IPC 패턴을 초래하고 특히 `IPC:ExecuteGather` 이벤트의 경우 대기 시간이 증가할 수 있습니다.

**대규모 결과 세트**  
대규모 결과 세트를 생성하는 쿼리는 리더 프로세스가 워커 프로세스로부터 결과를 수집하고 처리하는 데 더 많은 시간을 소비하므로 `IPC:ExecuteGather` 대기 시간이 늘어날 수 있습니다.

이러한 요인을 이해하면 Aurora PostgreSQL에서 병렬 쿼리 실행과 관련된 성능 문제를 진단하고 해결하는 데 도움이 될 수 있습니다.

## 작업
<a name="rpg-ipc-parallel-actions"></a>

병렬 쿼리와 관련하여 대기가 발생하면 일반적으로 백엔드 프로세스가 병렬 워커 프로세스를 조정하거나 대기 중임을 의미합니다. 이러한 대기는 병렬 계획을 실행하는 동안 흔히 발생합니다. 병렬 워커 사용량을 모니터링하고, 파라미터 설정을 검토하고, 쿼리 실행 및 리소스 할당을 조정하여 이러한 대기의 영향을 조사하고 완화할 수 있습니다.

**Topics**
+ [비효율적인 병렬 처리가 있는지 쿼리 계획 분석](#rpg-ipc-parallel-analyze-plans)
+ [병렬 쿼리 사용량 모니터링](#rpg-ipc-parallel-monitor)
+ [병렬 쿼리 설정 검토 및 조정](#rpg-ipc-parallel-adjust-settings)
+ [리소스 할당 최적화](#rpg-ipc-parallel-optimize-resources)
+ [연결 관리 조사](#rpg-ipc-parallel-connection-management)
+ [유지 관리 작업 검토 및 최적화](#rpg-ipc-parallel-maintenance)

### 비효율적인 병렬 처리가 있는지 쿼리 계획 분석
<a name="rpg-ipc-parallel-analyze-plans"></a>

병렬 쿼리 실행은 종종 시스템 불안정, CPU 급증 및 예측할 수 없는 쿼리 성능 변동으로 이어질 수 있습니다. 병렬 처리가 실제로 특정 워크로드를 개선하는지를 철저히 분석하는 것이 중요합니다. EXPLAIN ANALYZE를 사용하여 병렬 쿼리 실행 계획을 검토합니다.

세션 수준에서 병렬 처리를 일시적으로 비활성화하여 계획 효율성을 비교합니다.

```
SET max_parallel_workers_per_gather = 0;
EXPLAIN ANALYZE <your_query>;
```

병렬 처리를 다시 활성화하고 비교합니다.

```
RESET max_parallel_workers_per_gather;
EXPLAIN ANALYZE <your_query>;
```

병렬 처리를 비활성화했을 때 결과가 더 좋거나 더 일관된 경우 SET 명령을 사용하여 세션 수준에서 특정 쿼리에 대해 병렬 처리를 비활성화하는 것이 좋습니다. 더 광범위한 영향을 미치려면 DB 파라미터 그룹에서 관련 파라미터를 조정하여 인스턴스 수준에서 병렬 처리를 비활성화할 수 있습니다. 자세한 내용은 [Amazon RDS에서 DB 파라미터 그룹의 파라미터 수정](USER_WorkingWithParamGroups.Modifying.md) 섹션을 참조하세요.

### 병렬 쿼리 사용량 모니터링
<a name="rpg-ipc-parallel-monitor"></a>

다음 쿼리를 사용하여 병렬 쿼리 활동 및 용량에 대한 가시성을 확보합니다.

활성 병렬 워커 프로세스를 확인합니다.

```
SELECT
    COUNT(*)
FROM
    pg_stat_activity
WHERE
    backend_type = 'parallel worker';
```

이 쿼리는 활성 병렬 워커 프로세스의 수를 보여줍니다. 값이 크면 `max\$1parallel\$1workers`가 큰 값으로 구성되어 있음을 나타낼 수 있으며 이 값을 줄이는 것이 좋습니다.

동시 병렬 쿼리를 확인합니다.

```
SELECT
    COUNT(DISTINCT leader_pid)
FROM
    pg_stat_activity
WHERE
    leader_pid IS NOT NULL;
```

이 쿼리는 병렬 쿼리를 시작한 고유한 리더 프로세스의 수를 반환합니다. 여기서 숫자가 크면 여러 세션이 병렬 쿼리를 동시에 실행하고 있다는 뜻이며 CPU와 메모리에 대한 수요가 증가할 수 있습니다.

### 병렬 쿼리 설정 검토 및 조정
<a name="rpg-ipc-parallel-adjust-settings"></a>

다음 파라미터를 검토하여 워크로드에 적절한지 확인합니다.
+ `max_parallel_workers`: 모든 세션의 총 병렬 워커 수입니다.
+ `max_parallel_workers_per_gather`: 쿼리당 최대 워커 수입니다.

OLAP 워크로드의 경우 이러한 값을 늘리면 성능이 향상될 수 있습니다. OLTP 워크로드의 경우 일반적으로 낮은 값이 선호됩니다.

```
SHOW max_parallel_workers;
SHOW max_parallel_workers_per_gather;
```

### 리소스 할당 최적화
<a name="rpg-ipc-parallel-optimize-resources"></a>

CPU 사용률을 모니터링하고, 사용률이 지속적으로 높고 애플리케이션이 병렬 쿼리의 이점을 누릴 수 있는 경우 vCPU 수를 조정하는 것이 좋습니다. 병렬 작업에 적절한 메모리를 사용할 수 있는지 확인합니다.
+ 성능 개선 도우미 지표를 사용하여 시스템이 CPU 바운드인지 확인합니다.
+ 각 병렬 워커는 자체 `work_mem`을 사용합니다. 총 메모리 사용량이 인스턴스 한도 내에 있는지 확인합니다.

병렬 쿼리는 비병렬 쿼리보다 훨씬 많은 리소스를 사용할 수 있습니다. 각 워커 프로세스는 시스템에 추가 사용자 세션과 거의 동일한 영향을 미치는 완전히 별개의 프로세스이기 때문입니다. 이 설정에 대한 값을 선택할 때, 그리고 `work_mem`과 같이 리소스 사용률을 제어하는 다른 설정을 구성할 때 이 점을 고려해야 합니다. 자세한 내용은 [PostgreSQL 설명서](https://www.postgresql.org/docs/current/runtime-config-resource.html#GUC-WORK-MEM)를 참조하세요. `work_mem`과 같은 리소스 한도는 각 워커에 개별적으로 적용됩니다. 즉, 전체 사용률이 단일 프로세스보다 모든 프로세스에서 훨씬 높을 수 있습니다.

워크로드가 심하게 병렬화된 경우 vCPU를 늘리거나 메모리 파라미터를 조정하는 것이 좋습니다.

### 연결 관리 조사
<a name="rpg-ipc-parallel-connection-management"></a>

연결 소진이 발생하는 경우 애플리케이션 연결 풀링 전략을 검토합니다. 아직 사용하지 않는 경우 애플리케이션 수준에서 연결 풀링을 구현하는 것이 좋습니다.

### 유지 관리 작업 검토 및 최적화
<a name="rpg-ipc-parallel-maintenance"></a>

인덱스 생성 및 기타 유지 관리 작업을 조정하여 리소스 경합을 방지합니다. 이러한 작업은 사용량이 적은 시간으로 예약하는 것이 좋습니다. 사용자 쿼리 로드가 많은 기간에는 과도한 유지 관리(예: 병렬 인덱스 빌드)를 예약하지 마세요. 이러한 작업은 병렬 워커를 소비하고 정규 쿼리의 성능에 영향을 미칠 수 있습니다.

# IPC:ProcArrayGroupUpdate
<a name="apg-rpg-ipcprocarraygroup"></a>

`IPC:ProcArrayGroupUpdate` 이벤트는 세션이 해당 작업의 끝에 그룹 리더가 트랜잭션 상태를 업데이트할 때까지 기다릴 때 발생합니다. PostgreSQL은 일반적으로 IPC 유형 대기 이벤트를 병렬 쿼리 작업과 연결하지만, 이 특정 대기 이벤트는 병렬 쿼리에만 국한되지 않습니다.

**Topics**
+ [지원되는 엔진 버전](#apg-rpg-ipcprocarraygroup.supported)
+ [컨텍스트](#apg-rpg-ipcprocarraygroup.context)
+ [대기 증가의 가능한 원인](#apg-rpg-ipcprocarraygroup.causes)
+ [작업](#apg-rpg-ipcprocarraygroup.actions)

## 지원되는 엔진 버전
<a name="apg-rpg-ipcprocarraygroup.supported"></a>

이 대기 이벤트 정보는 모든 RDS for PostgreSQL 버전에서 지원됩니다.

## 컨텍스트
<a name="apg-rpg-ipcprocarraygroup.context"></a>

**프로세스 배열 이해** - 프로세스(proc) 배열은 PostgreSQL의 공유 메모리 구조입니다. 트랜잭션 세부 정보를 포함하여 실행 중인 모든 프로세스에 대한 정보가 들어 있습니다. 트랜잭션 완료(`COMMIT` 또는 `ROLLBACK`) 중에 ProcArray를 업데이트하여 변경 사항을 반영하고 어레이에서 transactionID를 지워야 합니다. 트랜잭션을 완료하려는 세션은 ProcArray에서 독점적 잠금을 획득해야 합니다. 이렇게 하면 다른 프로세스가 공유되거나 독점적인 잠금을 얻지 못합니다.

**그룹 업데이트 메커니즘** - COMMIT 또는 ROLLBACK을 수행하는 동안 백엔드 프로세스가 독점 모드에서 ProcArrayLock을 가져올 수 없는 경우, ProcArrayGroupMember라는 특수 필드를 업데이트합니다. 그러면 종료하려는 세션 목록에 트랜잭션이 추가됩니다. 그러면이 백엔드 프로세스가 절전 모드로 전환되고, 절전 모드로 전환되는 시간이 ProcArrayGroupUpdate 대기 이벤트로 계측됩니다. 리더 프로세스라고 하는 ProcArrayprocArrayGroupMember의 첫 번째 프로세스는 ProcArrayLock을 독점 모드로 획득합니다. 그런 다음 그룹 transactionID 지우기를 기다리는 프로세스 목록을 지웁니다. 이 작업이 완료되면 리더는 ProcArrayLock을 해제한 다음, 이 목록의 모든 프로세스를 깨워 트랜잭션이 완료되었음을 알립니다.

## 대기 증가의 가능한 원인
<a name="apg-rpg-ipcprocarraygroup.causes"></a>

실행 중인 프로세스가 많을수록 리더가 독점 모드에서 procArrayLock을 더 오래 유지합니다. 따라서 쓰기 트랜잭션이 많을수록 그룹 업데이트 시나리오로 인해 `ProcArrayGroupUpdate` 대기 이벤트를 기다리는 프로세스가 잠재적으로 쌓일 수 있습니다. Database Insights의 상위 SQL 보기에서 COMMIT이이 대기 이벤트의 대부분을 포함하는 문임을 알 수 있습니다. 이는 예상되지만 실행 중인 특정 쓰기 SQL에 대한 심층 조사를 통해 취해야 할 적절한 조치를 결정해야 합니다.

## 작업
<a name="apg-rpg-ipcprocarraygroup.actions"></a>

대기 이벤트의 원인에 따라 다른 작업을 권장합니다. Amazon RDS Performance Insights를 사용하거나 PostgreSQL 시스템 보기 `pg_stat_activity`를 쿼리하여 `IPC:ProcArrayGroupUpdate` 이벤트를 식별합니다.

**Topics**
+ [트랜잭션 커밋 및 롤백 작업 모니터링](#apg-rpg-ipcprocarraygroup.actions.monitor)
+ [동시성 감소](#apg-rpg-ipcprocarraygroup.actions.concurrency)
+ [연결 풀링 구현](#apg-rpg-ipcprocarraygroup.actions.pooling)
+ [더 빠른 스토리지 사용](#apg-rpg-ipcprocarraygroup.actions.storage)

### 트랜잭션 커밋 및 롤백 작업 모니터링
<a name="apg-rpg-ipcprocarraygroup.actions.monitor"></a>

**커밋 및 롤백 모니터링** - 커밋 및 롤백 수가 증가하면 ProcArray에 대한 부담이 증가할 수 있습니다. 예를 들어 중복 키 위반 증가로 인해 SQL 문이 실패하기 시작하면 롤백이 증가하여 ProcArray 경합과 테이블 팽창이 증가할 수 있습니다.

Amazon RDS Database Insights는 PostgreSQL 지표 `xact_commit` 및 `xact_rollback`을 제공하여 초당 커밋 및 롤백 수를 보고합니다.

### 동시성 감소
<a name="apg-rpg-ipcprocarraygroup.actions.concurrency"></a>

**트랜잭션 일괄 처리** - 가능한 경우 커밋/롤백 작업을 줄이기 위해 단일 트랜잭션에서 작업을 일괄 처리합니다.

**동시성 제한** - 동시 활성 트랜잭션 수를 줄여 ProcArray에서 잠금 경합을 완화합니다. 약간의 테스트가 필요하지만 총 동시 연결 수를 줄이면 경합을 줄이고 처리량을 유지할 수 있습니다.

### 연결 풀링 구현
<a name="apg-rpg-ipcprocarraygroup.actions.pooling"></a>

**연결 풀링 솔루션** - 연결 풀링을 사용하여 데이터베이스 연결을 효율적으로 관리함으로써 ProcArray의 총 백엔드 수와 그에 따른 워크로드를 줄입니다. 약간의 테스트가 필요하지만 총 동시 연결 수를 줄이면 경합을 줄이고 처리량을 유지할 수 있습니다.

**연결 폭주 감소** - 마찬가지로 연결을 자주 생성하고 종료하는 패턴으로 인해 ProcArray에 추가 압력이 발생합니다. 이 패턴을 줄이면 전반적인 경합이 줄어듭니다.

### 더 빠른 스토리지 사용
<a name="apg-rpg-ipcprocarraygroup.actions.storage"></a>

**전용 로그 볼륨** - `IPC:ProcArrayGroupUpdate` 대기 이벤트에 높은 `IO:WALWrite` 대기 이벤트가 수반되는 경우, 전용 로그 볼륨을 설정하면 WAL에 대한 병목 현상 쓰기를 줄일 수 있습니다. 그러면 커밋 성능이 향상됩니다.

자세한 내용은 [전용 로그 볼륨](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_PIOPS.dlv.html)을 참조하세요.

# Lock:advisory
<a name="wait-event.lockadvisory"></a>

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

**Topics**
+ [관련 엔진 버전](#wait-event.lockadvisory.context.supported)
+ [컨텍스트](#wait-event.lockadvisory.context)
+ [원인](#wait-event.lockadvisory.causes)
+ [작업](#wait-event.lockadvisory.actions)

## 관련 엔진 버전
<a name="wait-event.lockadvisory.context.supported"></a>

이 대기 이벤트 정보는 RDS for PostgreSQL 버전 9.6 이상과 관련이 있습니다.

## 컨텍스트
<a name="wait-event.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="wait-event.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="wait-event.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` 인수를 의미합니다.

# Lock:extend
<a name="wait-event.lockextend"></a>

`Lock:extend` 이벤트는 백엔드 프로세스가 릴레이션을 확장하기 위해 릴레이션을 잠그기를 기다리는 동안 다른 프로세스에서 동일한 목적으로 해당 릴레이션에 대한 잠금이 있는 경우에 발생합니다.

**Topics**
+ [지원되는 엔진 버전](#wait-event.lockextend.context.supported)
+ [컨텍스트](#wait-event.lockextend.context)
+ [대기 증가의 가능한 원인](#wait-event.lockextend.causes)
+ [작업](#wait-event.lockextend.actions)

## 지원되는 엔진 버전
<a name="wait-event.lockextend.context.supported"></a>

이 대기 이벤트 정보는 모든 RDS for PostgreSQL 버전에서 지원됩니다.

## 컨텍스트
<a name="wait-event.lockextend.context"></a>

이벤트 `Lock:extend`는 백엔드 프로세스가 관계를 확장하는 동안 다른 백엔드 프로세스가 잠금을 유지하는 관계를 확장하기 위해 대기 중임을 나타냅니다. 한 번에 하나의 프로세스만 관계를 확장할 수 있기 때문에 시스템은 `Lock:extend` 대기 이벤트를 생성합니다. `INSERT`,`COPY`, 및 `UPDATE` 연산은 이 이벤트를 생성할 수 있습니다.

## 대기 증가의 가능한 원인
<a name="wait-event.lockextend.causes"></a>

`Lock:extend` 이벤트가 정상보다 많이 나타나 성능 문제를 나타내는 경우 일반적인 원인은 다음과 같습니다.

**동일한 테이블에 대한 동시 삽입 또는 업데이트 서지 **  
동일한 테이블에 삽입하거나 업데이트하는 쿼리의 동시 세션 수가 증가할 수 있습니다.

**네트워크 대역폭 부족**  
DB 인스턴스의 네트워크 대역폭이 현재 워크로드의 스토리지 통신 요구 사항에 따라 충분하지 않을 수 있습니다. 이로 인해 `Lock:extend` 이벤트에서 스토리지 지연 시간이 증가할 수 있습니다.

## 작업
<a name="wait-event.lockextend.actions"></a>

대기 이벤트의 원인에 따라 다른 작업을 권장합니다.

**Topics**
+ [동일한 관계식으로 동시 삽입 및 업데이트 감소](#wait-event.lockextend.actions.action1)
+ [네트워크 대역폭 향상](#wait-event.lockextend.actions.increase-network-bandwidth)

### 동일한 관계식으로 동시 삽입 및 업데이트 감소
<a name="wait-event.lockextend.actions.action1"></a>

먼저, 증가가 있는지 확인합니다. `tup_inserted`와 `tup_updated` 지표 및 이 대기 이벤트의 증가가 수반됩니다. 그렇다면 삽입 및 업데이트 작업에 대한 경합이 높은 관계식을 확인합니다. 이를 확인하려면 `n_tup_ins` 및 `n_tup_upd` 필드의 값을 위한 `pg_stat_all_tables` 뷰를 쿼리하세요. `pg_stat_all_tables` 뷰에 대한 자세한 내용은 PostgreSQL 설명서의 [pg\$1stat\$1statements](https://www.postgresql.org/docs/13/monitoring-stats.html#MONITORING-PG-STAT-ALL-TABLES-VIEW)를 참조하세요.

차단 및 차단된 쿼리에 대한 자세한 정보를 위해서 다음 예와 같이 `pg_stat_activity`를 쿼리하세요.

```
SELECT
    blocked.pid,
    blocked.usename,
    blocked.query,
    blocking.pid AS blocking_id,
    blocking.query AS blocking_query,
    blocking.wait_event AS blocking_wait_event,
    blocking.wait_event_type AS blocking_wait_event_type
FROM pg_stat_activity AS blocked
JOIN pg_stat_activity AS blocking ON blocking.pid = ANY(pg_blocking_pids(blocked.pid))
where
blocked.wait_event = 'extend'
and blocked.wait_event_type = 'Lock';
 
   pid  | usename  |            query             | blocking_id |                         blocking_query                           | blocking_wait_event | blocking_wait_event_type
  ------+----------+------------------------------+-------------+------------------------------------------------------------------+---------------------+--------------------------
   7143 |  myuser  | insert into tab1 values (1); |        4600 | INSERT INTO tab1 (a) SELECT s FROM generate_series(1,1000000) s; | DataFileExtend      | IO
```

`Lock:extend` 이벤트 증가에 기여하는 관계를 파악한 후 다음 기슬을 사용하여 경합을 줄입니다.
+ 파티셔닝을 사용하여 동일한 테이블에 대한 경합을 줄일 수 있는지 확인합니다. 삽입되거나 업데이트된 튜플을 다른 파티션으로 분리하면 경합을 줄일 수 있습니다. 파티셔닝에 대한 자세한 내용은 [pg\$1partman 확장자를 사용하여 PostgreSQL 파티션 관리하기](PostgreSQL_Partitions.md) 섹션을 참조하세요.
+ 대기 이벤트가 주로 업데이트 활동으로 인한 경우 관계식의 채우기 요소 값을 줄이는 것이 좋습니다. 이렇게 하면 업데이트 중에 새 블록에 대한 요청을 줄일 수 있습니다. 필팩터는 테이블 페이지를 포장하기 위한 최대 공간을 결정하는 테이블의 저장 파라미터입니다. 이 값은 페이지의 전체 공간의 백분율로 표시됩니다. 필팩터 파라미터에 대한 자세한 내용은 PostgreSQL 설명서의 [테이블 생성](https://www.postgresql.org/docs/13/sql-createtable.html)을 참조하세요.
**중요**  
필팩터를 변경하는 경우 이 값을 변경하면 워크로드에 따라 성능에 부정적인 영향을 줄 수 있으므로 시스템을 테스트하는 것이 좋습니다.

### 네트워크 대역폭 향상
<a name="wait-event.lockextend.actions.increase-network-bandwidth"></a>

쓰기 지연 시간이 증가하는지 확인하려면 CloudWatch의 `WriteLatency` 지표를 확인하세요. 그렇다면, `WriteThroughput` 및 `ReadThroughput` Amazon CloudWatch 지표를 사용하여 DB 인스턴스의 스토리지 관련 트래픽을 모니터링하세요. 이러한 지표는 네트워크 대역폭이 워크로드의 스토리지 활동에 충분한지 확인하는 데 도움이 될 수 있습니다.

네트워크 대역폭이 충분하지 않으면 늘리세요. 만약 클라이언트 또는 DB 인스턴스가 네트워크 대역폭 제한에 도달했다면. 대역폭을 늘리는 유일한 방법은 DB 인스턴스 크기를 늘리는 것입니다.

CloudWatch 지표에 대한 자세한 내용은 [Amazon RDS에 대한 Amazon CloudWatch 지표](rds-metrics.md#rds-cw-metrics-instance) 섹션을 참조하세요. 각 DB 인스턴스 클래스에 대한 네트워크 성능에 관한 자세한 내용은 [Amazon RDS에 대한 Amazon CloudWatch 지표](rds-metrics.md#rds-cw-metrics-instance) 섹션을 참조하세요.

# Lock:Relation
<a name="wait-event.lockrelation"></a>

`Lock:Relation` 이벤트는 쿼리가 현재 다른 트랜잭션에 의해 잠긴 테이블 또는 뷰(관계식)에 대한 잠금을 얻기 위해 대기 중일 때 발생합니다.

**Topics**
+ [지원되는 엔진 버전](#wait-event.lockrelation.context.supported)
+ [컨텍스트](#wait-event.lockrelation.context)
+ [대기 증가의 가능한 원인](#wait-event.lockrelation.causes)
+ [작업](#wait-event.lockrelation.actions)

## 지원되는 엔진 버전
<a name="wait-event.lockrelation.context.supported"></a>

이 대기 이벤트 정보는 모든 RDS for PostgreSQL 버전에서 지원됩니다.

## 컨텍스트
<a name="wait-event.lockrelation.context"></a>

대부분의 PostgreSQL 명령은 암시적으로 잠금을 사용하여 테이블의 데이터에 대한 동시 액세스를 제어합니다. 애플리리케이션 코드에서 `LOCK` 명령과 함께 이러한 잠금을 명시적으로 사용할 수도 있습니다. 많은 잠금 모드는 서로 호환되지 않으며 동일한 객체에 액세스하려고 할 때 트랜잭션을 차단할 수 있습니다. 이 경우 RDS for PostgreSQL은 `Lock:Relation` 이벤트를 생성합니다. 다음은 몇 가지 일반적인 예입니다.
+ `ACCESS EXCLUSIVE`와 같은 독점 잠금은 모든 동시 액세스를 차단할 수 있습니다. 데이터 정의 언어(DDL) 작업(예: `DROP TABLE`,`TRUNCATE`,`VACUUM FULL`, 및`CLUSTER`)은 명시적으로 `ACCESS EXCLUSIVE` 잠금을 획득합니다. 또한 `ACCESS EXCLUSIVE`는 모드를 명시적으로 식별하지 않는 `LOCK TABLE` 문을 위한 기본 잠금 모드입니다.
+ 데이터 조작 언어(DML) 명령문 `UPDATE`,`DELETE`, 및 `INSERT`와 충돌하는 테이블에 `ROW EXCLUSIVE` 잠금을 획득하는 `CREATE INDEX (without CONCURRENT)`를 사용하세요.

테이블 수준 잠금 및 충돌하는 잠금 모드에 대한 자세한 내용은 PostgreSQL 설명서의 [명시적 잠금](https://www.postgresql.org/docs/13/explicit-locking.html)을 참조하세요.

쿼리 및 트랜잭션 차단은 일반적으로 다음 중 한 가지 방법으로 잠금 해제합니다.
+ 쿼리 차단 - 애플리케이션이 쿼리를 취소하거나 사용자가 프로세스를 종료할 수 있습니다. 세션의 명령문 시간 초과 또는 교착 상태 감지 메커니즘으로 인해 엔진이 쿼리를 강제로 종료할 수도 있습니다.
+ 트랜잭션 차단 - 트랜잭션이 `ROLLBACK`이나 `COMMIT` 문을 실행할 때 차단을 중지합니다. 롤백은 클라이언트 또는 네트워크 문제로 세션의 연결이 끊어지거나 종료된 경우에도 자동으로 수행됩니다. 세션은 데이터베이스 엔진이 종료될 때, 시스템의 메모리가 부족할 때 종료될 수 있습니다.

## 대기 증가의 가능한 원인
<a name="wait-event.lockrelation.causes"></a>

`Lock:Relation` 이벤트가 평소보다 더 자주 발생하면 성능 문제를 나타낼 수 있습니다. 일반적인 원인은 다음과 같습니다.

**테이블 잠금 충돌로 인한 동시 세션 증가**  
잠금 모드가 충돌하는 동일한 테이블을 하거나 잠그는 쿼리의 동시 세션 수가 증가할 수 있습니다.

**유지 관리 작업**  
`VACUUM`과 `ANALYZE` 같은 상태 유지 관리 작업은 충돌하는 잠금 수를 크게 늘릴 수 있습니다. `VACUUM FULL`은 하나의 `ACCESS EXCLUSIVE` 잠금을, `ANALYSE`는 하나의 `SHARE UPDATE EXCLUSIVE` 잠금을 획득합니다. 두 가지 유형의 잠금은 모두 `Lock:Relation` 대기 이벤트를 발생시킬 수 있습니다. 구체화된 뷰 새로 고침과 같은 애플리케이션 데이터 유지 관리 작업은 차단된 질의 및 트랜잭션을 증가시킬 수도 있습니다.

**리더 인스턴스 잠금**  
라이터와 리더가 보유한 관계 잠금 간에 충돌이 있을 수 있습니다. 현재 `ACCESS EXCLUSIVE` 관계 잠금만 리더 인스턴스에 복제됩니다. 그러나 `ACCESS EXCLUSIVE` 관계 잠금은 리더가 보유한 모든 `ACCESS SHARE` 관계 잠금과 충돌합니다. 이로 인해 리더에서 잠금 관계 대기 이벤트가 증가할 수 있습니다.

## 작업
<a name="wait-event.lockrelation.actions"></a>

대기 이벤트의 원인에 따라 다른 작업을 권장합니다.

**Topics**
+ [SQL 문 차단의 영향 감소](#wait-event.lockrelation.actions.reduce-blocks)
+ [유지 보수 작업의 영향 최소화](#wait-event.lockrelation.actions.maintenance)

### SQL 문 차단의 영향 감소
<a name="wait-event.lockrelation.actions.reduce-blocks"></a>

SQL 문 차단의 영향을 줄이려면 가능한 경우 애플리케이션 코드를 수정하세요. 다음은 블록을 줄이기 위한 두 가지 일반적인 기술입니다.
+ `NOWAIT` 옵션 사용 - `SELECT`나 `LOCK` 문 같은 일부 SQL 명령은 이 옵션을 지원합니다. `NOWAIT` 지시어는 잠금을 즉시 획득할 수 없는 경우 잠금 요청 쿼리를 취소합니다. 이 기술은 차단 세션이 그 뒤에 차단된 세션이 쌓이지 않게 하는 데 도움이 될 수 있습니다.

  예: 트랜잭션 A가 트랜잭션 B가 보유한 잠금을 기다리고 있다고 가정합니다. 이제 B가 트랜잭션 C에 의해 잠긴 테이블에 대한 잠금을 요청하면 트랜잭션 C가 완료될 때까지 트랜잭션 A가 차단될 수 있습니다. 그러나 트랜잭션 B가 `NOWAIT`를 사용하는 경우 C에서 잠금을 요청하면 빠르게 실패하고 트랜잭션 A가 계속해서 기다릴 필요가 없도록 할 수 있습니다.
+ `SET lock_timeout` 사용 - SQL 문이 관계식 잠금을 획득하기 위해 대기하는 시간을 제한하는 `lock_timeout` 값을 설정하세요. 지정된 제한 시간 내에 잠금을 획득하지 않으면 잠금을 요청하는 트랜잭션이 취소됩니다. 세션 수준에서 이 값을 설정합니다.

### 유지 보수 작업의 영향 최소화
<a name="wait-event.lockrelation.actions.maintenance"></a>

`VACUUM`과 `ANALYZE` 같은 유지 관리 작업이 중요합니다. 이러한 유지 관리 작업과 관련된 `Lock:Relation` 대기 이벤트를 찾을 수 있으므로 끄지 않는 것이 좋습니다. 다음 방법을 사용하면 이러한 작업의 효과를 최소화할 수 있습니다.
+ 사용량이 적은 시간대에 수동으로 유지 관리 작업을 실행합니다.
+ Autovacuum 작업으로 인한 `Lock:Relation` 대기를 줄이려면 필요한 autovacuum 튜닝을 수행하세요. Autovacuum 튜닝에 대한 자세한 내용은 [Amazon RDS 사용 설명서](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Appendix.PostgreSQL.CommonDBATasks.Autovacuum.html)의 *Amazon RDS에서 PostgreSQL Autovacuum 사용*을 참조하세요.

# Lock:transactionid
<a name="wait-event.locktransactionid"></a>

`Lock:transactionid` 이벤트는 트랜잭션이 행 수준 잠금을 대기 중일 때 발생합니다.

**Topics**
+ [지원되는 엔진 버전](#wait-event.locktransactionid.context.supported)
+ [컨텍스트](#wait-event.locktransactionid.context)
+ [대기 증가의 가능한 원인](#wait-event.locktransactionid.causes)
+ [작업](#wait-event.locktransactionid.actions)

## 지원되는 엔진 버전
<a name="wait-event.locktransactionid.context.supported"></a>

이 대기 이벤트 정보는 모든 RDS for PostgreSQL 버전에서 지원됩니다.

## 컨텍스트
<a name="wait-event.locktransactionid.context"></a>

이벤트 `Lock:transactionid` 트랜잭션이 동시에 실행 중인 트랜잭션에 이미 부여된 행 수준 잠금을 획득하려고 할 때 발생합니다. `Lock:transactionid` 대기 이벤트를 보여주는 세션이 이 잠금으로 인해 차단되었습니다. 차단 트랜잭션이 종료된 후 `COMMIT` 또는`ROLLBACK` 문, 차단된 트랜잭션을 진행할 수 있습니다.

RDS for PostgreSQL의 다중 버전 동시성 제어 의미 체계는 리더가 라이터를 차단하지 않고 라이터는 리더를 차단하지 않도록 보장합니다. 행 수준 충돌이 발생하려면 차단 및 차단된 트랜잭션이 다음 유형의 충돌하는 명령문을 실행해야 합니다.
+ `UPDATE`
+ `SELECT … FOR UPDATE`
+ `SELECT … FOR KEY SHARE`

명령문 `SELECT … FOR KEY SHARE`는 특별한 경우입니다. 데이터베이스가 `FOR KEY SHARE` 절을 사용해 참조 무결성의 성능을 최적화합니다. 행의 행 수준 잠금은 행을 참조하는 다른 테이블에서 `INSERT`, `UPDATE`, `DELETE` 명령을 차단할 수 있습니다.

## 대기 증가의 가능한 원인
<a name="wait-event.locktransactionid.causes"></a>

이 이벤트가 정상보다 많이 나타나는 경우 일반적으로 원인은 다음 조건과 결합된 `UPDATE`, `SELECT … FOR UPDATE`, 또는 `SELECT … FOR KEY SHARE` 문입니다.

**Topics**
+ [높은 동시성](#wait-event.locktransactionid.concurrency)
+ [트랜잭션의 유휴 상태](#wait-event.locktransactionid.idle)
+ [장기 실행 트랜잭션](#wait-event.locktransactionid.long-running)

### 높은 동시성
<a name="wait-event.locktransactionid.concurrency"></a>

RDS for PostgreSQL은 세분화된 행 수준 잠금 의미 체계를 사용할 수 있습니다. 다음 조건이 충족되면 행 수준 충돌 확률이 높아집니다.
+ 동일한 행에 대해 높은 동시 워크로드가 경합됩니다.
+ 동시성이 증가합니다.

### 트랜잭션의 유휴 상태
<a name="wait-event.locktransactionid.idle"></a>

가끔 `pg_stat_activity.state` 열에 `idle in transaction` 값이 표시됩니다. 이 값은 트랜잭션을 시작했지만 아직 `COMMIT` 또는 `ROLLBACK`을 실행하지 않은 세션에 대해 표시됩니다. 만약 `pg_stat_activity.state` 값이 `active`가 아니라면, `pg_stat_activity`에 표시된 쿼리는 실행을 마친 가장 최근의 쿼리입니다. 차단 세션은 열려 있는 트랜잭션이 잠금을 유지하고 있기 때문에 쿼리를 능동적으로 처리하지 않습니다.

유휴 트랜잭션이 행 수준 잠금을 획득한 경우 다른 세션에서 잠금을 획득하지 못할 수 있습니다. 이 조건으로 인해 `Lock:transactionid` 대기 이벤트가 자주 발생합니다. 문제를 진단하려면 `pg_stat_activity`와 `pg_locks`의 출력을 검사하세요.

### 장기 실행 트랜잭션
<a name="wait-event.locktransactionid.long-running"></a>

오랫동안 실행되는 트랜잭션은 오랜 시간 동안 잠금을 얻습니다. 이러한 장기 잠금은 다른 트랜잭션의 실행을 차단할 수 있습니다.

## 작업
<a name="wait-event.locktransactionid.actions"></a>

행 잠금은 `UPDATE`, `SELECT … FOR UPDATE`, 또는 `SELECT … FOR KEY SHARE` 문 사이의 충돌입니다. 솔루션을 시도하기 전에 이러한 명령문이 동일한 행에서 실행되는 시점을 확인하세요. 다음 섹션에 설명된 전략을 선택하려면 이 정보를 사용하세요.

**Topics**
+ [동시성에 대응](#wait-event.locktransactionid.actions.problem)
+ [유휴 트랜잭션에 대응](#wait-event.locktransactionid.actions.find-blocker)
+ [장기 실행 트랜잭션에 대응](#wait-event.locktransactionid.actions.concurrency)

### 동시성에 대응
<a name="wait-event.locktransactionid.actions.problem"></a>

동시성이 문제가 되는 경우 다음 기술 중 하나를 시도해 보세요.
+ 애플리케이션의 동시성을 낮춥니다. 예를 들어 활성 세션 수를 줄일 수 있습니다.
+ 연결 풀을 구현합니다. RDS 프록시로 연결을 풀링하는 방법에 대한 자세한 내용은 [ Amazon RDS Proxy](rds-proxy.md) 섹션을 참조하세요.
+ `UPDATE` 문과 `SELECT … FOR UPDATE` 문의 경쟁 방지를 위한 애플리케이션 또는 데이터 모델을 설계하세요. `SELECT … FOR KEY SHARE` 문으로 액세스하는 외래 키 수를 줄일 수도 있습니다.

### 유휴 트랜잭션에 대응
<a name="wait-event.locktransactionid.actions.find-blocker"></a>

`pg_stat_activity.state`가 `idle in transaction`을 나타낸다면, 다음 전략을 시도해 보세요.
+ 가능하면 자동 커밋을 켭니다. 이 접근 방식은 트랜잭션이 `COMMIT`이나 `ROLLBACK`을 대기하는 동안 다른 트랜잭션을 차단하는 것을 방지합니다.
+ `COMMIT`, `ROLLBACK`, 또는 `END`가 누락된 코드 경로 검색
+ 애플리케이션의 예외 처리 논리에 항상 유효한 `end of transaction`으로 향하는 경로가 있는지 확인하세요.
+ `COMMIT` 및 `ROLLBACK`과의 트랜잭션을 종료한 후 애플리케이션이 쿼리 결과를 처리하는지 확인하세요.

### 장기 실행 트랜잭션에 대응
<a name="wait-event.locktransactionid.actions.concurrency"></a>

장기 실행 트랜잭션으로 인해 `Lock:transactionid`가 자주 발생하는 경우 다음 전략을 시도해 보세요.
+ 장기 실행 트랜잭션에서 행 잠금을 차단합니다.
+ 가능하면 자동 커밋을 구현하여 쿼리 길이를 제한합니다.

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

`Lock:tuple` 이벤트는 백엔드 프로세스가 튜플에 대한 잠금 획득을 대기 중일 때 발생합니다.

**Topics**
+ [지원되는 엔진 버전](#wait-event.locktuple.context.supported)
+ [컨텍스트](#wait-event.locktuple.context)
+ [대기 증가의 가능한 원인](#wait-event.locktuple.causes)
+ [작업](#wait-event.locktuple.actions)

## 지원되는 엔진 버전
<a name="wait-event.locktuple.context.supported"></a>

이 대기 이벤트 정보는 모든 RDS for PostgreSQL 버전에서 지원됩니다.

## 컨텍스트
<a name="wait-event.locktuple.context"></a>

이벤트 `Lock:tuple`은 다른 백엔드가 동일한 튜플에서 충돌하는 잠금을 보유하는 동안 백엔드가 튜플에 대한 잠금을 얻기 위해 대기 중임을 나타냅니다. 다음 테이블에서는 세션이 `Lock:tuple` 이벤트를 생성하는 시나리오를 가정합니다.


|  Time  |  세션 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` 문을 실행하고 있습니다.
+ 다양한 요인으로 인해 애플리케이션이나 연결 풀이 더 많은 세션을 열어 동일한 작업을 실행할 수 있습니다. 새 세션이 동일한 행을 수정하려고 하면 DB 로드가 급증할 수 있으며, `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)을 참조하세요.
+ autocommit을 최대한 많이 사용합니다. 자세한 내용은 PostgreSQL 설명서의 [AUTOCOMMIT 설정](https://www.postgresql.org/docs/current/ecpg-sql-set-autocommit.html)을 참조하세요.

### 차단 세션 찾기
<a name="wait-event.locktuple.actions.find-blocker"></a>

`Lock:tuple` 대기 이벤트가 발생하는 동안 어떤 잠금이 서로 의존하는지 파악하여 차단 및 차단된 세션을 식별합니다. 자세한 내용은 PostgreSQL 위키의 [잠금 종속성 정보](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 대역폭의 최대 사용량과 같은 병목 현상이 발생시킬 수 있습니다. 병목 현상을 줄이려면 다음 방법을 고려하세요.
+ 인스턴스 클래스 유형을 확장하세요.
+ 리소스 집약적인 쿼리를 최적화하세요.
+ 애플리케이션 로직을 변경하세요.
+ 거의 액세스하지 않는 데이터를 아카이브하세요.

# LWLock:BufferMapping (LWLock:buffer\$1mapping)
<a name="wait-event.lwl-buffer-mapping"></a>

이 이벤트는 세션이 데이터 블록을 공유 버퍼 풀의 버퍼와 연결하기 위해 대기 중일 때 발생합니다.

**참고**  
RDS for PostgreSQL 버전 13 이상에서 이 이벤트의 이름은 `LWLock:BufferMapping`입니다. RDS for PostgreSQL 버전 12 이전에서 이 이벤트의 이름은 `LWLock:buffer_mapping`입니다.

**Topics**
+ [지원되는 엔진 버전](#wait-event.lwl-buffer-mapping.context.supported)
+ [컨텍스트](#wait-event.lwl-buffer-mapping.context)
+ [원인](#wait-event.lwl-buffer-mapping.causes)
+ [작업](#wait-event.lwl-buffer-mapping.actions)

## 지원되는 엔진 버전
<a name="wait-event.lwl-buffer-mapping.context.supported"></a>

이 대기 이벤트 정보는 RDS for PostgreSQL 버전 9.6 이상과 관련이 있습니다.

## 컨텍스트
<a name="wait-event.lwl-buffer-mapping.context"></a>

*공유 버퍼 풀*은 프로세스에서 사용 중이거나 사용되었던 모든 페이지를 보관하는 PostgreSQL 메모리 영역입니다. 프로세스에 페이지가 필요한 경우 페이지를 공유 버퍼 풀로 읽습니다. `shared_buffers` 파라미터는 공유 버퍼 크기를 설정하고 테이블 및 인덱스 페이지를 저장할 메모리 영역을 예약합니다. 이 파라미터를 변경하는 경우 데이터베이스를 다시 시작해야 합니다.

`LWLock:buffer_mapping` 대기 이벤트는 다음 시나리오에서 발생합니다.
+ 프로세스가 버퍼 테이블에서 페이지를 검색하고 공유 버퍼 매핑 잠금을 획득합니다.
+ 프로세스가 페이지를 버퍼 풀로 로드하고 배타적 버퍼 매핑 잠금을 획득합니다.
+ 프로세스가 페이지를 버퍼 풀로 로드하고 배타적 버퍼 매핑 잠금을 획득합니다.

## 원인
<a name="wait-event.lwl-buffer-mapping.causes"></a>

이 이벤트가 정상보다 많이 발생하여 성능 문제가 발생할 수 있는 경우 데이터베이스가 공유 버퍼 풀에 페이징 및 페이징 중입니다. 일반적인 원인은 다음과 같습니다.
+ 대규모 쿼리
+ 부풀린 인덱스 및 테이블
+ 전체 테이블 스캔
+ 작업 세트보다 작은 공유 풀 크기

## 작업
<a name="wait-event.lwl-buffer-mapping.actions"></a>

대기 이벤트의 원인에 따라 다른 작업을 권장합니다.

**Topics**
+ [버퍼 관련 지표 모니터링](#wait-event.lwl-buffer-mapping.actions.monitor-metrics)
+ [인덱싱 전략 평가](#wait-event.lwl-buffer-mapping.actions.indexes)
+ [신속하게 할당해야 하는 버퍼 수 저감](#wait-event.lwl-buffer-mapping.actions.buffers)

### 버퍼 관련 지표 모니터링
<a name="wait-event.lwl-buffer-mapping.actions.monitor-metrics"></a>

`LWLock:buffer_mapping`이 스파이크를 기다렸다가 버퍼 적중률을 조사합니다. 이러한 지표를 사용하여 버퍼 캐시에서 발생하는 상황을 더 잘 이해할 수 있습니다. 다음 지표를 검토합니다.

`blks_hit`  
이 성능 개선 도우미 카운터 지표는 공유 버퍼 풀에서 검색된 블록 수를 나타냅니다. `LWLock:buffer_mapping` 대기 이벤트가 발생하면 `blks_hit`에서 스파이크가 발생할 수 있습니다.

`blks_read`  
이 성능 개선 도우미 카운터 지표는 공유 버퍼 풀에서 I/O를 읽어야 하는 블록 수를 나타냅니다. `LWLock:buffer_mapping` 대기 이벤트의 리드업에서 `blks_read`의 스파이크가 발생할 수 있습니다.

### 인덱싱 전략 평가
<a name="wait-event.lwl-buffer-mapping.actions.indexes"></a>

인덱싱 전략의 성능 저하가 아닌지 확인하려면 다음을 알아보세요.

인덱스 팽창  
인덱스와 테이블 팽창으로 인해 불필요한 페이지가 공유 버퍼로 읽히지 않는지 확인합니다. 테이블에 사용되지 않는 행이 포함된 경우 데이터를 보관하고 테이블에서 행을 제거하는 것이 좋습니다. 그런 다음 크기가 조정된 테이블의 인덱스를 다시 작성할 수 있습니다.

자주 사용하는 쿼리의 인덱스  
최적의 인덱스가 있는지 확인하려면 성능 개선 도우미에서 DB 엔진 지표를 모니터링하세요. `tup_returned` 지표는 읽은 행의 수를 보여줍니다. `tup_fetched` 지표는 클라이언트에게 반환되는 행 수를 보여줍니다 `tup_returned`가 `tup_fetched`보다 훨씬 큰 경우, 데이터가 제대로 인덱스화되지 않을 수 있습니다. 또한 테이블 통계가 최신 상태가 아닐 수도 있습니다.

### 신속하게 할당해야 하는 버퍼 수 저감
<a name="wait-event.lwl-buffer-mapping.actions.buffers"></a>

`LWLock:buffer_mapping` 대기 이벤트를 줄이려면, 신속하게 할당해야 하는 버퍼 수를 줄이세요. 한 가지 전략은 소규모 배치 작업을 수행하는 것입니다. 테이블을 분할하여 더 작은 배치를 달성할 수 있습니다.

# LWLock:BufferIO(IPC:BufferIO)
<a name="wait-event.lwlockbufferio"></a>

`LWLock:BufferIO` 이벤트는 RDS for PostgreSQL이 페이지에 동시에 액세스하려고 하는 다른 프로세스들이 입출력 (I/O) 작업을 완료할 때까지 기다리는 경우에 발생합니다. 그 목적은 동일한 페이지를 공유 버퍼로 읽는 것입니다.

**Topics**
+ [관련 엔진 버전](#wait-event.lwlockbufferio.context.supported)
+ [컨텍스트](#wait-event.lwlockbufferio.context)
+ [원인](#wait-event.lwlockbufferio.causes)
+ [작업](#wait-event.lwlockbufferio.actions)

## 관련 엔진 버전
<a name="wait-event.lwlockbufferio.context.supported"></a>

이 대기 이벤트 정보는 모든 RDS for PostgreSQL 버전과 관련이 있습니다. RDS for PostgreSQL 12 이하 버전의 경우 이 대기 이벤트의 이름이 lwlock:buffer\$1io인 반면, RDS for PostgreSQL 13 버전에서는 lwlock:bufferio로 이름이 지정됩니다. RDS for PostgreSQL 14 버전부터는 BufferIO 대기 이벤트가 `LWLock`에서 `IPC` 대기 이벤트 유형(IPC:BufferIO)으로 변경되었습니다.

## 컨텍스트
<a name="wait-event.lwlockbufferio.context"></a>

각 공유 버퍼에는 매번 블록(또는 페이지)이 공유 버퍼 풀 외부에서 회수되야 하는 `LWLock:BufferIO` 대기 이벤트와 연결된 I/O 잠금이 있습니다.

이 잠금은 모두 동일한 블록에 액세스해야 하는 여러 세션을 처리하는 데 사용됩니다. 이 블록은 `shared_buffers` 파라미터로 정의된 공유 버퍼 풀 외부에서 읽어야 합니다.

공유 버퍼 풀 내에서 페이지를 읽는 즉시 `LWLock:BufferIO` 잠금은 해제됩니다.

**참고**  
`LWLock:BufferIO` 대기 이벤트는 [IO:DataFileRead](wait-event.iodatafileread.md) 대기 이벤트에 선행됩니다. `IO:DataFileRead` 대기 이벤트는 스토리지에서 데이터를 읽는 동안 발생합니다.

경량 잠금에 대한 자세한 내용은 [잠금 개요](https://github.com/postgres/postgres/blob/65dc30ced64cd17f3800ff1b73ab1d358e92efd8/src/backend/storage/lmgr/README#L20)를 참조하세요.

## 원인
<a name="wait-event.lwlockbufferio.causes"></a>

상위 대기에서 나타나는 `LWLock:BufferIO` 이벤트의 일반적인 원인은 다음을 포함합니다.
+ I/O 작업이 보류 중인 동일한 페이지에 액세스하려고 시도하는 여러 백엔드 또는 연결
+ 공유 버퍼 풀의 크기 간의 비율(`shared_buffers` 파라미터로 정의됨) 및 현재 워크로드에 필요한 버퍼 수
+ 공유 버퍼 풀의 크기가 다른 작업에서 제거되는 페이지 수와 균형이 맞지 않습니다.
+ 엔진이 공유 버퍼 풀에 필요한 것보다 많은 페이지를 읽어야 하는 크거나 부풀린 인덱스
+ DB 엔진이 테이블에서 필요한 것보다 더 많은 페이지를 읽도록 하는 인덱스의 부족
+ 체크포인트가 너무 자주 발생하거나 수정된 페이지를 너무 많이 플러시해야 함
+ 같은 페이지에서 작업을 수행하려고 시도하는 데이터베이스 연결의 갑작스러운 급증

## 작업
<a name="wait-event.lwlockbufferio.actions"></a>

대기 이벤트의 원인에 따라 다른 작업을 권장합니다.
+ `BufferCacheHitRatio` 및 `LWLock:BufferIO` 대기 이벤트의 급격한 감소 간의 상관관계에 대한 Amazon CloudWatch 지표가 관찰됩니다. 이 효과는 공유 버퍼 설정이 작음을 의미할 수 있습니다. DB 인스턴스 클래스를 늘리거나 확장해야 할 수 있습니다. 워크로드를 더 많은 리더 노드로 분할할 수 있습니다.
+ `LWLock:BufferIO`가 `BufferCacheHitRatio` 지표 강하와 동시에 발생한다면 워크로드 피크 시간을 기준으로 `max_wal_size`와 `checkpoint_timeout`을 튜닝하세요. 그런 다음 어떤 쿼리가 발생하는지 식별합니다.
+ 사용되지 않는 인덱스가 있는지 확인한 다음 제거합니다.
+ 분할된 테이블(분할된 인덱스도 있음)을 사용합니다. 이렇게 하면 인덱스 재정렬을 낮게 유지하고 영향을 줄일 수 있습니다.
+ 불필요하게 열을 인덱싱하지 마세요.
+ 연결 풀을 사용하여 갑작스러운 데이터베이스 연결 스파이크를 방지합니다.
+ 데이터베이스에 대한 최대 연결 수를 모범 사례로 제한합니다.

# LWLock:buffer\$1content (BufferContent)
<a name="wait-event.lwlockbuffercontent"></a>

`LWLock:buffer_content` 이벤트는 다른 세션에서 특정 데이터 페이지에 대해 쓰기 잠금을 설정한 동안 세션에서 메모리 내 해당 페이지 읽기 또는 쓰기를 대기 중일 때 발생합니다. RDS for PostgreSQL 13 이상에서는 이 대기 이벤트를 `BufferContent`라고 합니다.

**Topics**
+ [지원되는 엔진 버전](#wait-event.lwlockbuffercontent.context.supported)
+ [컨텍스트](#wait-event.lwlockbuffercontent.context)
+ [대기 증가의 가능한 원인](#wait-event.lwlockbuffercontent.causes)
+ [작업](#wait-event.lwlockbuffercontent.actions)

## 지원되는 엔진 버전
<a name="wait-event.lwlockbuffercontent.context.supported"></a>

이 대기 이벤트 정보는 모든 RDS for PostgreSQL 버전에서 지원됩니다.

## 컨텍스트
<a name="wait-event.lwlockbuffercontent.context"></a>

데이터를 읽거나 조작하기 위해 PostgreSQL 공유 메모리 버퍼를 통해 데이터에 액세스합니다. 버퍼에서 읽기 위해 프로세스는 공유 모드에서 버퍼 콘텐츠에 대한 경량 잠금(LWLock) 을 가져옵니다. 버퍼에 쓰려면 배타적 모드에서 해당 잠금을 가져옵니다. 공유 잠금을 사용하면 다른 프로세스가 동시에 해당 콘텐츠에 대한 공유 잠금을 획득할 수 있습니다. 배타적 잠금은 다른 프로세스에서 모든 유형의 잠금을 얻지 못하게 합니다.

`LWLock:buffer_content`(`BufferContent`) 이벤트는 여러 프로세스가 특정 버퍼의 내용을 잠그려고 시도하고 있음을 나타냅니다.

## 대기 증가의 가능한 원인
<a name="wait-event.lwlockbuffercontent.causes"></a>

`LWLock:buffer_content`(`BufferContent`) 이벤트가 정상보다 많이 나타나 성능 문제를 나타내는 경우 일반적인 원인은 다음과 같습니다.

**동일한 데이터에 대한 동시 업데이트 증가**  
동일한 테이블에 삽입하거나 업데이트하는 쿼리의 동시 세션 수가 증가할 수 있습니다. 이 경합은 인덱스가 많은 테이블에서 더 두드러질 수 있습니다.

**워크로드 데이터가 메모리에 없습니다.**  
활성 워크로드에서 처리 중인 데이터가 메모리에 없으면 이러한 대기 이벤트가 증가할 수 있습니다. 이 효과는 잠금을 유지하는 프로세스가 디스크 I/O 작업을 수행하는 동안 더 오래 유지할 수 있기 때문입니다.

**외래 키 제약 조건을 과도하게 사용**  
외래 키 제약 조건은 프로세스가 버퍼 콘텐츠 잠금에 보관하는 시간을 늘릴 수 있습니다. 이 효과는 해당 키가 업데이트되는 동안 읽기 작업에서 참조된 키에 대해 공유 버퍼 콘텐츠 잠금이 필요하기 때문입니다.

## 작업
<a name="wait-event.lwlockbuffercontent.actions"></a>

대기 이벤트의 원인에 따라 다른 작업을 권장합니다. Amazon RDS 성능 개선 도우미를 사용하거나 `pg_stat_activity` 뷰를 쿼리하여 `LWLock:buffer_content`(`BufferContent`) 이벤트를 식별할 수 있습니다.

**Topics**
+ [인메모리 효율성 향상](#wait-event.lwlockbuffercontent.actions.in-memory)
+ [외래 키 제약 조건 사용 감소](#wait-event.lwlockbuffercontent.actions.foreignkey)
+ [사용되지 않는 인덱스 삭제](#wait-event.lwlockbuffercontent.actions.indexes)
+ [시퀀스 사용 시 캐시 크기 늘리기](#wait-event.lwlockbuffercontent.actions.sequences)

### 인메모리 효율성 향상
<a name="wait-event.lwlockbuffercontent.actions.in-memory"></a>

활성 워크로드 데이터가 메모리에 있을 확률을 높이려면 테이블을 분할하거나 인스턴스 클래스를 확장하세요. DB 인스턴스 클래스에 대한 자세한 내용은 [DB 인스턴스 클래스](Concepts.DBInstanceClass.md) 섹션을 참조하세요.

### 외래 키 제약 조건 사용 감소
<a name="wait-event.lwlockbuffercontent.actions.foreignkey"></a>

외래 키 제약 조건을 사용을 위해 많은 수의 `LWLock:buffer_content`(`BufferContent`) 대기 이벤트를 경험하는 워크로드를 조사합니다. 불필요한 외래 키 제약 조건을 제거합니다.

### 사용되지 않는 인덱스 삭제
<a name="wait-event.lwlockbuffercontent.actions.indexes"></a>

많은 수의 `LWLock:buffer_content`(`BufferContent`) 대기 이벤트를 경험하는 워크로드를 위해 사용하지 않는 인덱스를 식별하고 제거합니다.

### 시퀀스 사용 시 캐시 크기 늘리기
<a name="wait-event.lwlockbuffercontent.actions.sequences"></a>

테이블에서 시퀀스를 사용하는 경우 캐시 크기를 늘려 시퀀스 페이지와 인덱스 페이지에서 경합을 제거합니다. 각 시퀀스는 공유 메모리에서 단일 페이지입니다. 캐시는 연결별로 사전 정의됩니다. 많은 동시 세션에서 시퀀스 값을 받는 경우 이 방법으로는 워크로드를 처리하기에 충분하지 않을 수 있습니다.

# LWLock:lock\$1manager (LWLock:lockmanager)
<a name="wait-event.lw-lock-manager"></a>

이 이벤트는 RDS for PostgreSQL 엔진이 빠른 경로 잠금이 불가능할 때 잠금을 할당, 확인 및 할당 해제하기 위해 공유 잠금 메모리 영역을 유지 관리하는 경우에 발생합니다.

**Topics**
+ [지원되는 엔진 버전](#wait-event.lw-lock-manager.context.supported)
+ [컨텍스트](#wait-event.lw-lock-manager.context)
+ [대기 증가의 가능한 원인](#wait-event.lw-lock-manager.causes)
+ [작업](#wait-event.lw-lock-manager.actions)

## 지원되는 엔진 버전
<a name="wait-event.lw-lock-manager.context.supported"></a>

이 대기 이벤트 정보는 RDS for PostgreSQL 버전 9.6 이상과 관련이 있습니다. 버전 13 이전의 RDS for PostgreSQL에서 이 대기 이벤트의 이름은 `LWLock:lock_manager`입니다. 버전 13 이상의 RDS for PostgreSQL에서 이 대기 이벤트의 이름은 `LWLock:lockmanager`입니다.

## 컨텍스트
<a name="wait-event.lw-lock-manager.context"></a>

SQL 문을 실행하면 RDS for PostgreSQL 동시 작업 중에 데이터베이스의 구조, 데이터 및 무결성을 보호하기 위해 잠금을 기록합니다. 엔진은 빠른 경로 잠금 또는 빠르지 않은 경로 잠금을 사용하여 이 목표를 달성할 수 있습니다. 빠르지 않은 경로 잠금은 빠른 경로 잠금보다 비용이 많이 들고 오버헤드가 더 많이 발생합니다.

### 빠른 경로 잠금
<a name="wait-event.lw-lock-manager.context.fast-path"></a>

자주 수행되고 해제되지만 충돌이 거의 발생하지 않는 잠금의 오버헤드를 줄이기 위해 백엔드 프로세스에서 빠른 경로 잠금을 사용할 수 있습니다. 데이터베이스는 다음 기준을 충족하는 잠금에 대해 이 메커니즘을 사용합니다.
+ DEFAULT 잠금 방법을 사용합니다.
+ 공유 관계가 아닌 데이터베이스 관계에 대한 잠금을 나타냅니다.
+ 그들은 충돌할 가능성이 없는 약한 잠금입니다.
+ 엔진은 충돌하는 잠금이 존재하지 않을 수 있는지 신속하게 확인할 수 있습니다.

다음 조건 중 하나에 부합할 때 엔진은 빠른 경로 잠금을 사용할 수 없습니다.
+ 이 잠금이 이전 기준을 충족하지 않습니다.
+ 백엔드 프로세스에 사용할 수 있는 슬롯이 더 이상 없습니다.

빠른 경로 잠금을 위해 쿼리를 조정하려는 경우 다음 쿼리를 사용할 수 있습니다.

```
SELECT count(*), pid, mode, fastpath
  FROM pg_locks
 WHERE fastpath IS NOT NULL
 GROUP BY 4,3,2
 ORDER BY pid, mode;
 count | pid  |      mode       | fastpath
-------+------+-----------------+----------
16 | 9185 | AccessShareLock | t
336 | 9185 | AccessShareLock | f
1 | 9185 | ExclusiveLock   | t
```

다음 쿼리는 데이터베이스의 합계만 표시합니다.

```
SELECT count(*), mode, fastpath
  FROM pg_locks
 WHERE fastpath IS NOT NULL
 GROUP BY 3,2
 ORDER BY mode,1;
count |      mode       | fastpath
-------+-----------------+----------
16 | AccessShareLock | t
337 | AccessShareLock | f
1 | ExclusiveLock   | t
(3 rows)
```

고속 경로 잠금에 대한 자세한 내용은 잠금 관리자 README의 [빠른 경로](https://github.com/postgres/postgres/blob/master/src/backend/storage/lmgr/README#L70-L76)와 PostgreSQL 설명서의 [pg-locks](https://www.postgresql.org/docs/9.3/view-pg-locks.html#AEN98195)를 참조하세요.

### 잠금 관리자의 배율 조정 문제 예
<a name="wait-event.lw-lock-manager.context.lock-manager"></a>

이 예에서는 이름이 `purchases`인 테이블이 매일로 분할된 5년짜리 데이터를 저장합니다. 각 파티션에는 두 개의 인덱스가 있습니다. 다음과 같은 일련의 이벤트가 발생합니다.

1. 며칠 분량의 데이터를 쿼리하면 데이터베이스가 많은 파티션을 읽어야 합니다.

1. 데이터베이스는 각 파티션에 대한 잠금 항목을 만듭니다. 파티션 인덱스가 최적기 액세스 경로의 일부인 경우 데이터베이스도 해당 인덱스에 대한 잠금 항목을 만듭니다.

1. 동일한 백엔드 프로세스에 대해 요청된 잠금 항목 수가 `FP_LOCK_SLOTS_PER_BACKEND`의 값인 16보다 높은 경우 잠금 관리자는 비고속 경로 잠금 방법을 사용합니다.

최신 애플리케이션에는 수백 개의 세션이 있을 수 있습니다. 동시 세션이 적절한 파티션 정리 없이 부모를 쿼리하는 경우 데이터베이스는 수백 또는 수천 개의 고속 경로 잠금을 생성할 수 있습니다. 일반적으로 이 동시성이 vCPU 수보다 높으면 `LWLock:lock_manager` 대기 이벤트가 표시됩니다.

**참고**  
`LWLock:lock_manager` 대기 이벤트는 데이터베이스 스키마의 파티션 또는 인덱스 수와 관련이 없습니다. 대신 데이터베이스가 제어해야 하는 비고속 경로 잠금 수와 관련이 있습니다.

## 대기 증가의 가능한 원인
<a name="wait-event.lw-lock-manager.causes"></a>

`LWLock:lock_manager` 대기 이벤트가 정상보다 더 발생하여 성능 문제를 나타낼 수 있으며 갑작스런 스파이크의 가장 큰 원인은 다음과 같습니다.
+ 동시 활성 세션은 빠른 경로 잠금을 사용하지 않는 쿼리를 실행하고 있습니다. 이러한 세션도 최대 vCPU를 초과합니다.
+ 많은 수의 동시 활성 세션이 심하게 분할된 테이블에 액세스하고 있습니다. 각 파티션에는 여러 개의 인덱스가 있습니다.
+ 데이터베이스에 연결 폭풍이 발생합니다. 기본적으로 일부 애플리케이션 및 연결 풀 소프트웨어는 데이터베이스 속도가 느릴 때 더 많은 연결을 만듭니다. 이 관행은 문제를 악화시킵니다. 연결 스톰이 발생하지 않도록 연결 풀 소프트웨어를 튜닝합니다.
+ 많은 수의 세션이 파티션을 정리하지 않고 상위 테이블을 쿼리합니다.
+ 데이터 정의 언어 (DDL), 유지 관리 명령은 자주 액세스하거나 수정되는 사용 중인 관계식 또는 튜플을 독점적으로 잠급니다.

## 작업
<a name="wait-event.lw-lock-manager.actions"></a>

`CPU` 대기 이벤트가 발생하는 것이 반드시 성능 문제를 나타내지는 않습니다. 성능이 저하되고 이 대기 이벤트가 DB 로드를 지배하는 경우에만 이 이벤트에 응답합니다.

**Topics**
+ [파티션 정리 사용](#wait-event.lw-lock-manager.actions.pruning)
+ [불필요한 인덱스 삭제](#wait-event.lw-lock-manager.actions.indexes)
+ [빠른 경로 잠금을 위한 쿼리 조정](#wait-event.lw-lock-manager.actions.tuning)
+ [다른 대기 이벤트 조정](#wait-event.lw-lock-manager.actions.other-waits)
+ [하드웨어 병목 현상 저감](#wait-event.lw-lock-manager.actions.hw-bottlenecks)
+ [연결 풀러 사용](#wait-event.lw-lock-manager.actions.pooler)
+ [RDS for PostgreSQL 버전 업그레이드](#wait-event.lw-lock-manager.actions.pg-version)

### 파티션 정리 사용
<a name="wait-event.lw-lock-manager.actions.pruning"></a>

*파티션 정리*는 선언적으로 분할된 테이블을 위한 쿼리 최적화 전략으로, 테이블 검색에서 불필요한 파티션을 제외하여 성능을 향상시킵니다. 파티션 정리는 기본적으로 활성화되어 있습니다. 꺼져 있으면 다음과 같이 켭니다.

```
SET enable_partition_pruning = on;
```

`WHERE` 절에 파티셔닝에 사용되는 열이 들어 있으면 쿼리는 파티션 정리를 활용할 수 있습니다. 더 자세한 내용은 PostgreSQL 설명서의 [파티션 정리](https://www.postgresql.org/docs/current/ddl-partitioning.html#DDL-PARTITION-PRUNING)를 참조하세요.

### 불필요한 인덱스 삭제
<a name="wait-event.lw-lock-manager.actions.indexes"></a>

데이터베이스에 사용되지 않거나 거의 사용되지 않는 인덱스가 포함되어 있을 수 있습니다. 이 경우 삭제하는 것이 좋습니다. 다음 중 하나를 수행하세요.
+ 불필요한 인덱스를 찾아내려면, PostgreSQL 위키의 [사용되지 않는 인덱스](https://wiki.postgresql.org/wiki/Index_Maintenance#Unused_Indexes)를 읽어보세요.
+ PG 컬렉터를 실행합니다. 이 SQL 스크립트는 데이터베이스 정보를 수집하여 통합 HTML 보고서로 표시합니다. '사용되지 않은 인덱스' 섹션을 확인하세요. 자세한 내용은 AWS Labs GitHub 리포지토리의 [pg-collector](https://github.com/awslabs/pg-collector)를 참조하세요.

### 빠른 경로 잠금을 위한 쿼리 조정
<a name="wait-event.lw-lock-manager.actions.tuning"></a>

쿼리에서 빠른 경로 잠금을 사용하는지 확인하려면 `pg_locks` 테이블의 `fastpath` 열을 쿼리하세요. 쿼리에서 빠른 경로 잠금을 사용하지 않는 경우 쿼리당 관계 수를 16 미만으로 줄이세요.

### 다른 대기 이벤트 조정
<a name="wait-event.lw-lock-manager.actions.other-waits"></a>

`LWLock:lock_manager`가 최상위 대기 목록에서 첫 번째 또는 두 번째일 경우, 다음 대기 이벤트도 목록에 나타나는지 확인합니다.
+ `Lock:Relation`
+ `Lock:transactionid`
+ `Lock:tuple`

위의 이벤트가 목록에서 높게 표시되는 경우 이러한 대기 이벤트를 먼저 조정하는 것이 좋습니다. 이러한 이벤트는 `LWLock:lock_manager` 드라이버가 될 수 있습니다.

### 하드웨어 병목 현상 저감
<a name="wait-event.lw-lock-manager.actions.hw-bottlenecks"></a>

CPU 부족 또는 Amazon EBS 대역폭의 최대 사용량과 같은 하드웨어 병목 현상이 발생할 수 있습니다. 이 경우에는 하드웨어 병목 현상을 줄이는 것이 좋습니다. 다음 조치를 고려해 보세요.
+ 인스턴스 클래스를 확장하세요.
+ 대량의 CPU와 메모리를 사용하는 쿼리를 최적화하세요.
+ 애플리케이션 로직을 변경하세요.
+ 데이터를 아카이빙하세요.

CPU, 메모리 및 EBS 네트워크 대역폭에 대한 자세한 내용은 [Amazon RDS 인스턴스 유형](https://aws.amazon.com/rds/instance-types/)을 참조하세요.

### 연결 풀러 사용
<a name="wait-event.lw-lock-manager.actions.pooler"></a>

총 활성 연결 수가 최대 vCPU를 초과하는 경우 인스턴스 유형이 지원할 수 있는 것보다 많은 OS 프로세스에 CPU가 필요합니다. 이 경우에는 연결 풀을 사용하거나 튜닝하는 것이 좋습니다. 인스턴스 유형별 vCPU 수에 대한 자세한 내용은 [Amazon RDS 인스턴스 유형](https://aws.amazon.com/rds/instance-types/)을 참조하세요.

연결 풀링에 대한 자세한 내용은 다음 리소스를 참조하세요.
+ [ Amazon RDS Proxy](rds-proxy.md)
+ [pgbouncer](http://www.pgbouncer.org/usage.html)
+ *PostgreSQL 설명서*의 [연결 풀 및 데이터 원본](https://www.postgresql.org/docs/7.4/jdbc-datasource.html)

### RDS for PostgreSQL 버전 업그레이드
<a name="wait-event.lw-lock-manager.actions.pg-version"></a>

현재 버전의 RDS for PostgreSQL이 12보다 낮은 경우 버전 12 이상으로 업그레이드하세요. PostgreSQL 버전 12 이상에는 향상된 파티션 메커니즘이 있습니다. 버전 12의 더 자세한 정보는 [PostgreSQL 릴리스 12.0]( https://www.postgresql.org/docs/release/12.0/)을 참조하세요. RDS for PostgreSQL 업그레이드에 대한 자세한 내용은 [RDS for PostgreSQL DB 엔진 업그레이드](USER_UpgradeDBInstance.PostgreSQL.md) 섹션을 참조하세요.

# LWLock:pg\$1stat\$1statements
<a name="apg-rpg-lwlockpgstat"></a>

LWLock:pg\$1stat\$1statements 대기 이벤트는 `pg_stat_statements` 확장이 SQL 문을 추적하는 해시 테이블에서 배타적 잠금을 수행할 때 발생합니다. 다음 시나리오에서 발생할 수 있습니다.
+ 추적된 문 수가 구성된 `pg_stat_statements.max` 파라미터 값에 도달하고 더 많은 항목을 위한 공간을 확보해야 하는 경우, 확장은 직접 호출 수를 기준으로 정렬을 수행하고 가장 적게 실행된 문 중 5%를 제거한 다음 해시를 나머지 항목으로 다시 채웁니다.
+ `pg_stat_statements`가 디스크의 `pgss_query_texts.stat` 파일에 대해 `garbage collection` 작업을 수행하고 파일을 다시 쓰는 경우

**Topics**
+ [지원되는 엔진 버전](#apg-rpg-lwlockpgstat.supported)
+ [컨텍스트](#apg-rpg-lwlockpgstat.context)
+ [대기 증가의 가능한 원인](#apg-rpg-lwlockpgstat.causes)
+ [작업](#apg-rpg-lwlockpgstat.actions)

## 지원되는 엔진 버전
<a name="apg-rpg-lwlockpgstat.supported"></a>

 이 대기 이벤트 정보는 모든 RDS for PostgreSQL 버전에서 지원됩니다.

## 컨텍스트
<a name="apg-rpg-lwlockpgstat.context"></a>

**pg\$1stat\$1statements 확장 이해** - pg\$1stat\$1statements 확장은 해시 테이블에서 SQL 문 실행 통계를 추적합니다. 이 확장은 `pg_stat_statements.max` 파라미터에서 정의된 한도까지 SQL 문을 추적합니다. 이 파라미터는 추적할 수 있는 문의 최대 수를 결정하며, 이는 pg\$1stat\$1statements 뷰의 최대 행 수에 해당합니다.

**문 통계 지속성** - 이 확장은 다음을 통해 인스턴스 재시작 시 문 통계를 유지합니다.
+ pg\$1stat\$1statements.stat라는 파일에 데이터 쓰기
+ pg\$1stat\$1statements.save 파라미터를 사용하여 지속성 동작 제어

pg\$1stat\$1statements.save가 다음과 같이 설정된 경우:
+ on(기본값): 통계는 종료 시 저장되고 서버 시작 시 다시 로드됩니다.
+ off: 통계는 종료 시 저장되지 않으며 서버 시작 시 다시 로드되지 않습니다.

**쿼리 텍스트 저장소** - 이 확장은 추적된 쿼리의 텍스트를 `pgss_query_texts.stat`라는 파일에 저장합니다. 이 파일은 폐영역 회수가 발생하기 전에 추적된 모든 SQL 문의 평균 크기의 두 배까지 늘어날 수 있습니다. 이 확장에는 정리 작업 및 `pgss_query_texts.stat` 파일 재작성 중에 해시 테이블에 대한 배타적 잠금이 필요합니다.

**문 할당 취소 프로세스** - 추적된 문 수가 `pg_stat_statements.max` 한도에 도달하고 새 문을 추적해야 하는 경우 확장은 다음을 수행합니다.
+ 해시 테이블에서 배타적 잠금(LWLock:pg\$1stat\$1statements)을 수행합니다.
+ 기존 데이터를 로컬 메모리에 로드합니다.
+ 직접 호출 수를 기반으로 빠른 정렬을 수행합니다.
+ 가장 적게 호출된 문(하위 5%)을 제거합니다.
+ 해시 테이블을 나머지 항목으로 다시 채웁니다.

**문 할당 취소 모니터링** - PostgreSQL 14 이상에서는 pg\$1stat\$1statements\$1info 뷰를 사용하여 문 할당 취소를 모니터링할 수 있습니다. 이 뷰에는 새 문을 위한 공간을 마련하기 위해 문을 할당 취소한 횟수를 보여주는 dealloc 열이 포함되어 있습니다.

문의 할당 취소가 자주 발생하면 디스크에서 `pgss_query_texts.stat` 파일의 폐영역 회수가 더 자주 발생합니다.

## 대기 증가의 가능한 원인
<a name="apg-rpg-lwlockpgstat.causes"></a>

`LWLock:pg_stat_statements` 대기 시간 증가의 일반적인 원인은 다음과 같습니다.
+ 애플리케이션에서 사용하는 고유 쿼리 수가 증가함
+ 사용되는 고유 쿼리 수 대비 `pg_stat_statements.max` 파라미터 값이 작음

## 작업
<a name="apg-rpg-lwlockpgstat.actions"></a>

대기 이벤트의 원인에 따라 다른 작업을 권장합니다. Amazon RDS Performance Insights를 사용하거나 `pg_stat_activity` 뷰를 쿼리하여 `LWLock:pg_stat_statements` 이벤트를 식별할 수 있습니다.

다음 `pg_stat_statements` 파라미터를 조정하여 추적 동작을 제어하고 LWLock:pg\$1stat\$1 문 대기 이벤트를 줄입니다.

**Topics**
+ [pg\$1stat\$1statements.track 파라미터 비활성화](#apg-rpg-lwlockpgstat.actions.disabletrack)
+ [pg\$1stat\$1statements.max 파라미터 증가](#apg-rpg-lwlockpgstat.actions.increasemax)
+ [pg\$1stat\$1statements.track\$1utility 파라미터 비활성화](#apg-rpg-lwlockpgstat.actions.disableutility)

### pg\$1stat\$1statements.track 파라미터 비활성화
<a name="apg-rpg-lwlockpgstat.actions.disabletrack"></a>

LWLock:pg\$1stat\$1statements 대기 이벤트가 데이터베이스 성능에 부정적인 영향을 미치고 근본 원인을 식별하기 위해 `pg_stat_statements` 뷰를 추가로 분석하기 전에 신속한 솔루션이 필요한 경우 `pg_stat_statements.track` 파라미터를 `none`으로 설정하여 파라미터를 비활성화할 수 있습니다. 그러면 문 통계 수집이 비활성화됩니다.

### pg\$1stat\$1statements.max 파라미터 증가
<a name="apg-rpg-lwlockpgstat.actions.increasemax"></a>

할당 취소를 줄이고 디스크에서 `pgss_query_texts.stat` 파일의 폐영역 회수를 최소화하려면 `pg_stat_statements.max` 파라미터 값을 늘립니다. 기본값은 `5,000`입니다.

**참고**  
`pg_stat_statements.max` 파라미터는 정적입니다. 이 파라미터에 변경 사항을 적용하려면 DB 인스턴스를 다시 시작해야 합니다.

### pg\$1stat\$1statements.track\$1utility 파라미터 비활성화
<a name="apg-rpg-lwlockpgstat.actions.disableutility"></a>

pg\$1stat\$1statements 뷰를 분석하여 `pg_stat_statements`에서 추적하는 리소스를 가장 많이 소비하는 유틸리티 명령을 확인할 수 있습니다.

`pg_stat_statements.track_utility` 파라미터는 모듈이 SELECT, INSERT, UPDATE, DELETE 및 MERGE를 제외한 모든 명령을 포함하는 유틸리티 명령을 추적하는지를 제어합니다. 이 파라미터는 기본적으로 `on`로 설정되어 있습니다.

예를 들어 애플리케이션이 본질적으로 고유한 많은 저장점 쿼리를 사용하는 경우 문 할당 취소가 증가할 수 있습니다. 이를 해결하기 위해 `pg_stat_statements.track_utility` 파라미터를 비활성화하여 `pg_stat_statements`가 저장점 쿼리를 추적하지 않도록 할 수 있습니다.

**참고**  
`pg_stat_statements.track_utility` 파라미터는 동적 파라미터입니다. 데이터베이스 인스턴스를 다시 시작하지 않고도 값을 변경할 수 있습니다.

**Example pg\$1stat\$1statements의 고유한 저장점 쿼리 예시**  <a name="savepoint-queries"></a>

```
                     query                       |       queryid       
-------------------------------------------------+---------------------
 SAVEPOINT JDBC_SAVEPOINT_495701                 | -7249565344517699703
 SAVEPOINT JDBC_SAVEPOINT_1320                   | -1572997038849006629
 SAVEPOINT JDBC_SAVEPOINT_26739                  |  54791337410474486
 SAVEPOINT JDBC_SAVEPOINT_1294466                |  8170064357463507593
 ROLLBACK TO SAVEPOINT JDBC_SAVEPOINT_65016      | -33608214779996400
 SAVEPOINT JDBC_SAVEPOINT_14185                  | -2175035613806809562
 SAVEPOINT JDBC_SAVEPOINT_45837                  | -6201592986750645383
 SAVEPOINT JDBC_SAVEPOINT_1324                   |  6388797791882029332
```

PostgreSQL 17에는 유틸리티 명령 추적을 위한 몇 가지 향상된 기능이 도입되었습니다.
+ 이제 Savepoint 이름이 상수로 표시됩니다.
+ 이제 두 단계 커밋 명령의 글로벌 트랜잭션 ID(GID)가 상수로 표시됩니다.
+ DEALLOCATE 문의 이름이 상수로 표시됩니다.
+ 이제 CALL 파라미터가 상수로 표시됩니다.

# LWLock:SubtransSLRU (LWLock:SubtransControlLock)
<a name="wait-event.lwlocksubtransslru"></a>

`LWLock:SubtransSLRU` 및 `LWLock:SubtransBuffer` 대기 이벤트는 세션이 하위 트랜잭션 정보를 위해 가장 오래전에 사용된 단순(SLRU) 캐시에 액세스하기 위해 대기 중임을 나타냅니다. 이는 트랜잭션 가시성 및 상위-하위 관계를 결정할 때 발생합니다.
+ `LWLock:SubtransSLRU`: 프로세스가 하위 트랜잭션에 대해 가장 오래전에 사용된 단순(SLRU) 캐시에 액세스하기 위해 대기 중입니다. 버전 13 이전의 RDS for PostgreSQL에서는 이 대기 이벤트를 `SubtransControlLock`이라고 합니다.
+ `LWLock:SubtransBuffer`: 프로세스가 하위 트랜잭션에 대해 가장 오래전에 사용된 단순(SLRU) 버퍼에서 I/O를 기다리고 있습니다. 버전 13 이전의 RDS for PostgreSQL에서는 이 대기 이벤트를 `subtrans`이라고 합니다.

**Topics**
+ [지원되는 엔진 버전](#wait-event.lwlocksubtransslru.supported)
+ [컨텍스트](#wait-event.lwlocksubtransslru.context)
+ [대기 증가의 가능한 원인](#wait-event.lwlocksubtransslru.causes)
+ [작업](#wait-event.lwlocksubtransslru.actions)

## 지원되는 엔진 버전
<a name="wait-event.lwlocksubtransslru.supported"></a>

이 대기 이벤트 정보는 모든 RDS for PostgreSQL 버전에서 지원됩니다.

## 컨텍스트
<a name="wait-event.lwlocksubtransslru.context"></a>

**하위 트랜잭션 이해** - 하위 트랜잭션은 PostgreSQL의 트랜잭션 내 트랜잭션입니다. 이를 중첩 트랜잭션이라고도 합니다.

하위 트랜잭션은 일반적으로 다음을 사용할 때 생성됩니다.
+ `SAVEPOINT` 명령
+ 예외 블록(`BEGIN/EXCEPTION/END`)

하위 트랜잭션을 사용하면 전체 트랜잭션에 영향을 주지 않고 트랜잭션의 일부를 롤백할 수 있습니다. 이를 통해 트랜잭션 관리를 세밀하게 제어할 수 있습니다.

**구현 세부 정보** - PostgreSQL은 하위 트랜잭션을 기본 트랜잭션 내의 중첩 구조로 구현합니다. 각 하위 트랜잭션은 자체 트랜잭션 ID를 가져옵니다.

주요 구현 측면:
+ 트랜잭션 ID는 `pg_xact`에서 추적됩니다.
+ 상위-하위 관계는 `PGDATA` 아래의 `pg_subtrans` 하위 디렉터리에 저장됩니다.
+ 각 데이터베이스 세션은 최대 `64`개의 활성 하위 트랜잭션을 유지할 수 있습니다.
+ 이 제한을 초과하면 하위 트랜잭션 오버플로가 발생하므로 하위 트랜잭션 정보를 위해 가장 오래전에 사용된 단순(SLRU) 캐시에 액세스해야 합니다.

## 대기 증가의 가능한 원인
<a name="wait-event.lwlocksubtransslru.causes"></a>

하위 트랜잭션 SLRU 경합의 일반적인 원인은 다음과 같습니다.
+ **SAVEPOINT 및 EXCEPTION 처리의 과도한 사용** - `EXCEPTION` 핸들러가 있는 PL/pgSQL 프로시저는 예외 발생 여부에 관계없이 암시적 저장점을 자동으로 생성합니다. 각 `SAVEPOINT`는 새 하위 트랜잭션을 시작합니다. 단일 트랜잭션에 64개 이상의 하위 트랜잭션이 누적되면 하위 트랜잭션 SLRU 오버플로가 트리거됩니다.
+ **드라이버 및 ORM 구성** - `SAVEPOINT`는 애플리케이션 코드에서 명시적으로 사용하거나 드라이버 구성을 통해 암시적으로 사용할 수 있습니다. 일반적으로 사용되는 많은 ORM 도구 및 애플리케이션 프레임워크는 기본적으로 중첩된 트랜잭션을 지원합니다. 다음은 몇 가지 일반적인 예제입니다.
  + JDBC 드라이버 파라미터 `autosave`는 `always` 또는 `conservative`로 설정하면 각 쿼리 전에 저장점을 생성합니다.
  + `propagation_nested`로 설정된 경우 Spring Framework 트랜잭션 정의입니다.
  + `requires_new: true`가 설정된 경우의 Rails입니다.
  + `session.begin_nested`가 사용되는 경우의 SQLAlchemy입니다.
  + 중첩 `atomic()` 블록이 사용되는 경우의 Django입니다.
  + `Savepoint`가 사용되는 경우의 GORM입니다.
  + 롤백 수준 설정이 문 수준 롤백으로 설정된 경우의 psqlODBC입니다(예: `PROTOCOL=7.4-2`).
+ **장기 실행 트랜잭션 및 하위 트랜잭션이 있는 높은 동시 워크로드** - 동시 워크로드가 많고 장기 실행 트랜잭션 및 하위 트랜잭션 중에 하위 트랜잭션 SLRU 오버플로가 발생하면 PostgreSQL의 경합이 증가합니다. 이는 `LWLock:SubtransBuffer` 및 `LWLock:SubtransSLRU` 잠금에 대한 승격된 대기 이벤트로 표시됩니다.

## 작업
<a name="wait-event.lwlocksubtransslru.actions"></a>

대기 이벤트의 원인에 따라 다른 작업을 권장합니다. 일부 작업은 즉각적인 완화를 제공하는 반면, 다른 작업은 조사 및 장기 수정이 필요합니다.

**Topics**
+ [하위 트랜잭션 사용량 모니터링](#wait-event.lwlocksubtransslru.actions.monitor)
+ [메모리 파라미터 구성](#wait-event.lwlocksubtransslru.actions.memory)
+ [장기적인 조치](#wait-event.lwlocksubtransslru.actions.longterm)

### 하위 트랜잭션 사용량 모니터링
<a name="wait-event.lwlocksubtransslru.actions.monitor"></a>

PostgreSQL 버전 16.1 이상에서는 다음 쿼리를 사용하여 백엔드당 하위 트랜잭션 수 및 오버플로 상태를 모니터링합니다. 이 쿼리는 백엔드 통계를 활동 정보와 조인하여 하위 트랜잭션을 사용하는 프로세스를 보여 줍니다.

```
SELECT a.pid, usename, query, state, wait_event_type,
       wait_event, subxact_count, subxact_overflowed
FROM (SELECT id, pg_stat_get_backend_pid(id) pid, subxact_count, subxact_overflowed
      FROM pg_stat_get_backend_idset() id
           JOIN LATERAL pg_stat_get_backend_subxact(id) AS s ON true
     ) a
JOIN pg_stat_activity b ON a.pid = b.pid;
```

PostgreSQL 버전 13.3 이상에서는 `pg_stat_slru` 뷰에서 하위 트랜잭션 캐시 압력을 모니터링합니다. 다음 SQL 쿼리는 Subtrans 구성 요소에 대한 SLRU 캐시 통계를 검색합니다.

```
SELECT * FROM pg_stat_slru WHERE name = 'Subtrans';
```

`blks_read` 값이 지속적으로 증가하면 캐시되지 않은 하위 트랜잭션에 대한 디스크 액세스가 잦아 잠재적 SLRU 캐시 압력을 나타냅니다.

### 메모리 파라미터 구성
<a name="wait-event.lwlocksubtransslru.actions.memory"></a>

PostgreSQL 17.1 이상에서는 `subtransaction_buffers` 파라미터를 사용하여 하위 트랜잭션 SLRU 캐시 크기를 구성할 수 있습니다. 다음 구성 예제에서는 하위 트랜잭션 버퍼 파라미터를 설정하는 방법을 보여 줍니다.

```
subtransaction_buffers = 128
```

이 파라미터는 하위 트랜잭션 콘텐츠(`pg_subtrans`)를 캐싱하는 데 사용되는 공유 메모리의 양을 지정합니다. 단위 없이 지정하면 값은 일반적으로 각각 8KB인 `BLCKSZ`바이트 블록을 나타냅니다. 예를 들어, 값을 128로 설정하면 하위 트랜잭션 캐시에 1MB(128 \$1 8kB)의 메모리가 할당됩니다.

**참고**  
모든 인스턴스가 일관되게 유지되도록 클러스터 수준에서 이 파라미터를 설정할 수 있습니다. 특정 워크로드 요구 사항 및 인스턴스 클래스에 적합하도록 값을 테스트하고 조정합니다. 파라미터 변경 사항을 적용하려면 인스턴스를 재부팅해야 합니다.

### 장기적인 조치
<a name="wait-event.lwlocksubtransslru.actions.longterm"></a>
+ **애플리케이션 코드 및 구성 검사** - 애플리케이션 코드 및 데이터베이스 드라이버 구성을 검토하여 일반적으로 명시적 및 암시적 `SAVEPOINT` 사용량과 하위 트랜잭션 사용량을 모두 확인합니다. 64개 이상의 하위 트랜잭션을 생성할 가능성이 있는 트랜잭션을 식별합니다.
+ **저장점 사용량 감소** - 트랜잭션에서 저장점 사용을 최소화합니다.
  + EXCEPTION 블록을 사용하여 PL/pgSQL 프로시저 및 함수를 검토합니다. EXCEPTION 블록은 암시적 저장점을 자동으로 생성하여 하위 트랜잭션 오버플로에 기여할 수 있습니다. 각 EXCEPTION 절은 실행 중에 실제로 예외가 발생하는지 여부에 관계없이 하위 트랜잭션을 생성합니다.  
**Example**  

    예제 1: 문제가 있는 예외 블록 사용

    다음 코드 예제는 여러 하위 트랜잭션을 생성하는 문제가 있는 EXCEPTION 블록 사용을 보여 줍니다.

    ```
    CREATE OR REPLACE FUNCTION process_user_data()
    RETURNS void AS $$
    DECLARE
        user_record RECORD;
    BEGIN
        FOR user_record IN SELECT * FROM users LOOP
            BEGIN
                -- This creates a subtransaction for each iteration
                INSERT INTO user_audit (user_id, action, timestamp)
                VALUES (user_record.id, 'processed', NOW());
                
                UPDATE users 
                SET last_processed = NOW() 
                WHERE id = user_record.id;
                
            EXCEPTION
                WHEN unique_violation THEN
                    -- Handle duplicate audit entries
                    UPDATE user_audit 
                    SET timestamp = NOW() 
                    WHERE user_id = user_record.id AND action = 'processed';
            END;
        END LOOP;
    END;
    $$ LANGUAGE plpgsql;
    ```

    다음은 예외 처리 대신 UPSERT를 사용하여 하위 트랜잭션 사용을 줄이는 개선된 코드 예제입니다.

    ```
    CREATE OR REPLACE FUNCTION process_user_data()
    RETURNS void AS $$
    DECLARE
        user_record RECORD;
    BEGIN
        FOR user_record IN SELECT * FROM users LOOP
            -- Use UPSERT to avoid exception handling
            INSERT INTO user_audit (user_id, action, timestamp)
            VALUES (user_record.id, 'processed', NOW())
            ON CONFLICT (user_id, action) 
            DO UPDATE SET timestamp = NOW();
            
            UPDATE users 
            SET last_processed = NOW() 
            WHERE id = user_record.id;
        END LOOP;
    END;
    $$ LANGUAGE plpgsql;
    ```  
**Example**  

    예제 2: STRICT 예외 핸들러

    다음 코드 예제는 NO\$1DATA\$1FOUND를 사용한 문제가 있는 EXCEPTION 처리를 보여 줍니다.

    ```
    CREATE OR REPLACE FUNCTION get_user_email(p_user_id INTEGER)
    RETURNS TEXT AS $$
    DECLARE
        user_email TEXT;
    BEGIN
        BEGIN
            -- STRICT causes an exception if no rows or multiple rows found
            SELECT email INTO STRICT user_email 
            FROM users 
            WHERE id = p_user_id;
            
            RETURN user_email;
            
        EXCEPTION
            WHEN NO_DATA_FOUND THEN
                RETURN 'Email not found';
        END;
    END;
    $$ LANGUAGE plpgsql;
    ```

    다음 향상된 코드 예제에서는 예외 처리 대신 IF NOT FOUND를 사용하여 하위 트랜잭션을 방지합니다.

    ```
    CREATE OR REPLACE FUNCTION get_user_email(p_user_id INTEGER)
    RETURNS TEXT AS $$
    DECLARE
        user_email TEXT;
    BEGIN
         SELECT email INTO user_email 
         FROM users 
         WHERE id = p_user_id;
            
         IF NOT FOUND THEN
             RETURN 'Email not found';
         ELSE
             RETURN user_email;
         END IF;
    END;
    $$ LANGUAGE plpgsql;
    ```
  + JDBC 드라이버 – 파라미터 `autosave`는 `always` 또는 `conservative`로 설정하면 각 쿼리 전에 저장점을 생성합니다. `never` 설정이 애플리케이션에 적합한지 평가합니다.
  + PostgreSQL ODBC 드라이버(psqlODBC) - 롤백 수준 설정(문 수준 롤백용)은 암시적 저장점을 생성하여 문 롤백 기능을 활성화합니다. 트랜잭션 수준 롤백이 애플리케이션에 적합한지 여부를 평가합니다.
  + ORM 트랜잭션 구성 검사
  + 저장점이 필요하지 않은 대체 오류 처리 전략 고려
+ **트랜잭션 설계 최적화** - 트랜잭션을 재구성하여 과도한 중첩을 방지하고 하위 트랜잭션 오버플로 조건의 가능성을 줄입니다.
+ **장기 실행 트랜잭션 감소** - 장기 실행 트랜잭션은 하위 트랜잭션 정보를 더 오래 보유하여 하위 트랜잭션 문제를 악화시킬 수 있습니다. Performance Insights 지표를 모니터링하고 유휴 트랜잭션을 자동으로 종료하도록 `idle_in_transaction_session_timeout` 파라미터를 구성합니다.
+ Performance Insights 지표 모니터링 - `idle_in_transaction_count`(트랜잭션 상태에서 유휴 상태인 세션 수) 및 `idle_in_transaction_max_time`(가장 오래 실행되는 유휴 트랜잭션 기간)을 포함한 지표를 추적하여 장기 실행 트랜잭션을 감지합니다.
+ `idle_in_transaction_session_timeout` 구성 - 지정된 기간 후에 유휴 트랜잭션을 자동으로 종료하도록 파라미터 그룹에서이 파라미터를 설정합니다.
+ 선제적 모니터링 - `LWLock:SubtransBuffer` 및 `LWLock:SubtransSLRU`의 높은 발생을 모니터링하고 이벤트가 중요해지기 전에 하위 트랜잭션 관련 경합을 감지할 때까지 기다립니다.

# Timeout:PgSleep
<a name="wait-event.timeoutpgsleep"></a>

`Timeout:PgSleep` 이벤트는 서버 프로세스가 `pg_sleep` 함수를 가리키고 및 절전 시간 초과가 만료될 때까지 대기할 때 발생합니다.

**Topics**
+ [지원되는 엔진 버전](#wait-event.timeoutpgsleep.context.supported)
+ [대기 증가의 가능한 원인](#wait-event.timeoutpgsleep.causes)
+ [작업](#wait-event.timeoutpgsleep.actions)

## 지원되는 엔진 버전
<a name="wait-event.timeoutpgsleep.context.supported"></a>

이 대기 이벤트 정보는 모든 RDS for PostgreSQL 버전에서 지원됩니다.

## 대기 증가의 가능한 원인
<a name="wait-event.timeoutpgsleep.causes"></a>

이 대기 이벤트는 애플리리케이션, 저장된 함수 또는 사용자가 다음 함수 중 하나를 호출하는 SQL 문을 실행할 때 발생합니다.
+ `pg_sleep`
+ `pg_sleep_for`
+ `pg_sleep_until`

위의 함수는 지정된 시간(초)이 경과할 때까지 실행을 지연시킵니다. 예를 들어, `SELECT pg_sleep(1)`은 1초 동안 일시 중지됩니다. 자세한 내용은 PostgreSQL 설명서의 [실행 지연](https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-DELAY)을 참조하세요.

## 작업
<a name="wait-event.timeoutpgsleep.actions"></a>

`pg_sleep` 함수를 실행 중인 명령문을 식별합니다. 함수의 사용이 적절한지 확인합니다.

# Timeout:VacuumDelay
<a name="wait-event.timeoutvacuumdelay"></a>

`Timeout:VacuumDelay` 이벤트는 vacuum I/O의 비용 한도가 초과되었고 vacuum 프로세스가 절전 모드로 전환되었음을 나타냅니다. vacuum 작업은 해당 비용 지연 파라미터에 지정된 기간 동안 중지되었다가 작업을 재개합니다. 수동 vacuum 명령의 경우 `vacuum_cost_delay` 파라미터에 지연이 지정됩니다. autovacuum 대몬(deamon)의 경우 `autovacuum_vacuum_cost_delay parameter.`에 지연이 지정됩니다.

**Topics**
+ [지원되는 엔진 버전](#wait-event.timeoutvacuumdelay.context.supported)
+ [컨텍스트](#wait-event.timeoutvacuumdelay.context)
+ [대기 증가의 가능한 원인](#wait-event.timeoutvacuumdelay.causes)
+ [작업](#wait-event.timeoutvacuumdelay.actions)

## 지원되는 엔진 버전
<a name="wait-event.timeoutvacuumdelay.context.supported"></a>

이 대기 이벤트 정보는 모든 RDS for PostgreSQL 버전에서 지원됩니다.

## 컨텍스트
<a name="wait-event.timeoutvacuumdelay.context"></a>

PostgreSQL에는 autovacuum 대몬(daemon)과 수동 vacuum 명령이 모두 있습니다. RDS for PostgreSQL DB 인스턴스의 경우 autovacuum 프로세스가 기본적으로 켜져 있습니다. 수동 vacuum 명령은 필요에 따라 사용됩니다(예: 죽은 튜플의 테이블을 지우거나 새 통계를 생성하기 위해).

vacuum이 진행 중일 때 PostgreSQL은 시스템이 다양한 I/O 작업을 수행할 때의 예상 비용을 내부 카운터를 사용하여 추적합니다. 카운터가 비용 한도 파라미터에 지정된 값에 도달하면 작업을 수행하는 프로세스는 비용 지연 파라미터에 지정된 짧은 기간 동안 절전 상태로 전환됩니다. 그런 다음 카운터를 재설정하고 작업을 계속합니다.

vacuum 프로세스에는 리소스 소비 조절에 사용할 수 있는 파라미터가 있습니다. autovacuum과 수동 vacuum 명령에는 비용 한도 값을 설정하기 위한 자체 파라미터가 있습니다. 또한 비용 지연, 즉 한도에 도달했을 때 vacuum을 절전 상태로 전환하는 데 걸리는 시간을 지정하는 자체 파라미터도 있습니다. 비용 지연 파라미터는 이렇게 리소스 소비 제한 메커니즘으로 작동합니다. 다음 목록에서 이러한 파라미터에 대한 설명을 찾을 수 있습니다.

**autovacuum 대몬(daemon)의 제한에 영향을 주는 파라미터**
+ `[autovacuum\$1vacuum\$1cost\$1limit](https://www.postgresql.org/docs/current/static/runtime-config-autovacuum.html#GUC-AUTOVACUUM-VACUUM-COST-LIMIT)` - 자동 vacuum 작업에 사용할 비용 한도 값을 지정합니다. 이 파라미터의 설정을 늘리면 vacuum 프로세스가 더 많은 리소스를 사용할 수 있고 `Timeout:VacuumDelay` 대기 이벤트가 줄어듭니다.
+ `[autovacuum\$1vacuum\$1cost\$1delay](https://www.postgresql.org/docs/current/static/runtime-config-autovacuum.html#GUC-AUTOVACUUM-VACUUM-COST-DELAY)` - 자동 vacuum 작업에 사용할 비용 지연 값을 지정합니다. 기본값은 2밀리초입니다. 지연 파라미터를 0으로 설정하면 제한 메커니즘이 해제되므로 `Timeout:VacuumDelay` 대기 이벤트가 나타나지 않습니다.

자세한 내용은 PostgreSQL 설명서의 [자동 Vacuuming](https://www.postgresql.org/docs/current/runtime-config-autovacuum.html#GUC-AUTOVACUUM-VACUUM-COST-DELAY)을 참조하세요.

**수동 vacuum 프로세스의 제한에 영향을 미치는 파라미터**
+ `vacuum_cost_limit` - vacuum 프로세스가 절전 상태로 전환되는 임계값입니다. 기본적으로 한도는 200입니다. 이 숫자는 다양한 리소스에 필요한 추가 I/O의 누적 예상 비용을 나타냅니다. 이 값을 늘리면 `Timeout:VacuumDelay` 대기 이벤트 수가 줄어듭니다.
+ `vacuum_cost_delay` - vacuum 비용 한도에 도달했을 때 vacuum 프로세스가 절전 상태가 되는 시간입니다. 기본 설정은 0이며, 이는 이 기능이 해제되어 있음을 의미합니다. 이 값을 정수 값으로 설정하면 이 기능을 켜는 데 걸리는 시간을 밀리초 단위로 지정할 수 있지만 기본 설정을 그대로 두는 것이 좋습니다.

`vacuum_cost_delay` 파라미터에 대한 자세한 내용은 PostgreSQL 설명서에서 [리소스 소비](https://www.postgresql.org/docs/current/runtime-config-resource.html#RUNTIME-CONFIG-RESOURCE-VACUUM-COST)를 참조하세요.

RDS for PostgreSQL에서 autovacuum을 구성하고 사용하는 자세한 방법은 [Amazon RDS for PostgreSQL에서 PostgreSQL 자동 정리 사용](Appendix.PostgreSQL.CommonDBATasks.Autovacuum.md)을 참조하세요.

## 대기 증가의 가능한 원인
<a name="wait-event.timeoutvacuumdelay.causes"></a>

`Timeout:VacuumDelay`는 vacuum의 절전 시간을 제어하는 비용 한도 파라미터 설정(`vacuum_cost_limit`, `autovacuum_vacuum_cost_limit`)과 비용 지연 파라미터(`vacuum_cost_delay`, `autovacuum_vacuum_cost_delay`) 간의 균형에 영향을 받습니다. 비용 한도 파라미터 값을 높이면 vacuum이 절전 상태로 전환되기 전에 더 많은 리소스를 사용할 수 있습니다. 그러면 `Timeout:VacuumDelay` 대기 이벤트가 줄어듭니다. 지연 파라미터 중 하나를 늘리면 `Timeout:VacuumDelay` 대기 이벤트가 더 자주, 더 오랜 시간 동안 발생합니다.

`autovacuum_max_workers` 파라미터 설정은 `Timeout:VacuumDelay`의 수도 늘릴 수 있습니다. 각각의 추가 autovacuum 작업자 프로세스는 내부 카운터 메커니즘에 기여하므로 단일 autovacuum 작업자 프로세스를 사용할 때보다 한도에 더 빨리 도달할 수 있습니다. 비용 한도에 더 빨리 도달할수록 비용 지연이 더 자주 적용되어 `Timeout:VacuumDelay` 대기 이벤트가 더 많이 발생합니다. 자세한 내용은 PostgreSQL 설명서의 [autovacuum\$1max\$1workers](https://www.postgresql.org/docs/current/runtime-config-autovacuum.html#GUC-AUTOVACUUM-MAX-WORKERS)를 참조하세요.

큰 객체(예: 500GB 이상)도 이 대기 이벤트를 발생시킬 수 있습니다. vacuum이 큰 객체의 처리를 완료하는 데 시간이 걸릴 수 있기 때문입니다.

## 작업
<a name="wait-event.timeoutvacuumdelay.actions"></a>

vacuum 작업이 예상대로 완료되면 문제 해결이 필요하지 않습니다. 즉, 이 대기 이벤트가 반드시 문제를 나타내는 것은 아닙니다. 이는 완료해야 하는 다른 프로세스에 리소스를 적용할 수 있도록 지연 파라미터에 지정된 시간 동안 vacuum이 절전 상태로 전환되고 있음을 나타냅니다.

vacuum 작업을 더 빨리 완료하려면 지연 파라미터를 낮추면 됩니다. 이렇게 하면 vacuum의 절전 시간이 단축됩니다.