포스트

random_page_cost 값의 재해석

random_page_cost 기본값 4.0 은 스토리지를 재서 맞추면 된다고들 합니다. Vondra 의 글을 따라가며, 이 값이 사실은 Cost Model 의 빈틈을 메우는 proxy 라는 관점을 정리했습니다.

random_page_cost 값의 재해석

이 글은 Tomas Vondra 의 “Some more thoughts on random_page_cost” (2026-06-23) 를 읽고, 제 관점에서 해석해 정리한 글입니다.
원문 : https://vondra.me/posts/some-more-thoughts-on-random-page-cost/
같은 주제의 POSETTE 2026 발표 영상 : https://www.youtube.com/watch?v=lbrWJgl8uDQ

PostgreSQL 의 random_page_cost 기본값은 2002년부터 4.0 입니다. 랜덤 I/O 가 순차 I/O 보다 얼마나 비싼지를 옵티마이저에게 알려 주는 값입니다. 그런데 스토리지는 회전 디스크에서 SSD 를 거쳐 NVMe 까지 완전히 바뀌었는데도 20년이 넘게 그대로입니다. 그래서 SSD 를 쓰면 random_page_cost 를 낮추라는 조언을 흔히 듣습니다. 빠른 디스크니 랜덤 I/O 가 싸다는 논리입니다.

Vondra 가 이 값을 두고 쓴 후속 글이 흥미로워서, 읽으면서 제 나름대로 정리하고 해석해 봤습니다. 결론부터 말하면, 이 글은 “스토리지를 재서 맞는 값으로 바꾸자” 는 직관 자체를 흔듭니다.

각 H/W 환경에 맞는 random_page_cost 측정

Vondra 는 앞선 글에서 random_page_cost 를 직접 측정해 보려 했습니다. 큰 테이블과 인덱스를 하나 만들고, EXPLAIN ANALYZE 의 Buffers 통계로 순차 스캔과 인덱스 스캔이 각각 읽은 페이지 수와 시간을 잰 다음, 페이지 한 장을 랜덤으로 읽는 시간과 순차로 읽는 시간의 비율을 구하는 방식입니다. 그 비율이 곧 그 스토리지의 추정 random_page_cost 가 됩니다.

EXPLAIN ANALYZE 의 Buffers 통계로 random_page_cost 를 실측하는 계산 랜덤 페이지 읽기 시간 ÷ 순차 페이지 읽기 시간 = 추정 random_page_cost. 이 사례에서는 약 27.7 이 나왔습니다.

여러 머신과 스토리지에서 같은 방식으로 재 본 결과가 인상적이었습니다. 어느 환경도 기본값 4.0 근처에 오지 않았습니다.

host 와 스토리지 종류별 추정 random_page_cost 표 같은 SSD 라도 구성에 따라 15 에서 40 사이, 회전 SATA 는 140 까지 벌어집니다. 공통점은 전부 4.0 보다 훨씬 크다는 것입니다.

여기까지만 보면 결론은 단순해 보입니다. 기본값 4.0 은 현실과 동떨어졌으니, 내 스토리지를 재서 맞는 값으로 갈아 끼우면 된다는 것입니다. 그런데 이번 후속 글에서 Vondra 는 바로 그 결론을 스스로 뒤집습니다.

SATA 측정값으로 본 모순

재미있는 단서가 하나 있었습니다. Vondra 가 머신을 정비하려고 케이스를 열었다가, 까맣게 잊고 있던 회전 SATA 디스크를 발견했다고 합니다. 2000년쯤 4.0 이라는 기본값이 정해질 무렵 쓰였을 스토리지에 가장 가까운 물건입니다. 당시엔 PATA 나 SCSI 였겠지만 어쨌든 회전 디스크라는 점은 같습니다. 그러니 이 디스크에서 재면 드디어 4.0 에 가까운 값이 나오지 않을까 기대해 봄 직합니다.

결과는 약 140 이었습니다. 4.0 은커녕 SSD 추정값의 서너 배, 많게는 아홉 배에 이릅니다. 저는 이 대목이 글에서 가장 중요한 전환점이라고 봅니다. 가장 오래된 회전 디스크에서조차 4.0 이 나오지 않는다면, 4.0 은 애초에 랜덤 I/O 의 “원시 비용” 을 잰 값이 아니었다는 뜻이 됩니다. SSD 에서 어쩌다 4.0 과 비슷하게 보이는 건 그저 우연인 셈입니다.

값을 올리면 느려지는 역설

또 하나 걸리는 지점이 있습니다. 과거에 random_page_cost 를 올려 본 사람들의 피드백은, 값을 올리니 성능이 좋아지기는커녕 오히려 나빠졌다는 것이었습니다. 측정값에 가깝게 “더 정확히” 맞췄는데 왜 더 느려질까요. 비용 산정이 정확해졌으면 계획도 나아져야 할 텐데 반대 결과가 나온 셈입니다.

