본문으로 건너뛰기

"내부구조" 태그로 연결된 2개 게시물개의 게시물이 있습니다.

모든 태그 보기

pg_walviz WAL 시각화

· 약 3분

WAL을 공부할 때 제일 답답한 건 실체가 안 보인다는 점이었어요. record가 페이지 경계에서 쪼개진다, 세그먼트 첫 페이지에는 long header가 붙는다, 이런 문장을 문서로는 읽는데 실제 바이트가 어떻게 놓이는지는 상상에 맡겨야 했거든요.

Bertrand Drouvot(베르트랑 드루보)가 8월 13일 공개한 pg_walviz가 정확히 그 지점을 채웁니다. WAL 세그먼트 파일 하나를 읽어 브라우저에서 시각화하는 읽기 전용 도구입니다. 현재 버전은 v0.1.0-beta.1이고 저장소는 GitHub에 있습니다.

무엇을 보여주나

화면은 서로 동기화되는 네 개의 뷰로 구성됩니다.

내용
세그먼트 개요히트맵. resource manager별로 record가 세그먼트 어디에 몰려 있는지
record 조각 목록선택한 페이지에 걸친 record들, 페이지 경계에서 쪼개진 조각 포함
record 검사기선택한 record의 헤더, 물리 배치, full-page image 정보
물리 바이트색으로 구분된 원본 바이트열

히트맵에서 눈에 띄는 영역을 클릭하면 그 페이지의 record 목록이 뜨고, record를 고르면 헤더 필드와 실제 바이트가 같이 하이라이트됩니다. 페이지 번호, record 번호, 파일 오프셋, LSN을 직접 입력해서 이동할 수도 있습니다.

pg_waldump와 겹치는 도구가 아니냐는 생각이 들 수 있는데, 역할이 다릅니다. pg_waldump는 record의 논리적 내용을 사람이 읽을 텍스트로 풀어 줍니다. 어떤 rmgr가 어떤 연산을 기록했는지 보기에 좋습니다. pg_walviz는 record가 세그먼트 안에 물리적으로 어떻게 저장되는지를 보여줍니다. 페이지 헤더, record 조각, continuation record, 정렬 padding, 블록 참조가 바이트 위에 그대로 표시됩니다. 실제로 pg_walviz는 내부적으로 pg_waldump를 사용하므로 두 도구는 상하 관계에 가깝습니다.

실행 방법

PostgreSQL 서버도, 데이터 디렉터리도 필요 없습니다. 세그먼트 파일 하나와 그 파일을 만든 서버 버전에 맞는 pg_waldump 바이너리만 있으면 됩니다.

~/pg_walviz/bin/pg_walviz \
--pg-waldump /usr/pgsql-18/bin/pg_waldump \
/archive/000000010000000000000042

실행하면 로컬 웹서버가 뜨고 브라우저가 열립니다. 원격 서버에서 실행할 때는 브라우저 자동 실행을 끄고 포트를 지정합니다.

~/pg_walviz/bin/pg_walviz --no-open --port 8765 \
--pg-waldump /usr/pgsql-18/bin/pg_waldump \
/archive/000000010000000000000042

주의사항이 둘 있습니다. 현재 쓰기가 진행 중인 세그먼트는 열지 말라는 것, 그리고 WAL에는 실데이터가 들어 있으므로 로컬 밖으로 노출하지 말라는 것입니다. 특히 두 번째는 archive에서 세그먼트를 복사해 분석용 장비에서 여는 습관과 묶어 기억해 둘 만합니다. WAL은 INSERT된 행의 내용을 그대로 담고 있어서, 세그먼트 파일 하나가 곧 데이터 유출 경로가 됩니다.

어디에 쓸 만한가

첫째는 학습입니다. WAL record 헤더의 xl_prev가 무엇인지, full-page image가 왜 checkpoint 직후에 몰리는지 같은 주제는 글로 읽는 것보다 히트맵에서 직접 확인하는 쪽이 빠릅니다. checkpoint 직후 세그먼트를 열어 보면 FPI가 차지하는 공간이 시각적으로 드러나고, full_page_writes가 WAL 볼륨에 미치는 영향이 감으로 잡힙니다.

둘째는 장애 분석의 보조 도구입니다. 예전에 standby가 record with incorrect prev-link를 반복하며 멈춘 사건을 분석할 때는 pg_waldump 출력과 오프셋 계산을 손으로 맞춰 가며 recycled 세그먼트의 잔재를 추론했습니다. 그때 이 도구가 있었다면 문제 지점의 바이트를 바로 눈으로 확인했을 겁니다. 세그먼트 경계의 long page header(40바이트)와 첫 record의 위치 같은 것들이 화면에 그대로 보이니까요.

PostgreSQL 내부를 3D 도시로 만든 PGSimCity가 buffer와 프로세스를 보여주는 조감도였다면, pg_walviz는 WAL이라는 한 지점을 현미경으로 파는 도구입니다. 아직 beta라 큰 세그먼트에서 로딩이 느리고 단일 파일 검사가 권장되는 수준이지만, 방향이 좋습니다.

archive에 쌓여 있는 세그먼트 하나 골라서 열어 보세요. 문서 열 페이지보다 히트맵 한 화면이 WAL 구조를 빨리 가르쳐 줄 거예요.

참고 자료

pgrust 쿼리 엔진

· 약 4분

PostgreSQL로 분석 쿼리를 돌려 본 사람은 다들 한 번쯤 궁금해했을 거예요. 같은 데이터를 두고 왜 컬럼 스토어 엔진들과 이렇게까지 차이가 날까요. 인덱스나 통계의 문제가 아니라 더 아래층의 문제라는 감은 오는데, 그 아래층이 정확히 뭘까요.

