본문 바로가기
1. 개발/1.8. ActiveMQ

Paging 모드에서 메시지를 다시 메모리로 읽어오는 'Page Size' 최적화는?

by 엉짱 2026. 3. 25.
반응형

Paging 모드에서 메시지를 다시 메모리로 읽어오는 'Page Size' 최적화는?

엔터프라이즈 메시징 시스템에서 트래픽 폭주로 인해 브로커의 메모리가 한계에 도달하면, ActiveMQ Artemis는 시스템의 붕괴(OOM)를 막기 위해 수신되는 메시지들을 메모리에 올리지 않고 디스크로 바로 저장하는 '페이징(Paging)' 모드에 돌입합니다.

하지만 진정한 병목과 장애는 메시지를 디스크에 '쓸 때'가 아니라, 컨슈머(Consumer)가 큐의 메시지를 소비하여 메모리에 여유가 생겼을 때 디스크에 쌓아둔 메시지들을 다시 메모리로 '읽어올 때(Depaging)' 발생합니다.

이 페이징과 디페이징의 사이클을 효율적으로 통제하는 가장 결정적인 설정값이 바로 page-size-bytes입니다. 본 가이드에서는 이 페이지 크기 설정이 브로커의 메모리와 디스크 I/O에 미치는 치명적인 영향과, 시스템 아키텍처에 맞춘 최적화 전략을 상세히 해부합니다.


1. 디페이징(Depaging) 메커니즘의 이해

페이징 모드가 시작되면 브로커는 paging 디렉토리에 순차적으로 파일들을 생성하여 메시지를 기록합니다. 이후 컨슈머가 열심히 작업을 처리하여 브로커의 메모리(max-size-bytes) 사용량이 줄어들면, 브로커는 디스크에 잠들어 있던 페이징 파일들을 순서대로 열어 다시 힙(Heap) 메모리로 적재합니다. 이 복원 과정을 디페이징(Depaging)이라고 부릅니다.

여기서 아키텍트가 반드시 인지해야 할 핵심 동작 원리가 있습니다. 브로커는 디스크에서 메시지를 하나씩 낱개로 읽어오지 않습니다. 디스크 I/O 효율을 극대화하기 위해, 브로커는 생성되어 있는 '하나의 페이징 파일(Page File) 단위'로 데이터를 통째로 메모리에 읽어 들입니다.


2. 'page-size-bytes'의 역할과 딜레마

broker.xml 파일의 주소 설정(Address Settings) 블록에 정의하는 page-size-bytes 파라미터는 디스크에 생성되는 페이징 파일 1개의 최대 크기를 결정합니다. 기본값은 10MB입니다.

메시지가 계속 유입되면 브로커는 10MB짜리 파일을 하나 만들고, 가득 차면 닫은 뒤 다음 10MB짜리 파일을 생성하는 식으로 디스크를 채워나갑니다. 이 파일 하나의 크기를 어떻게 설정하느냐에 따라 브로커의 운명이 극명하게 갈리게 됩니다.


3. Page Size를 너무 '크게' 설정했을 때의 치명적 장애 (OOM 및 블로킹)

디스크 조각화를 막겠다는 생각으로 page-size-bytes를 100MB나 500MB처럼 아주 거대하게 설정하면 어떤 일이 벌어질까요?

A. 디페이징 시점의 메모리 초과 (Memory Overflow)
특정 큐의 최대 허용 메모리(max-size-bytes)가 100MB라고 가정해 보겠습니다. 메모리가 꽉 차서 페이징이 발생했고, 페이징 파일 1개의 크기를 100MB로 설정해 두었습니다.
컨슈머가 메시지를 일부 처리하여 큐의 메모리에 10MB의 여유 공간이 생겼습니다. 브로커는 디페이징을 시도하며 디스크에 있는 100MB짜리 페이징 파일 하나를 통째로 힙 메모리에 올리려 시도합니다.
결과는 참혹합니다. 10MB밖에 여유가 없는 공간에 100MB의 데이터를 한 번에 밀어 넣으려 하므로, 큐는 즉시 메모리 한계를 다시 초과하게 되고 극심한 가비지 컬렉션(GC) 스파이크와 함께 시스템 전체가 블로킹 상태에 빠집니다.

