View a markdown version of this page

Aurora PostgreSQL에서 REPLICA IDENTITY FULL의 성능 문제 방지 - Amazon Aurora

Aurora PostgreSQL에서 REPLICA IDENTITY FULL의 성능 문제 방지

PostgreSQL 논리적 복제를 사용하려면 구독자가 업데이트하거나 삭제할 올바른 행을 찾을 수 있도록 게시된 각 테이블에 복제본 ID가 있어야 합니다. 기본적으로 프라이머리 키는 복제본 ID 역할을 합니다. 테이블에 프라이머리 키 또는 적절한 고유 인덱스가 없는 경우 복제본 ID를 FULL로 설정하여 PostgreSQL이 전체 행을 키로 사용하도록 할 수 있습니다.

REPLICA IDENTITY FULL은 프라이머리 키 없이 테이블을 복제하는 즉각적인 문제를 해결하지만 게시자와 구독자 모두에게 심각한 성능 문제를 일으킬 수 있습니다. 블루/그린 배포와 같이 내부적으로 논리적 복제에 의존하는 기능을 포함하여 Aurora PostgreSQL에서 논리적 복제를 사용하는 모든 사용자에게 이러한 영향을 이해하는 것이 중요합니다.

REPLICA IDENTITY FULL로 인해 문제가 발생하는 이유

게시자의 WAL 볼륨 증가

REPLICA IDENTITY 설정은 PostgreSQL이 미리 쓰기 로그(WAL)에 쓰는 정보를 제어하여 업데이트되거나 삭제된 행을 식별합니다. 기본 복제본 ID(프라이머리 키)에서는 키 열만 이전 행 ID로 로깅됩니다. FULL을 사용하면 PostgreSQL은 각 UPDATEDELETE에 대해 모든 열의 이전 값을 기록합니다. 이로 인해 다음과 같은 여러 결과가 발생합니다.

  • WAL 크기가 크게 증가합니다. 업데이트의 경우 모든 열의 이전 값과 새 값이 모두 기록되므로 각 WAL 레코드의 크기가 거의 두 배로 늘어납니다. 테이블에 TOAST를 사용하여 저장된 큰 값이 포함된 경우 업데이트에 의해 수정되지 않은 경우에도 TOASTed 값을 가져와서 WAL에 기록해야 하므로 증가량이 훨씬 더 클 수 있습니다.

  • 게시자의 I/O 및 CPU 사용량이 더 높습니다. 추가 WAL 쓰기는 특히 쓰기 작업이 많은 워크로드의 경우 더 많은 디스크 I/O 대역폭과 CPU 주기를 소비합니다.

  • 구독자에게 더 많은 데이터가 전송됩니다. 게시자는 네트워크를 통해 더 큰 WAL 레코드를 각 구독자에게 전송하여 대역폭 소비를 늘려야 합니다.

구독자의 느린 행 조회

구독자가 UPDATE 또는 DELETE 로그 레코드를 수신하면 테이블의 로컬 복사본에서 일치하는 행을 찾아야 합니다. REPLICA IDENTITY FULL을 사용하면 구독자는 이전 행 이미지의 모든 열 값과 일치하는 행을 검색합니다.

PostgreSQL이 이 검색을 수행하는 방법은 PostgreSQL 메이저 버전에 따라 다릅니다.

  • PostgreSQL 16 이전: 테이블에 프라이머리 키가 없고 명시적으로 구성된 복제본 ID 인덱스가 없는 경우 구독자는 모든 단일 UPDATE 또는 DELETE 작업에 대해 전체 테이블을 순차적으로 스캔합니다. 큰 테이블에서는 이렇게 하면 적용 성능이 매우 느려집니다.

  • PostgreSQL 16 이상: 인덱스가 복제본 ID로 명시적으로 설정되지 않은 경우에도 구독자는 행 조회에 btree 또는 해시 인덱스를 사용할 수 있습니다. 그러나 구독자는 어떤 인덱스가 가장 효율적인지 평가하지 않습니다. 버전 16부터 PostgreSQL은 찾은 첫 번째 적합한 인덱스를 선택하며 사용자는 이 선택을 제어할 수 없습니다. 선택한 인덱스의 선택도가 낮은 경우(예: 부울 또는 상태 열의 인덱스) 행 조회는 순차 스캔만큼 느릴 수 있습니다. 따라서 REPLICA IDENTITY FULL에서 암시적 인덱스 선택에 의존하는 것은 신뢰할 수 없으며 권장 구성이 아닌 폴백으로 간주해야 합니다.