Vondra 의 설명은, random_page_cost 가 사실은 불완전한 Cost Model 을 “보정” 하는 역할을 해 왔다는 것입니다. 저도 이 해석이 가장 말이 된다고 생각합니다. Cost Model 은 여러 캐싱 효과나, 랜덤 I/O 를 많이 일으키는 계획이 실제로 쓰는 자원을 제대로 반영하지 못합니다. 애초에 모든 Cost Model 은 근사이고, 그것도 꽤 거친 근사입니다. 모든 세부를 정확히 흉내 내려 들면 결국 원래 시스템만큼 커져서 모델일 이유가 사라집니다. 그 거친 근사의 빈틈을 random_page_cost 라는 손잡이 하나가 대신 메우고 있었던 것으로 보입니다.

Vondra 가 짚는 빈틈은 크게 두 가지입니다.

Cost Model 이 놓치는 메모리

Cost Model 이 보는 자원은 CPU 와 I/O 두 가지입니다. 둘 다 핵심이지만, 또 하나의 중요한 자원인 메모리가 빠져 있습니다.

work_mem 이 있긴 합니다. 하지만 이건 정렬이나 해싱에 쓰는 작업 버퍼 크기를 제한하는 안전장치에 가깝습니다. 버퍼가 작으면 연산이 디스크로 더 많이 흘러넘치니 I/O 양에는 영향을 주지만, 실행 계획이 실제로 “점유” 하는 메모리 자체는 추적하지 않습니다.

Vondra 가 든 예가 이 차이를 잘 보여 줍니다. 100GB 테이블에 쿼리 조건에 맞는 “관심 있는” 데이터가 1GB 들어 있다고 해 봅니다. 이 테이블은 순차 스캔으로 훑을 수도 있고 인덱스로 접근할 수도 있습니다.

  • 테이블이 메모리에 올라와 있지 않아 디스크에서 새로 읽어와야 하는 상태라면, 순차 스캔은 그 100GB 를 읽어 들이면서 공유 버퍼나 페이지 캐시에 있던 다른 데이터를 그만큼 밀어냅니다. 쿼리 자체는 work_mem=4MB 로도 멀쩡히 돌지만, 사실상 100GB 의 메모리를 쓰는 셈입니다.
  • 반면 인덱스는 테이블의 약 1퍼센트, 관심 있는 1GB 만 건드립니다. 랜덤 I/O 라 시간은 더 걸릴 수 있어도, 캐시에서 밀려나는 다른 데이터는 훨씬 적습니다.

같은 결과를 내는 두 계획이 메모리에 주는 부담은 100GB 와 1GB 로 전혀 다른데, Cost Model 은 이 차이를 전혀 보지 못합니다. 저는 이 지점이 “랜덤 I/O 가 비싸 보여도 결과적으로 더 쌀 수 있다” 는 직관과 정확히 맞닿아 있다고 봤습니다.

Active Set

두 번째 빈틈은 active set 이라는 개념입니다. 데이터베이스가 1TB 라도, 실제 운영 시스템 대부분은 그중 아주 일부만 접근합니다. 사용자는 최근 주문만 들여다보는 식입니다. 이렇게 실제로 접근하는 부분집합을 데이터베이스의 active set 이라고 부릅니다.

운영의 핵심은 이 active set 을 메모리에 붙들어 두는 것입니다. 그런데 랜덤 I/O 를 많이 하는 계획은 더 국소적인 경향이 있습니다. 인덱스 스캔은 보통 순차 스캔보다 훨씬 작은 비율의 데이터만 건드립니다. I/O 는 더 비쌀 수 있어도, 어쨌든 관심 있는 데이터에 한정됩니다.

반대로 순차 스캔은 active set 을 크게 부풀립니다. 심하면 데이터베이스 전체로 늘어납니다. 전체가 들어갈 만큼 RAM 이 넉넉하지 않은 한 반가운 일이 아닙니다. 인덱스 스캔은 active set 을 작게 유지하게 해 줍니다.

하지만 플래너는 active set 을 전혀 인식하지 못합니다. 모든 쿼리가 차가운 데이터에서 시작하는 것처럼 계획을 세우고, 캐싱 효과는 같은 쿼리 안에서처럼 아주 제한된 경우에만 고려합니다.

결국 random_page_cost 란

제가 내린 결론은, 값 하나를 절대적인 정답처럼 믿고 고정하지는 말자는 것입니다. 다만 그동안 하드웨어 수준이 전반적으로 올라온 만큼, 기본값 4 를 그대로 두기보다 2 정도까지 한번 낮춰 보고 전체 쿼리 플랜이 어떻게 달라지는지를 직접 확인해 보는 방법이 좋을 것 같습니다. pg_stat_statements 의 상위 쿼리들이 더 낮은 값에서 이득을 보는지 살피고, 값을 조정한 뒤 의도한 효과가 나는지 또는 다른 쿼리를 해치지는 않는지 지켜보는 식입니다. 결국 시스템마다 다를 수밖에 없는, 관찰과 피드백으로 굴러가는 작업인 것 같습니다.

절대적인 “정답 값” 을 찾기보다, 하드웨어가 좋아진 만큼 기본값 4 에서 2 정도로 낮춰 보고 전체 쿼리 플랜이 어떻게 달라지는지를 관찰하는 편이 현실적인 것 같습니다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.