TECH 으로 돌아가기
TECH HACKER NEWS 어제 8분 읽기 31 READS

"Postgres는 느리지 않다, 스토리지가 느릴 뿐" — NVMe vs EBS 벤치마크가 남긴 것

"Postgres는 느리지 않다, 스토리지가 느릴 뿐" — NVMe vs EBS 벤치마크가 남긴 것
SOURCE IMAGE · HACKER NEWS

대규모로 PostgreSQL을 운영하다 보면 원인을 특정하기 어려운 성능 저하를 자주 만난다. 적재(ingestion)가 느려지고, P95 읽기 지연이 들쭉날쭉해지며, 오토배큠이 밀리고, 체크포인트가 실제 워크로드와 자원을 다투고, 논리 복제 지연이 쌓인다. POSETTE 2026에서 Sai Srirampur가 발표한 "Postgres Isn't Slow. Your Storage Is."는 이렇게 서로 무관해 보이는 증상들이 사실 하나의 공통 원인, 즉 스토리지 성능에서 비롯될 수 있다고 지적한다. 이 글은 그 발표에서 제시된 벤치마크와 프로덕션 아키텍처를 실무 관점에서 정리한 것이다.

증상은 다섯 가지, 병목은 하나

발표가 지목한 전형적 증상은 다섯 가지다. UPDATE·UPSERT가 많은 적재 파이프라인이 수초에서 수분으로 늘어지는 현상, 대부분 빠르지만 일부 쿼리만 밀리초에서 초 단위로 튀어 P95가 요동치는 현상, 처리량 증가로 오토배큠이 밀리며 테이블 블로트가 기가바이트에서 테라바이트로 누적되는 현상, 체크포인트가 길어지며 클라이언트 워크로드와 I/O·CPU를 다투는 현상, 그리고 Kafka나 ClickHouse로 스트리밍할 때 WAL 디코딩이 느려지고 복제 슬롯이 커지며 지연이 복합적으로 쌓이는 현상이다.

이들의 공통 배경은 작업 집합(working set)이 메모리와 OS 페이지 캐시를 넘어서는 순간부터 물리 I/O가 늘어난다는 점이다. 느린 스토리지에서는 그 읽기 비용과 큐 대기가 평균 지연과 꼬리 지연을 함께 끌어올린다. 게다가 클라이언트 쿼리와 배경 유지보수(힙·인덱스 쓰기, WAL 동기화, 체크포인트, VACUUM, 논리 디코딩 스필)가 동일한 대역폭·IOPS·큐 용량을 나눠 쓴다. 스토리지가 한계에 닿으면 작업들이 서로를 지연시키기 시작하고, 이것이 겉보기엔 무관한 여러 문제의 뿌리가 된다.

같은 워크로드, 스토리지만 바꿨을 때

저자는 변수를 스토리지로 좁히기 위해 동일한 조건의 클러스터 8개를 구성했다. 4개는 인스턴스 스토어 NVMe, 4개는 3,000 IOPS 기준선의 gp3 EBS였고, 각각 m6id.4xlarge(16 vCPU, 64 GiB RAM, shared_buffers 16 GiB) 위에서 돌았다. 데이터는 33억 행, 482 GiB 규모의 pgbench_accounts 힙이며, 64개 클라이언트와 16개 워커로 5분간 동일한 고동시성 랜덤 UPDATE를 실행했다. 두 그룹의 유일한 차이는 스토리지 등급이었다.

결과는 중앙값 기준 NVMe 16,030 TPS 대 EBS 1,734 TPS로 이 테스트에서 9.24배 차이가 났고, 트랜잭션 지연 중앙값은 36.9ms에서 4.0ms로 떨어졌다. 10GB 블로트에 대한 VACUUM은 EBS 964초에서 NVMe 366초로, 10GB 논리 복제 백로그 해소는 238초에서 139초로 줄었다. 다만 저자는 이 수치가 해당 워크로드와 해당 스토리지 구성에 한정된다고 분명히 못 박았다. EBS 쪽은 기준선 3,000 IOPS의 gp3였고, 다른 EBS 구성이라면 결과도 달라질 가능성이 크다.

지연은 CPU가 아니라 대기에서 왔다