REPLICA IDENTITY FULL이 복제 지연을 유발하는 방법

게시자의 더 큰 WAL과 구독자의 더 느린 행 조회라는 두 가지 문제가 결합되어 복제 지연이 발생합니다.

기본적으로 PostgreSQL 논리적 복제는 구독당 단일 적용 작업자 프로세스를 사용하여 게시자로부터 변경 사항을 수신하고 구독자의 테이블에 적용합니다. 적용 작업자는 한 번에 한 행씩 순차적으로 변경 사항을 커밋 순서로 처리합니다. 즉, 구독자의 처리량은 각 개별 변경 사항을 얼마나 빨리 적용할 수 있는지에 따라 제한됩니다.

REPLICA IDENTITY FULL이 적절한 인덱스가 없는 테이블에 설정된 경우 UPDATEDELETE마다 일치하는 행을 찾기 위해 전체 테이블을 순차적으로 스캔해야 합니다. 테이블에 수백만 개의 행이 있는 경우 이러한 각 작업은 몇 초 이상 걸릴 수 있습니다. 그 결과 계단식 문제가 발생합니다.

  1. 게시자는 구독자가 적용할 수 있는 것보다 더 빠르게 변경 사항을 생성합니다. 게시자의 쓰기 워크로드는 정상 속도로 지속되지만 구독자의 적용 작업자는 각 행 조회에 대해 순차적 스캔 또는 선택도가 낮은 인덱스에서 병목 현상이 발생합니다.

  2. WAL은 게시자에 누적되며 스토리지를 소진할 수 있습니다. PostgreSQL은 구독자가 WAL 세그먼트를 적용했음을 확인할 때까지 WAL 세그먼트를 회수할 수 없습니다. 구독자가 더 뒤처지면 게시자는 디스크에 WAL을 누적합니다. Aurora PostgreSQL에서 이는 CloudWatch에서 OldestReplicationSlotLag가 증가하는 것으로 나타납니다. 심각한 경우 사용 가능한 모든 스토리지를 사용하고 게시자가 쓰기 수락을 중지할 수 있습니다.

  3. 지연은 자기 강화적입니다. 구독자가 뒤처지면 구독자의 테이블이 복제된 삽입에서 계속 증가하여 각 순차 스캔이 더 느려집니다. 개입 없이 지연은 제한 없이 증가합니다.

이 문제는 자주 UPDATE 또는 DELETE 작업을 받는 테이블의 경우 특히 심각합니다. 구독자에 대한 행 조회가 필요하지 않으므로 INSERT 작업은 영향을 받지 않습니다.

참고

PostgreSQL 16부터 적용 작업자는 대규모 스트리밍 트랜잭션에 병렬 적용을 사용할 수 있으므로 처리량에 도움이 될 수 있습니다. 그러나 인덱스가 없는 REPLICA IDENTITY FULL의 기본 행 조회 병목 현상은 남아 있습니다. 각 개별 행을 찾으려면 여전히 스캔이 필요하기 때문입니다.

블루/그린 배포에 미치는 영향

Amazon Aurora의 블루/그린 배포는 데이터베이스당 단일 구독을 설정하여 그린 환경을 블루 환경과 동기화된 상태로 유지하기 위해 내부적으로 논리적 복제를 사용합니다. 그린 환경의 논리적 복제 적용 프로세스는 단일 스레드입니다. 단일 적용 작업자 프로세스는 블루 환경에서 모든 변경 사항을 수신하고 한 번에 하나씩 커밋 순서로 적용합니다. 블루/그린 복제 경로에는 병렬이 적용되지 않습니다.

이 단일 스레드 설계는 블루 환경을 따라잡는 그린 환경의 기능은 전적으로 적용 작업자가 각 개별 변경을 처리할 수 있는 속도에 달려 있음을 의미합니다. 테이블이 프라이머리 키 또는 적절한 인덱스 없이 REPLICA IDENTITY FULL을 사용하는 경우 적용 작업자에 미치는 영향은 PostgreSQL 버전에 따라 달라집니다. 16 이전 버전에서는 해당 테이블의 모든 UPDATEDELETE에서 적용 작업자가 전체 테이블을 순차적으로 스캔하여 일치하는 행을 찾도록 강제합니다. 버전 16 이상에서 PostgreSQL은 적절한 인덱스를 사용할 수 있는 경우 적절한 인덱스를 사용하지만, 적격 인덱스가 없는 경우에도 적용 작업자는 여전히 순차적 스캔으로 돌아갑니다. 적용 작업자가 한 행에 대해 큰 테이블을 스캔하는 동안 모든 테이블에서 대기 중인 다른 모든 변경 사항은 대기 중입니다.

