백엔드 개발자로서 Kafka, Elasticsearch 같은 시스템을 사용하다 보면 "Page Cache 덕분에 빠르다"는 설명을 종종 접하게 됩니다. 하지만 정작 Page Cache가 무엇인지, 어떻게 동작하는지는 잘 모르는 경우가 많습니다.
이 글에서는 Linux Page Cache의 동작 원리부터 Dirty Page 개념, 그리고 이를 적극 활용하는 소프트웨어들까지 살펴보겠습니다.
Page Cache는 Linux 커널이 디스크 I/O 성능을 향상시키기 위해 사용하는 메모리 캐싱 메커니즘입니다. 디스크에서 읽은 데이터를 RAM에 저장해두어 반복적인 디스크 접근을 줄입니다.
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Application │ ──> │ Page Cache │ ──> │ Disk │
│ │ <── │ (RAM) │ <── │ │
└─────────────┘ └─────────────┘ └─────────────┘
| 특징 | 설명 |
|---|---|
| 투명성 | 애플리케이션이 인식하지 못하게 자동 동작 |
| Write-back | 쓰기 시 즉시 디스크에 기록하지 않음 |
| LRU 기반 | 최근 사용되지 않은 페이지부터 제거 |
| 동적 크기 | 사용 가능한 메모리에 따라 자동 조절 |
sync 명령 또는 fsync() 호출 시 즉시 디스크에 기록여기서 중요한 점은 write() 호출이 반환되었다고 해서 데이터가 디스크에 기록된 것이 아니라는 것입니다.
# 메모리 사용량 확인 (buff/cache 항목이 Page Cache)
free -h
# Page Cache 상세 정보
cat /proc/meminfo | grep -E "Cached|Buffers|Dirty"
# Page Cache 수동 정리
echo 1 > /proc/sys/vm/drop_caches # pagecache만
echo 2 > /proc/sys/vm/drop_caches # dentries, inodes
echo 3 > /proc/sys/vm/drop_caches # 전체
Dirty Page는 Page Cache에 있는 데이터 중 수정되었지만 아직 디스크에 기록되지 않은 페이지입니다.
Clean Page: 메모리 내용 = 디스크 내용 (안전)
Dirty Page: 메모리 내용 ≠ 디스크 내용 (유실 위험)
┌────────────────┐ write() ┌────────────────┐ flush ┌────────────────┐
│ Clean Page │ ───────────> │ Dirty Page │ ──────────> │ Clean Page │
│ (Disk=Memory) │ │ (Disk≠Memory) │ │ (Disk=Memory) │
└────────────────┘ └────────────────┘ └────────────────┘
| 조건 | 설명 |
|---|---|
dirty_expire_centisecs 초과 | 기본 30초 이상 dirty 상태 유지 시 |
dirty_background_ratio 초과 | 메모리의 10% 이상이 dirty일 때 (비동기) |
dirty_ratio 초과 | 메모리의 20% 이상이 dirty일 때 (동기, 블로킹) |
sync/fsync() 호출 | 명시적 동기화 요청 시 |
| 메모리 부족 | 새 페이지 할당 필요 시 |
# dirty page가 디스크에 기록되기 시작하는 비율 (기본: 10%)
/proc/sys/vm/dirty_background_ratio
# dirty page 최대 비율 - 초과 시 동기 쓰기 (기본: 20%)
/proc/sys/vm/dirty_ratio
# dirty page 유지 최대 시간 (기본: 30초)
/proc/sys/vm/dirty_expire_centisecs
# 현재 dirty page 크기
cat /proc/meminfo | grep Dirty
# 실시간 모니터링
watch -n 1 'grep -E "Dirty|Writeback" /proc/meminfo'
전원 장애 발생 시:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ App │ │ Page Cache │ X │ Disk │
│ 저장 완료 │ │ Dirty Page │ ──> │ 기록 안됨 │
└─────────────┘ └─────────────┘ └─────────────┘
↓
데이터 유실!
// 방법 1: fsync - 특정 파일만 동기화
write(fd, data, size);
fsync(fd); // 디스크 기록 완료까지 대기
// 방법 2: O_SYNC 플래그 - 매 write마다 동기화
open("file", O_WRONLY | O_SYNC);
// 방법 3: O_DIRECT - Page Cache 우회
open("file", O_WRONLY | O_DIRECT);
Kafka는 자체 버퍼 풀을 만들지 않고 OS Page Cache에 전적으로 의존합니다.
Producer → Broker(Page Cache) → Consumer
↓ (비동기)
Disk
왜 가능한가:
sendfile())로 Page Cache에서 Network로 직접 전송sendfile()이란?
sendfile()은 커널 내에서 파일 데이터를 소켓으로 직접 전송하는 시스템 콜입니다. 일반적인 파일 전송은Disk → Page Cache → User Buffer → Socket Buffer → NIC경로로 여러 번 복사가 발생하지만,sendfile()은 User Buffer를 거치지 않고 Page Cache에서 Socket Buffer로 직접 전달합니다. 이를 Zero-copy라고 부르며, CPU 복사 비용을 제거하여 처리량을 높입니다.
Lucene 세그먼트 파일을 mmap으로 매핑하여 OS가 알아서 캐싱하게 합니다.
mmap이란?
mmap()은 파일을 프로세스의 가상 메모리 주소 공간에 직접 매핑하는 시스템 콜입니다. 일반적인read()호출은Disk → Page Cache → User Buffer로 데이터를 복사하지만, mmap은 Page Cache를 가상 메모리에 직접 매핑하여 복사 없이 배열처럼 접근할 수 있습니다. 파일 전체를 메모리에 올리지 않고 접근한 부분만 Page Fault를 통해 로드(Lazy Loading)되므로 대용량 인덱스 파일에 효율적입니다.
Kafka Streams, Flink, TiKV 등에서 사용하는 LSM-Tree 기반 스토리지입니다. 읽기에 mmap을 활용하여 Page Cache 이점을 얻습니다.
Write → MemTable → SST Files (Page Cache) → Disk
사용처:
메모리 데이터 → fork() → 자식 프로세스가 RDB 저장
↓
Page Cache → Disk
Copy-on-Write를 활용하여 fork() 시 부모/자식이 Page Cache를 공유합니다.
fork()란?
fork()는 현재 프로세스를 복제하여 자식 프로세스를 생성하는 시스템 콜입니다. Redis는 RDB 스냅샷 저장 시fork()로 자식 프로세스를 만들고, 자식이 디스크에 데이터를 쓰는 동안 부모는 계속 클라이언트 요청을 처리합니다. 이때 Copy-on-Write(COW) 방식으로 부모/자식이 메모리 페이지를 공유하다가, 부모가 데이터를 수정할 때만 해당 페이지를 복사합니다. 덕분에 대용량 데이터도 빠르게 fork하고, 메모리 사용량도 최소화할 수 있습니다.
# sendfile로 Page Cache → Socket 직접 전송
sendfile on;
tcp_nopush on;
# 자주 접근하는 파일 FD 캐시
open_file_cache max=1000 inactive=60s;
| 소프트웨어 | 전략 | 이유 |
|---|---|---|
| Kafka | 전적으로 의존 | Sequential I/O, Zero-copy |
| Elasticsearch | 적극 활용 (mmap) | 읽기 중심, 인덱스 캐싱 |
| RocksDB | 부분 활용 | LSM-Tree 특성상 순차 쓰기 |
| PostgreSQL | 이중 캐싱 | Shared Buffer + OS Cache |
| MySQL InnoDB | 우회 (O_DIRECT) | 자체 Buffer Pool로 세밀한 제어 |
| Oracle | 우회 | 자체 SGA 버퍼 |
대부분의 경우 OS가 알아서 처리하므로 직접 신경 쓸 일은 거의 없습니다. 다만 Kafka, Elasticsearch 같은 소프트웨어를 사용할 때 "왜 이렇게 빠르지?", "메모리 설정은 왜 이렇게 권장하지?"라는 의문이 생긴다면, Page Cache가 어떻게 활용되고 있는지 살펴보면 좋습니다. 내부 동작 원리를 이해하면 성능 튜닝이나 장애 대응 시 더 나은 판단을 내릴 수 있습니다.