37ms에 이르는 EBS 트랜잭션 지연을 분해하면 병목의 위치가 드러난다. 페이지 읽기가 EBS에서 약 19ms(NVMe 0.3ms), 커밋 시 WAL fsync가 11ms(NVMe 1.5ms), 락·스케줄러 오버헤드가 약 5ms(NVMe 0.2ms)를 차지했고, 정작 CPU 연산 자체는 양쪽 모두 약 2ms로 비슷했다. 추가 지연의 대부분이 PostgreSQL이 더 많은 연산을 해서가 아니라 스토리지를 기다린 데서 나왔다는 뜻이다.

실행 중 pg_stat_activity 스냅숏도 같은 그림을 보여준다. 64개 백엔드 중 EBS는 29개가 IO:DataFileRead를 대기(약 45%)했지만 NVMe는 9개(약 14%)에 그쳤다. CPU 프로파일은 더 역설적이다. 약 240초 구간에서 NVMe 호스트는 2,253 CPU-초(약 9.4코어 상당)를 쌓은 반면 EBS는 251 CPU-초(약 1코어)에 불과했다. EBS 프로세스가 스토리지를 기다리며 CPU 바깥에서 보낸 시간이 그만큼 길었다는 의미다. 로컬 NVMe의 핵심은 I/O를 없애는 것이 아니라 그 비용을 밀리초에서 수십 마이크로초 수준으로 낮춰, 캐시 미스·WAL 플러시·유지보수 I/O가 여전히 발생하더라도 시스템 전체에 주는 충격을 줄이는 데 있다.

휘발성 디스크를 프로덕션에서 쓰는 법

명백한 반론은 인스턴스 스토어 NVMe가 머신 수명에 묶여 있어 노드가 사라지면 로컬 데이터도 사라진다는 점이다. 스냅숏을 EBS API로 뜰 수 없고, 가용 용량도 인스턴스 타입에 의존한다. 발표의 답은 내구성을 개별 Postgres 노드가 아니라 클러스터 수준에서 제공하면 된다는 것이다. 이를 위해 세 가지를 요구한다. 노드 손실에서 살아남도록 복제하고, 복구 데이터를 머신 바깥에 유지하며, 인스턴스별 NVMe 용량에 맞춰 용량을 계획한다.

구체적으로는 두 개의 대기 후보를 둔 쿼럼 기반 동기 복제다. ANY 1 (standby1, standby2) 같은 구성이면 둘 중 하나만 WAL을 내구적으로 확인해도 커밋할 수 있어 느린 쪽을 기다리지 않고, 노드를 가용 영역(AZ)에 분산하면 AZ 장애에도 대비된다. 노드 바깥 내구성은 WAL-G 같은 오픈소스로 물리 베이스 백업과 연속 WAL 아카이빙을 제공하며, 낮은 지연의 WAL 전송으로 초 단위 RPO와 보존 기간 내 특정 시점 복구(PITR)를 노릴 수 있다. 백업과 WAL은 별도 장애 도메인에, 필요하면 다른 리전 사본까지 둔다. 대기 노드는 빠른 페일오버를, 백업은 손상·운영 실수·클러스터 전체 장애를 막는 서로 다른 역할을 하므로 두 복구 경로 모두 정기적으로 검증해야 한다. 발표는 Instacart와 Datadog이 각각 지연에 민감한 워크로드에서 로컬 NVMe PostgreSQL을 사용한 공개 사례를 인용한다.

제목보다 결론은 좁다. 로컬 NVMe는 스토리지 병목 워크로드의 I/O 패널티를 크게 줄이지만, 그 대가로 다른 운영 모델을 요구한다. 가용성은 쿼럼 복제로, 복구는 독립 저장된 베이스 백업과 아카이브 WAL로 설계해야 한다. 실무적 함의는 간단하다. PostgreSQL이 더 이상 확장되지 않는 듯 보일 때, 데이터베이스와 스토리지를 따로 보지 말고 함께 들여다보라는 것이다.

SOURCE · HACKER NEWS
원문 전체 보기 → https://clickhouse.com/blog/posette-talk-recap-postgres-isnt...
SHARE
NEXT · CHOOSE

변화를 읽었다면,
내가 만들 수익 구조를 고릅니다.

정보를 더 모으는 데서 멈추지 않고, 광고·외주·판매·중개·구독 중 내 상황에 맞는 출발점을 정해보세요.

21가지 수익 구조 살펴보기 →
처리 중...