처리중입니다. 잠시만 기다려주세요.
TTJ 코딩클래스
정규반 단과 자료실 테크 뉴스 코딩 퀴즈
테크 뉴스
Hacker News 2026.03.21 48
#AI

PostgreSQL의 work_mem 설정, 무심코 올리면 서버가 위험해지는 이유

Hacker News 원문 보기

흔한 성능 튜닝 조언의 함정

PostgreSQL을 사용하다 보면 쿼리가 느릴 때 가장 먼저 접하는 튜닝 조언 중 하나가 "work_mem을 올려라"입니다. 구글 검색이든, Stack Overflow든, 심지어 AI 챗봇에게 물어봐도 비슷한 답이 돌아옵니다. 느린 정렬(sort)이나 해시 조인(hash join) 쿼리의 EXPLAIN 결과에서 "Sort Method: external merge Disk"같은 문구를 보면, 메모리가 부족해서 디스크를 사용하고 있다는 의미이니 work_mem을 올리면 해결된다는 것이죠. 실제로 이 조언은 틀린 말이 아닙니다. 하지만 여기에는 운영 환경에서 심각한 장애를 일으킬 수 있는 함정이 숨어 있습니다.

work_mem이 정확히 뭔가

work_mem은 PostgreSQL에서 정렬(ORDER BY), 해시 조인, 해시 기반 집계(GROUP BY) 등의 작업을 수행할 때 각 작업이 사용할 수 있는 메모리 상한을 지정하는 파라미터입니다. 기본값은 4MB로, 꽤 보수적인 수치입니다. 이 값보다 더 많은 메모리가 필요한 작업은 디스크의 임시 파일을 사용하게 되는데, 디스크 I/O는 메모리 연산보다 수백~수천 배 느리기 때문에 쿼리 성능이 크게 저하됩니다.

여기까지만 들으면 "그러면 work_mem을 1GB로 올려놓으면 되는 거 아닌가?"라고 생각할 수 있습니다. 이것이 바로 함정의 시작입니다.

함정의 핵심: work_mem은 "쿼리당"이 아니라 "작업당"

가장 중요한 포인트는 work_mem이 커넥션 단위도, 쿼리 단위도 아닌, 개별 작업(operation) 단위로 적용된다는 것입니다. 하나의 SQL 쿼리 안에 정렬이 3번, 해시 조인이 2번 있다면, 그 쿼리 하나가 work_mem × 5 만큼의 메모리를 사용할 수 있습니다. 여기에 동시 접속 수를 곱하면 상황은 급격히 악화됩니다.

구체적으로 계산해 봅시다. work_mem을 256MB로 설정했다고 가정합니다. 하나의 복잡한 분석 쿼리가 내부적으로 8개의 정렬/해시 작업을 수행한다면, 그 쿼리 하나가 최대 2GB의 메모리를 잡을 수 있습니다. 이런 쿼리를 10명의 사용자가 동시에 실행하면? 20GB입니다. 서버의 전체 RAM이 32GB라면, PostgreSQL의 shared_buffers와 OS 캐시, 그리고 다른 프로세스가 사용하는 메모리까지 합치면 순식간에 OOM(Out of Memory) 상황에 도달합니다.

더 교묘한 점은, 이 문제가 평소에는 드러나지 않는다는 것입니다. 일반적인 단순 쿼리는 work_mem을 거의 사용하지 않습니다. 문제는 복잡한 리포트 쿼리나 대량 데이터를 처리하는 배치 작업이 들어올 때, 그리고 그런 작업이 동시에 여러 개 실행될 때 갑자기 터집니다. "설정 바꾸고 한 달 동안 문제없었는데 갑자기 서버가 죽었다"는 경험담이 나오는 이유가 바로 이것입니다.

그러면 어떻게 해야 하나

올바른 접근 방식은 work_mem을 전역적으로 크게 올리는 것이 아니라, 필요한 상황에서만 세션 레벨로 조정하는 것입니다. PostgreSQL은 SET 명령으로 현재 세션의 work_mem을 동적으로 변경할 수 있습니다.

예를 들어, 대용량 분석 쿼리를 실행하기 전에 SET work_mem = '256MB';로 올려놓고, 작업이 끝나면 RESET work_mem;으로 되돌리는 방식입니다. 이렇게 하면 해당 세션에서만 큰 메모리를 사용하고, 나머지 일반적인 OLTP 쿼리들은 보수적인 기본값으로 안전하게 동작합니다.

애플리케이션 레벨에서는 커넥션 풀(PgBouncer 등)을 사용할 때 트랜잭션 시작 시 SET LOCAL을 사용하는 것이 더 안전합니다. SET LOCAL work_mem = '256MB';는 현재 트랜잭션이 끝나면 자동으로 원래 값으로 돌아가기 때문에, 커넥션 풀에서 다음 사용자가 해당 커넥션을 받았을 때 의도치 않게 높은 work_mem이 적용되는 문제를 방지할 수 있습니다.