블루/그린 배포의 결과는 중요합니다.

  • 복제 지연은 지속적으로 증가합니다. 블루 환경이 단일 적용 작업자가 처리할 수 있는 것보다 더 빠르게 쓰기 트래픽을 생성하는 경우 그린 환경은 점점 더 뒤쳐집니다. 적용 작업자는 단일 스레드이므로 캐치업을 병렬화할 방법이 없습니다.

  • 전환을 차단할 수 있습니다. 블루/그린 전환을 수행하려면 그린 환경을 블루 환경과 완전히 동기화해야 합니다. 복제 지연이 너무 높으면 제한 시간 내에 전환을 완료할 수 없습니다.

  • 그린 환경은 절대 따라잡지 못할 수 있습니다. REPLICA IDENTITY FULL을 사용하고 인덱스가 없는 대용량 테이블이 있는 쓰기 중심 워크로드의 경우 적용 속도가 너무 느려서 그린 환경이 영구적으로 뒤처져 복제본 ID 구성을 먼저 해결하지 않으면 전환이 불가능해질 수 있습니다.

  • WAL은 블루 환경에 누적됩니다. 그린 환경이 뒤쳐져 있는 동안 블루 환경은 복제 슬롯에 대한 WAL 세그먼트를 유지합니다. 이렇게 하면 블루(프로덕션) 환경에서 스토리지 사용량이 증가하고 프로덕션 성능에 영향을 미칠 수 있습니다.

이러한 문제를 방지하려면 블루/그린 배포를 생성하기 전에 모든 테이블에 ALTER TABLE ... REPLICA IDENTITY USING INDEX를 사용하여 복제본 ID로 명시적으로 구성된 프라이머리 키 또는 적절한 고유 인덱스가 있는지 확인합니다. PostgreSQL 16 이상에서 암시적 인덱스를 선택할 때 REPLICA IDENTITY FULL에 의존하지 마세요. 구독자가 선택이 잘못된 인덱스를 선택하거나 순차 스캔으로 돌아갈 수 있기 때문입니다. 대표적인 쓰기 워크로드로 배포를 테스트하여 그린 환경이 따라갈 수 있는지 확인합니다.

블루/그린 배포 제한 사항에 대한 자세한 내용은 Amazon Aurora 블루/그린 배포 관련 제한 사항 및 고려 사항 섹션을 참조하세요. 모범 사례는 블루/그린 배포 모범 사례에 대한 Aurora PostgreSQL 모범 사례 섹션을 참조하세요.

REPLICA IDENTITY FULL을 사용하여 테이블을 식별하는 방법

다음 쿼리를 실행하여 REPLICA IDENTITY FULL에서 모든 테이블을 찾습니다.

SELECT n.nspname AS schema, c.relname AS table_name, c.relreplident FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind = 'r' AND c.relreplident = 'f' AND n.nspname NOT IN ('pg_catalog', 'information_schema') ORDER BY n.nspname, c.relname;

relreplident 열 값은 다음과 같습니다.

  • d - 기본값(프라이머리 키)

  • n — 없음

  • f - 전체(전체 행)

  • i - 특정 인덱스

해결 방법 및 모범 사례

가능한 경우 프라이머리 키 추가

가장 효과적인 솔루션은 프라이머리 키가 없는 테이블에 프라이머리 키를 추가하는 것입니다. 프라이머리 키가 있는 경우 PostgreSQL은 이를 기본 복제본 ID로 사용하여 구독자에 대한 효율적인 행 조회를 제공하고 게시자의 WAL 오버헤드를 최소화합니다.

ALTER TABLE my_table ADD COLUMN id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY;
중요

기본값 표현식은 휘발성인 nextval()를 사용하기 때문에 이 문은 ACCESS EXCLUSIVE 잠금을 획득하고 전체 테이블을 다시 작성합니다. 테이블에 대한 모든 읽기 및 쓰기는 재작성 기간 동안 차단됩니다. 대형 테이블의 경우 이로 인해 상당한 가동 중지 시간이 발생할 수 있습니다. 유지 관리 기간 동안 이 변경을 계획하거나 먼저 열을 null로 생성한 다음 별도의 단계에서 제약 조건을 채우고 추가하는 등의 대체 접근 방식을 고려합니다.

