기타 Hacker News · 1일 전

"데이터보다 주소록이 더 크다고?" 리눅스 서버 메모리를 몰래 갉아먹는 범인의 정체

핵심 요약
  • 일반적으로 전체 메모리의 1% 미만으로 여겨지던 리눅스 페이지 테이블이 특정 환경에서는 실제 데이터보다 더 많은 메모리를 집어삼킬 수 있습니다.
  • 수많은 프로세스가 대용량 공유 메모리를 동시에 참조할 때 각 프로세스마다 별도의 페이지 테이블이 생성되어 기가바이트 단위의 메모리 낭비와 OOM 크래시를 유발합니다.
  • Postgres, ClickHouse 등 현대 DB에서도 자주 목격되며, 휴지 페이지(Huge Pages) 적용 등으로 완화할 수 있으나 메모리 단편화 등 제약이 존재합니다.
요약 리눅스 창시자 리누스 토르발스는 과거 멀티레벨 페이지 테이블을 도입하며 메모리 효율성보다는 접근 지연 시간(캐시 적중률)에 집중했고, 페이지 테이블 자체가 차지하는 메모리는 상대적으로 부차적인 문제로 여겼습니다. 일반적으로 단일 매핑 환경에서 페이지 테이블 엔트리(PTE)는 매핑된 메모리의 약 512분의 1(1% 미만)에 불과하기 때문입니다. 하지만 여러 프로세스가 대용량 공유 메모리를 동시에 매핑하는 환경에서는 이야기가 완전히 달라집니다. 데이터 페이지는 공유되더라도 각 프로세스는 자신만의 페이지 테이블을 따로 할당받아야 하기 때문입니다. 예를 들어 512개의 프로세스가 동일한 4KiB 페이지를 매핑하면, 8바이트 크기의 PTE 512개가 모여 실제 데이터 페이지 크기만큼의 메모리를 페이지 테이블만으로 소비하게 됩니다. 실제 역사적으로도 이러한 구조적 한계 때문에 시스템 장애가 빈번히 발생했습니다. 2002년 32비트 PAE 시스템에서 발생했던 공유 메모리 고갈 문제를 시작으로, 2022년에는 512GB RAM 서버에서 오라클 DB 클라이언트 1,500여 개가 300GB 공유 메모리(SGA)에 붙었다가 페이지 테이블 오버헤드로 인해 메모리 부족(OOM)으로 다운되는 사례가 보고되었습니다. 또한 메모리 할당 라이브러리가 munmap 대신 madvise(MADV_DONTNEED)를 사용할 경우 데이터 페이지만 비워지고 빈 페이지 테이블은 그대로 남아 기가바이트 단위로 메모리를 낭비하는 문제도 지적되었습니다. 이 같은 현상은 PostgreSQL이나 ClickHouse 등 현대 데이터베이스에서도 동일하게 재현됩니다. 수백 개의 연결이 대형 캐시를 스캔하면서 페이지 테이블 크기가 수십 기가바이트까지 폭증해 스왑이 발생하거나 OOM 킬러가 작동하곤 합니다. 거대 페이지(Huge Pages)를 활용하면 페이지 테이블 크기를 수십~수백 메가바이트 수준으로 획기적으로 줄여 이를 완화할 수 있지만, 단편화되지 않은 연속 메모리를 확보해야 한다는 현실적인 제약이 따릅니다.
Sponsored · 광고