또 다른 접근은 역할(role) 기반 설정입니다. 분석용 사용자 계정과 일반 서비스 계정을 분리하고, ALTER ROLE analytics_user SET work_mem = '256MB';처럼 역할 수준에서 설정을 다르게 가져가는 것입니다. 이렇게 하면 분석 팀의 쿼리는 충분한 메모리를 사용하면서도, 웹 서비스의 일반 쿼리는 안전한 기본값으로 유지할 수 있습니다.

업계 맥락: 메모리 관련 설정의 보편적 함정

이 문제는 PostgreSQL에만 국한된 것이 아닙니다. 데이터베이스 메모리 설정은 거의 모든 DBMS에서 비슷한 함정이 존재합니다. MySQL의 sort_buffer_size도 마찬가지로 커넥션당 할당되기 때문에 동시 접속이 많아지면 메모리가 폭발할 수 있고, Oracle의 PGA(Program Global Area) 관련 설정도 유사한 주의가 필요합니다.

공통된 교훈은, 데이터베이스의 메모리 파라미터를 변경할 때는 반드시 "최악의 경우 동시 사용 시나리오"를 계산해야 한다는 것입니다. 설정값 × 쿼리당 최대 작업 수 × 최대 동시 커넥션 수 = 이론적 최대 메모리 사용량. 이 값이 시스템의 가용 메모리를 넘지 않는지 확인하는 것이 기본입니다.

최근에는 PostgreSQL 15에서 도입된 hash_mem_multiplier 파라미터도 이 맥락에서 알아둘 필요가 있습니다. 해시 기반 작업에 work_mem의 몇 배까지 허용할지를 별도로 제어할 수 있는 파라미터인데, 이 역시 무분별하게 올리면 같은 종류의 문제를 야기합니다.

한국 개발자에게 주는 시사점

한국의 많은 서비스들이 PostgreSQL을 적극적으로 도입하고 있는 추세입니다. 특히 AWS RDS나 Aurora PostgreSQL을 사용하는 경우, 인스턴스 크기에 따라 가용 메모리가 정해져 있으므로 work_mem 설정의 영향이 더 직접적입니다. db.r6g.large 인스턴스(16GB RAM)에서 work_mem을 무심코 512MB로 올렸다가, 동시 분석 쿼리가 몰리는 시점에 인스턴스가 응답 불능에 빠지는 시나리오는 충분히 현실적입니다.

실무에서의 권장 사항을 정리하면 다음과 같습니다.

  • 전역 work_mem은 보수적으로 유지하세요. 4MB~64MB 사이가 대부분의 OLTP 워크로드에 적합합니다.
  • 큰 work_mem이 필요한 쿼리는 세션 또는 트랜잭션 레벨에서 SET으로 조정하세요.
  • EXPLAIN (ANALYZE, BUFFERS)로 실제 메모리 사용량을 확인하세요. 디스크 스필(spill)이 발생하는 쿼리를 식별하고, 해당 쿼리에만 선택적으로 work_mem을 올리는 것이 올바른 접근입니다.
  • 모니터링을 설정하세요. pg_stat_activity와 OS 레벨 메모리 모니터링을 통해 메모리 사용량 급증을 조기에 감지할 수 있어야 합니다.

마무리

데이터베이스 튜닝에서 "이 값을 올려라"라는 단순한 조언은 대부분 맥락이 빠져 있습니다. work_mem은 올리면 확실히 특정 쿼리가 빨라지지만, 그 대가를 전체 시스템이 치를 수 있습니다. 여러분의 PostgreSQL 서버에서 work_mem은 현재 얼마로 설정되어 있나요? 그리고 최악의 동시 접속 시나리오에서 메모리 사용량을 계산해 본 적이 있나요?


🔗 출처: Hacker News

이 뉴스가 유용했나요?

이 기술을 직접 배워보세요

AI 도구, 직접 활용해보세요

AI 시대, 코딩으로 수익을 만드는 방법을 배울 수 있습니다.

AI 활용 강의 보기

"비전공 직장인인데 반년 만에 수익 파이프라인을 여러 개 만들었습니다"

실제 수강생 후기
  • 비전공자도 6개월이면 첫 수익
  • 20년 경력 개발자 직강
  • 자동화 프로그램 + 소스코드 제공

매일 AI·개발 뉴스를 받아보세요

주요 테크 뉴스를 매일 아침 이메일로 전해드립니다.

스팸 없이, 언제든 구독 취소 가능합니다.