애플리케이션 제약으로 인해 프라이머리 키를 추가할 수 없는 경우 NOT NULL 열 세트에 고유 인덱스를 추가하고 이를 복제본 ID로 설정하는 것이 좋습니다.

CREATE UNIQUE INDEX my_table_replica_idx ON my_table (col1, col2); ALTER TABLE my_table REPLICA IDENTITY USING INDEX my_table_replica_idx;
참고

인덱스가 빌드되는 동안 쓰기가 차단되지 않도록 하려면 CONCURRENTLY 절인 CREATE UNIQUE INDEX CONCURRENTLY my_table_replica_idx ON my_table (col1, col2);를 사용합니다.

참고

복제본 ID에 사용되는 인덱스는 고유해야 하고, 일부가 아니어야 하며, 연기할 수 없어야 하고, NOT NULL 제약 조건이 있는 열만 포함해야 합니다.

암시적 인덱스 선택에 의존하지 마세요(PostgreSQL 16 이상).

PostgreSQL 16부터 구독자의 적용 작업자는 복제본 ID가 FULL로 설정된 경우 해당 인덱스가 복제본 ID로 명시적으로 구성되지 않은 경우에도 행 조회에 btree 또는 해시 인덱스를 사용할 수 있습니다. 이렇게 하면 경우에 따라 순차적 스캔을 방지할 수 있지만 다음과 같은 이유로 이러한 암시적 동작에 의존하는 것이 안티 패턴입니다.

  • 선택한 인덱스는 제어하지 않습니다. PostgreSQL은 가장 선택적이거나 효율적인 인덱스가 아니라 카탈로그 순서로 찾은 첫 번째 적격 인덱스를 선택합니다. 테이블에 적격 인덱스가 여러 개 있는 경우 선택한 인덱스의 선택도가 낮아 조회 성능이 저하될 수 있습니다.

  • 동작은 취약합니다. 인덱스를 추가, 삭제 또는 재구축하면 적용 작업자가 사용하는 인덱스가 변경되어 복제 시 예기치 않은 성능 회귀가 발생할 수 있습니다.

  • 기본 문제를 마스킹합니다. 프라이머리 키 또는 명시적 복제본 ID가 없는 테이블은 본질적으로 논리적 복제에 위험합니다. 암시적 인덱스 선택에 의존하면 문제가 해결되지 않습니다.

대신 복제된 모든 테이블에 대해 복제본 ID를 명시적으로 구성합니다.

  • 가장 좋은 옵션: 프라이머리 키를 추가합니다. 가장 안정적이고 효율적인 복제본 ID입니다.

  • 대안: NOT NULL 열만 있는 특정 고유하고 부분적이지 않으며 지연 불가능한 인덱스를 지정하는 데 ALTER TABLE ... REPLICA IDENTITY USING INDEX를 사용합니다. 이를 통해 행 식별에 사용되는 열을 명시적으로 제어할 수 있습니다.

두 옵션 모두 사용할 수 없는 테이블에 대해서만 REPLICA IDENTITY FULL을 예약하고 성능이 직접 제어할 수 없는 요인에 따라 달라진다는 점을 이해합니다.

복제 지연 모니터링

REPLICA IDENTITY FULL을 사용하는 경우 복제 지연을 면밀히 모니터링하여 구독자가 심각해지기 전에 속도 저하를 감지합니다.

게시자에서 현재 WAL 위치와 구독자가 확인한 내용 간의 지연을 확인합니다.

SELECT slot_name, confirmed_flush_lsn, pg_current_wal_lsn(), (pg_current_wal_lsn() - confirmed_flush_lsn) AS lag_bytes FROM pg_replication_slots WHERE slot_type = 'logical';

lag_bytes 값이 꾸준히 증가하면 구독자가 뒤처지고 있음을 나타냅니다. pg_stat_replication_slots 뷰는 각 복제 슬롯의 사용량에 대한 추가 통계를 제공합니다.

구독자에서 pg_stat_subscription 뷰는 수신 및 보고된 마지막 WAL 위치를 포함하여 각 적용 작업자의 상태를 표시합니다.