7월 초 regression test 전체 통과 소식으로 화제가 됐던 Rust 재작성 프로젝트 pgrust가 7월 30일 v0.2를 릴리스하면서 성능 주장을 들고 나왔고, 며칠 뒤 저자가 그 성능이 어디서 나왔는지 해설한 글을 올렸습니다. 해설 글이 Hacker News 339점을 받으며 이 기간 PostgreSQL 관련 글 중 가장 크게 회자됐는데, pgrust 홍보를 걷어내고 봐도 PostgreSQL executor가 어디서 시간을 쓰는지 보여주는 교재로 훌륭합니다.

Volcano 모델: 우아함의 청구서

PostgreSQL의 executor는 Volcano 모델(iterator 모델)입니다. 실행 계획의 각 노드가 next()를 구현하고, 부모 노드가 자식에게 next()를 호출하면 행이 한 번에 하나씩 위로 올라옵니다.

이 모델의 장점은 우아함입니다. 어떤 연산자든 같은 인터페이스로 조립되고, 중간 결과를 통째로 메모리에 들 필요가 없습니다. OLTP처럼 행 몇 개를 만지는 쿼리에서는 완벽합니다.

문제는 5억 행을 집계할 때입니다. 행 하나마다 함수 호출 체인이 반복되니 호출 오버헤드가 5억 번 쌓이고, CPU 입장에서는 분기가 많고 예측이 어려운 코드가 됩니다. 파이프라이닝과 캐시가 일을 못 합니다. 저자의 측정에서 5억 행 합계 쿼리가 PostgreSQL에서 약 20초 걸렸는데, 이 시간 대부분이 데이터가 아니라 행을 나르는 절차에 쓰인 셈입니다.

세 단계 처방

해설 글은 같은 쿼리를 세 단계로 빠르게 만듭니다. 측정 환경은 AWS c8g.4xlarge(Graviton4, 16 vCPU), PostgreSQL 18.4 대비, 병렬 실행은 꺼서 엔진 구조 차이만 비교했습니다.

단계실행 시간직전 대비
Volcano 방식 재구현1.3초기준
+ batching (1024행)480ms2.7배
+ operator fusion358ms1.3배
+ SIMD135ms2.7배

Batchingnext()next_batch()로 바꿔 1024행씩 나르는 것입니다. 함수 호출 오버헤드가 1/1024로 줄고, 루프가 단순해져 CPU가 파이프라이닝할 수 있는 코드가 됩니다. 이 한 가지만으로 2.7배입니다.

Operator fusion은 인접한 노드를 하나로 합치는 것입니다. sequential scan과 aggregation이 별도 노드면 batch를 만들어 넘기고 다시 읽는 복사가 생기는데, 합치면 스캔하면서 바로 집계합니다. 중간 버퍼가 사라집니다.

SIMD는 마지막 단계입니다. batch로 정렬된 데이터는 벡터 연산에 이상적인 모양이라, ARM64 벡터 명령으로 덧셈을 처리하면 일반 루프보다 3배 빨라집니다. batching이 먼저 있었기에 가능한 최적화입니다. 행 단위로 흩어진 데이터에는 SIMD를 적용할 자리가 없습니다.

결과적으로 20초가 135ms로, 약 150배입니다. 릴리스 노트는 여기에 columnar 저장 포맷까지 얹어 ClickBench 종합에서 ClickHouse를 앞선다고 주장합니다. 이 주장에는 유보를 달아 두는 게 좋겠습니다. 자체 벤치마크이고, 독립 검증이 쌓이려면 시간이 필요합니다. OLTP 쪽 30% 향상 주장도 마찬가지입니다.

PostgreSQL은 이걸 몰랐을까

당연히 알고 있습니다. 이 글이 보여준 처방은 학계와 업계에서 오래된 레시피입니다. MonetDB/X100 계열 연구가 20년 전에 벡터화 실행을 정립했고, DuckDB와 ClickHouse가 그 위에 서 있습니다.

PostgreSQL 본가에도 부분 부분 들어와 있습니다. JIT 컴파일이 expression 평가를 통째로 컴파일해 호출 오버헤드를 줄이고, executor 내부에 부분적인 batch 처리가 도입되는 흐름도 있습니다. 다만 35년 된 C 코드베이스에서 executor의 기본 단위를 행에서 batch로 바꾸는 것은, 백지에서 Rust로 다시 짜는 것과는 전혀 다른 난이도입니다. pgrust가 유리한 것은 Rust여서라기보다 백지여서입니다.

그래서 이 프로젝트를 보는 제 관점은 "PostgreSQL의 대체재가 나타났다"가 아닙니다. 재미있는 것은 두 가지입니다. 하나, PostgreSQL 호환(문법, 프로토콜, regression test)을 유지하면서 executor만 현대식으로 갈아 끼우면 어디까지 가는지 보여주는 실험이라는 점. 둘, vacuum이 미운 날에서 다뤘던 것과 같은 질문, 즉 PostgreSQL의 설계 결정들이 어떤 트레이드오프였는지를 실물로 보여준다는 점입니다. Volcano 모델은 게으름이 아니라 1980년대의 합리적 선택이었고, 그 청구서가 분석 워크로드에서 날아오고 있을 뿐입니다.

실무 관점의 결론은 소박하게 남겨 둘게요. 오늘 PostgreSQL에서 느린 집계를 빠르게 하는 검증된 길은 여전히 병렬 쿼리, JIT, 사전 집계, 그리고 필요하면 분석 전용 엔진의 병행입니다. pgrust는 북마크해 두고, regression test 통과라는 출발점이 성능 주장 이후에도 유지되는지 지켜보겠습니다.

참고 자료