페이지 캐시와 버퍼 캐시 — 리눅스 IO 캐싱 구조
리눅스에서
free명령을 치면 buff/cache 항목이 있습니다. 이 메모리는 낭비가 아니라, 디스크 I/O를 줄이기 위한 캐시입니다. 이 구조를 이해하면 시스템 성능 튜닝의 기본이 보입니다.
리눅스 I/O 캐싱 구조
애플리케이션
│
│ read() / write()
▼
VFS (Virtual File System)
│
▼
페이지 캐시 (Page Cache)
│
│ 캐시 미스 시
▼
파일 시스템 (ext4, xfs 등)
│
▼
블록 I/O 레이어
│
▼
디바이스 드라이버
│
▼
디스크 (HDD/SSD)
페이지 캐시 (Page Cache)
디스크에서 읽은 파일 데이터를 메모리에 캐싱 합니다. 페이지(4KB) 단위로 관리합니다.
읽기 과정
read() 호출
│
▼
페이지 캐시에 있나? ──── Yes ──→ 캐시에서 바로 반환 (캐시 히트)
│
No (캐시 미스)
│
▼
디스크에서 읽기 → 페이지 캐시에 저장 → 애플리케이션에 반환
쓰기 과정
write() 호출
│
▼
페이지 캐시에 쓰기 (Dirty Page로 표시)
│
│ write()는 여기서 반환 (빠름!)
│
▼
커널의 pdflush/writeback 스레드가
나중에 디스크에 비동기 기록
- Write-Back 방식: 쓰기 시 캐시에만 쓰고, 나중에 디스크에 반영
dirty_expire_centisecs: dirty 페이지가 디스크에 기록되기까지의 최대 대기 시간 (기본 3000 = 30초)fsync()를 호출하면 즉시 디스크에 기록
버퍼 캐시 (Buffer Cache)
블록 디바이스의 메타데이터를 캐싱 합니다. 현대 리눅스에서는 페이지 캐시에 통합되었습니다.
과거 (2.4 이전):
페이지 캐시: 파일 데이터 캐싱
버퍼 캐시: 블록 디바이스 메타데이터 캐싱 (별도 관리)
현재 (2.4 이후):
페이지 캐시에 통합 — 버퍼는 페이지 캐시의 일부로 관리
# free 명령에서 확인
$ free -h
total used free shared buff/cache available
Mem: 16Gi 8Gi 2Gi 500Mi 6Gi 7Gi
# buff/cache 6GB: 디스크 캐시로 사용 중
# available 7GB: 실제로 사용 가능한 메모리 (캐시는 필요 시 해제됨)
캐시 관련 커널 파라미터
# 더티 페이지 비율 (전체 메모리의 %)
cat /proc/sys/vm/dirty_ratio # 동기 쓰기 전환 임계값 (기본 20%)
cat /proc/sys/vm/dirty_background_ratio # 백그라운드 기록 시작 임계값 (기본 10%)
# 캐시 수동 비우기 (운영 중에는 사용하지 않는 것이 좋음)
echo 1 > /proc/sys/vm/drop_caches # 페이지 캐시 비우기
echo 2 > /proc/sys/vm/drop_caches # 슬랩 캐시(dentry, inode) 비우기
echo 3 > /proc/sys/vm/drop_caches # 둘 다 비우기
Readahead (미리 읽기)
순차 읽기 패턴을 감지하면 커널이 미리 다음 데이터를 읽어 캐시에 올립니다.
# readahead 크기 확인 (섹터 단위, 512바이트)
cat /sys/block/sda/queue/read_ahead_kb # 기본 128KB
# 대용량 순차 읽기 시 증가
echo 2048 > /sys/block/sda/queue/read_ahead_kb
Direct I/O
페이지 캐시를 우회하여 디스크에 직접 I/O합니다.
int fd = open("file", O_RDWR | O_DIRECT);
// 캐시 없이 디스크 직접 접근
// 데이터베이스가 자체 캐시를 관리할 때 사용 (MySQL InnoDB 등)
- 데이터베이스는 자체 버퍼 풀이 있으므로 OS 페이지 캐시와 이중 캐싱을 피하기 위해 Direct I/O 사용
핵심 포인트
- free 명령의 buff/cache: 낭비가 아니라 캐시 — available을 봐야 실제 여유 메모리
- Write-Back vs Write-Through: 리눅스는 Write-Back — 성능은 좋지만 정전 시 데이터 손실 가능
- DB가 Direct I/O 쓰는 이유: 자체 캐시와 OS 캐시의 이중 캐싱 방지
- fsync()의 중요성: WAL이나 트랜잭션 로그는 반드시 fsync()로 디스크 기록 보장
정리
리눅스 페이지 캐시는 디스크 I/O 성능의 핵심입니다. 남는 메모리를 자동으로 캐시로 활용하고, 필요하면 해제합니다. 시스템 모니터링에서 "메모리가 다 찼다"고 당황하기 전에 buff/cache를 확인하는 것이 기본입니다.