SELECT subname, received_lsn, latest_end_lsn, last_msg_send_time, last_msg_receipt_time FROM pg_stat_subscription;
참고

PostgreSQL 16 이상에서는 기본 적용 작업자와 병렬 적용 작업자를 구분하도록 worker_type을 선택할 수도 있습니다.

received_lsnlatest_end_lsn 사이의 큰 간격 또는 last_msg_send_time의 기한 경과 타임스탬프는 적용 작업자가 따라잡기 위해 어려움을 겪고 있음을 나타낼 수 있습니다. 또한 pg_stat_subscription_stats 뷰는 지연에 영향을 미칠 수 있는 적용 오류 및 충돌을 추적합니다.

Aurora PostgreSQL의 경우 복제 슬롯에서 가장 뒤쳐진 슬롯의 바이트 지연을 추적하는 OldestReplicationSlotLag CloudWatch 지표를 모니터링할 수도 있습니다. 값 상승은 복제 지연의 초기 경고 신호입니다. 자세한 내용은 Aurora PostgreSQL 논리적 복제를 위한 라이트-스루 캐시 및 논리적 슬롯 모니터링 단원을 참조하세요.

적용 중에 최적화되지 않은 인덱스를 사용할 수 있는 테이블 확인

구독자는 적용 작업자가 과도한 힙 읽기를 수행하는 테이블을 식별할 수 있습니다. 이는 적용 중에 테이블에 행 조회에 대한 효율적인 인덱스가 없음을 나타낼 수 있습니다. 구독자에 대해 다음 쿼리를 실행합니다.

SELECT relname, heap_blks_read, heap_blks_hit, idx_blks_read, idx_blks_hit, heap_blks_read + heap_blks_hit AS total_heap_access FROM pg_statio_user_tables WHERE heap_blks_read > 0 ORDER BY heap_blks_read DESC LIMIT 10;

idx_blks_read에 비해 heap_blks_read 값이 높은 테이블은 적용 작업자가 효율적인 인덱스를 사용하여 UPDATEDELETE 작업에 대한 행을 찾지 못하고 있음을 나타낼 수 있습니다. 이는 REPLICA IDENTITY FULL이 사용 중일 때 복제 지연의 일반적인 원인입니다.

참고

이 쿼리를 사용하려면 구독자에서 track_counts 파라미터를 활성화해야 합니다. 이 파라미터는 기본적으로 활성화됩니다.

REPLICA IDENTITY FULL이 필요한지 평가

REPLICA IDENTITY FULL을 설정하기 전에 실제로 필요한지 여부를 고려하세요. 이를 사용하는 일반적인 이유는 다음과 같습니다.

  • 테이블에는 프라이머리 키 또는 고유 인덱스가 없습니다.

  • 변경 데이터 캡처(CDC) 소비자를 위한 행의 전체 사전 이미지가 필요합니다.

  • 해당 열을 수정하지 않는 업데이트의 경우 복제 이벤트에 TOASTed 열 값이 포함되어야 합니다.

프라이머리 키가 없어서 유일한 이유가 있다면 거의 항상 프라이머리 키를 추가하는 것이 더 나은 경로입니다. CDC에 대한 전체 사전 이미지가 필요한 경우 CDC 소비자가 상태를 외부로 유지하여 전체 행을 재구성할 수 있는지 여부를 고려하면 REPLICA IDENTITY FULL의 WAL 및 구독자 오버헤드를 피할 수 있습니다.

권장 사항 요약

시나리오 권장 사항
테이블에 프라이머리 키가 있음 기본 복제본 ID 사용(작업 필요 없음)
테이블에 고유한 NOT NULL 인덱스가 있음 ALTER TABLE ... REPLICA IDENTITY USING INDEX를 사용하여 해당 인덱스를 복제본 ID로 설정
테이블에 적합한 키가 없음(PostgreSQL 16 이상) 프라이머리 키 또는 고유 인덱스를 추가합니다. 암시적 인덱스 선택과 함께 REPLICA IDENTITY FULL을 사용하는 것은 신뢰할 수 없으며 최후의 수단이어야 합니다.
테이블에 적합한 키가 없음(PostgreSQL 16 이전) 프라이머리 키 또는 고유 인덱스를 추가하고 가능하면 REPLICA IDENTITY FULL은 피합니다.
대용량/TOASTed 열이 있는 쓰기 작업이 많은 워크로드 WAL 볼륨 증폭으로 인한 REPLICA IDENTITY FULL 방지