B. 끝없는 페이징 루프 현상
메모리에 올라오자마자 한계치를 초과했기 때문에 브로커는 이 메시지들을 다시 디스크로 밀어내는(Paging) 작업을 시작할 수 있습니다. 읽어오고 다시 내쫓는 무의미한 I/O 루프에 빠져 브로커의 CPU와 디스크 대역폭이 100%로 치솟으며 라우팅 기능이 완전히 마비됩니다.


4. Page Size를 너무 '작게' 설정했을 때의 성능 저하 (I/O 병목)

반대로, 메모리에 부담을 주지 않기 위해 page-size-bytes를 1MB나 500KB처럼 극단적으로 작게 설정한다면 다른 형태의 인프라 장애를 마주하게 됩니다.

A. 파일 디스크립터(File Descriptor) 고갈
트래픽 피크 타임에 10GB의 데이터가 페이징되었다고 가정해 봅니다. 파일 크기가 1MB라면 디스크에는 무려 10,000개의 자잘한 페이징 파일이 생성됩니다. 브로커가 이 수많은 파일을 열고 닫는 과정에서 운영체제(OS)의 파일 디스크립터 한계치(ulimit)에 도달하여 브로커 프로세스가 강제 종료될 위험이 매우 높아집니다.

B. 순차 I/O의 이점 상실과 탐색 오버헤드
파일이 너무 잘게 쪼개져 있으면, 디스크 컨트롤러가 파일의 헤더를 읽고 다음 파일로 넘어가기 위해 잦은 컨텍스트 스위칭과 물리적 디스크 헤더 이동(HDD의 경우)을 수행해야 합니다. 대용량 데이터를 한 번에 쓱 읽어들이는 순차 읽기(Sequential Read)의 압도적인 성능 이점을 스스로 포기하게 되는 꼴입니다.


5. 완벽한 밸런스를 위한 아키텍처 튜닝 가이드 (Best Practices)

그렇다면 이 딜레마를 해결할 최적의 페이지 크기는 어떻게 계산해야 할까요? 아키텍트가 설정 파일(broker.xml)을 구성할 때 반드시 지켜야 할 황금비율이 존재합니다.

  1. 메모리 임계값 대비 10% ~ 20%의 법칙:
    page-size-bytes는 반드시 해당 큐에 할당된 최대 메모리인 max-size-bytes의 10%에서 최대 20% 사이의 값으로 설정해야 합니다.
    예를 들어 큐의 최대 메모리가 100MB라면, 페이지 크기는 10MB에서 20MB 사이가 가장 이상적입니다. 이렇게 설정해야 메모리에 10%의 여유가 생겼을 때, 브로커가 10MB짜리 페이징 파일 하나를 메모리에 올려도 전체 한계치(100MB)를 터뜨리지 않고 부드럽게 메시지를 공급할 수 있습니다.
  2. 평균 메시지 크기 고려:
    큐로 들어오는 개별 메시지의 크기가 유독 큰 비즈니스 환경(예: 장문의 XML이나 JSON 페이로드)이라면, 페이징 파일 하나에 최소한 수십~수백 개의 메시지가 온전히 담길 수 있도록 페이지 크기를 상향 조정해야 합니다. 10MB짜리 메시지가 들어오는데 페이지 크기가 1MB라면 브로커 내부 로직이 심각하게 꼬이게 됩니다.
  3. OS 페이지 캐시(Page Cache) 활용:
    디페이징 시 속도를 끌어올리려면 페이징 파일들이 OS의 메모리 캐시에 적절히 상주해 있는 것이 유리합니다. 서버의 가용 RAM 용량을 모니터링하여, 한 번에 읽어 들이는 페이지 묶음이 물리 메모리 내에서 부드럽게 스와핑(Swapping)될 수 있도록 인프라 스펙과 브로커 설정을 동기화해야 합니다.

결론적으로 페이징 모드에서 'Page Size'는 단순한 파일 자르기 옵션이 아닙니다. 메모리의 여유 공간과 디스크 I/O 속도 사이의 줄다리기를 통제하는 핵심 밸브입니다. 큐의 최대 허용 메모리와 유입되는 메시지의 크기를 정확히 분석하여, 이 밸브를 시스템에 딱 맞게 조절하시기 바랍니다.

반응형