Linux Page Cache는 어떻게 동작할까? 메모리와 디스크 I/O의 관계
Linux 서버에서 free -h 명령어를 실행했을 때 예상보다 메모리 사용량이 높게 나타나는 경우가 있다. 실행 중인 애플리케이션이 많지 않은데도 메모리가 상당 부분 사용되고 있으면 메모리 부족을 의심하기 쉽다.
하지만 Linux는 남는 메모리를 그냥 비워두지 않는다. 사용 가능한 RAM을 Page Cache로 적극 활용해 디스크 I/O를 줄이고 전체 시스템 성능을 높인다.
따라서 서버 운영자가 메모리 상태를 정확하게 판단하려면 단순히 used 값만 보는 것이 아니라 Page Cache가 왜 만들어지고, 언제 회수되며, 실제 디스크 I/O와 어떤 관계를 갖는지 이해해야 한다.
Page Cache가 필요한 이유
CPU와 메모리는 매우 빠르게 데이터를 처리하지만 SSD나 HDD 같은 저장장치는 상대적으로 느리다.
애플리케이션이 파일을 읽을 때마다 실제 디스크에 접근한다면 디스크 응답을 기다리는 시간이 반복적으로 발생한다.
예를 들어 웹 서버가 동일한 정적 파일을 반복해서 제공한다고 가정해보자.
Client
↓
Nginx
↓
File Read
↓
Disk
매 요청마다 이 과정이 반복된다면 저장장치의 I/O 부하는 증가한다.
Linux는 이를 줄이기 위해 한 번 읽은 파일 데이터를 메모리에 보관한다.
첫 번째 요청
Application
↓
Page Cache 확인
↓ Cache Miss
Disk에서 데이터 읽기
↓
Page Cache 저장
↓
Application 전달
이후 동일한 데이터가 다시 필요하면 상황이 달라진다.
두 번째 요청
Application
↓
Page Cache 확인
↓ Cache Hit
RAM에서 데이터 전달
느린 디스크 접근 대신 빠른 메모리를 사용하는 것이다.
이 때문에 Linux 서버에서 메모리가 많이 사용되고 있다는 사실 자체가 반드시 문제가 되는 것은 아니다.
Linux Page Cache는 실제로 어떻게 동작할까?
Linux 커널은 파일 데이터를 페이지 단위로 관리한다.
일반적인 시스템에서 메모리 페이지 크기는 4KB인 경우가 많으며 다음 명령으로 확인할 수 있다.
getconf PAGE_SIZE
예를 들어 다음과 같이 출력될 수 있다.
4096
이는 기본 메모리 페이지 크기가 4096Byte라는 의미다.
애플리케이션이 파일을 읽으면 커널은 우선 해당 파일의 필요한 영역이 Page Cache에 존재하는지 확인한다.
캐시에 데이터가 있다면 디스크를 읽지 않고 메모리에서 바로 반환한다. 이를 Cache Hit라고 한다.
반대로 데이터가 없다면 Cache Miss가 발생하고 저장장치에서 데이터를 읽어 메모리에 적재한다.
여기서 중요한 점은 Page Cache가 특정 애플리케이션에 종속된 캐시가 아니라는 것이다.
Nginx가 읽었던 파일이라도 다른 프로세스가 같은 파일을 읽는다면 커널의 Page Cache를 활용할 수 있다.
즉 Page Cache는 Linux 커널이 시스템 전체에서 관리하는 파일 I/O 캐시다.
왜 free를 보면 메모리가 거의 다 사용된 것처럼 보일까?
다음과 같은 서버가 있다고 가정해보자.
free -h
결과가 다음과 비슷할 수 있다.
total used free shared buff/cache available
Mem: 16Gi 4.0Gi 1.0Gi 500Mi 11Gi 11Gi
Swap: 2.0Gi 0B 2.0Gi
free만 보면 1GB밖에 남지 않았기 때문에 메모리가 부족하다고 생각할 수 있다.
하지만 buff/cache에 11GB가 사용되고 있으며 available 역시 약 11GB다.
이 경우 실제 상황은 메모리 부족과 거리가 멀다.
Linux는 사용하지 않는 RAM 상당 부분을 캐시로 활용하고 있으며 애플리케이션이 추가 메모리를 요구하면 필요에 따라 이 캐시를 회수할 수 있다.
그래서 최근 Linux 서버에서는 단순한 free보다 available 값을 함께 보는 것이 중요하다.
free
→ 현재 완전히 비어 있는 메모리
available
→ Swap 사용 없이 새로운 프로세스가 사용할 수 있을 것으로 추정되는 메모리
Page Cache를 메모리 누수와 혼동하면 정상적인 서버에서도 불필요한 장애 대응을 하게 될 수 있다.
Page Cache와 디스크 Read I/O의 관계
Page Cache의 효과가 크게 나타나는 대표적인 환경은 동일한 파일을 반복적으로 읽는 서버다.
예를 들어 1GB 파일을 처음 읽어보자.
cat testfile > /dev/null
처음 실행할 때 해당 파일이 캐시에 없다면 실제 저장장치에서 데이터를 읽어야 한다.
이때 iostat을 함께 확인하면 디스크 Read I/O가 증가할 수 있다.
iostat -xz 1
여기서는 단순히 명령을 실행하는 것보다 다음 항목의 변화를 보는 것이 중요하다.
r/s
rkB/s
r_await
%util
r/s와 rkB/s가 증가한다면 실제 읽기 요청이 저장장치로 전달되고 있다는 의미다.
같은 파일을 다시 읽었을 때 Page Cache에 데이터가 남아 있다면 애플리케이션은 상당 부분을 메모리에서 가져올 수 있다.
따라서 애플리케이션에서는 파일을 읽고 있는데 실제 디스크의 Read I/O는 예상보다 낮게 나타날 수 있다.
이것이 논리적인 파일 읽기와 물리적인 디스크 읽기가 항상 동일하지 않은 이유다.
Write에서도 Page Cache가 사용된다
Page Cache는 파일 읽기에만 사용되는 것이 아니다.
Linux에서 일반적인 파일 쓰기는 데이터를 즉시 저장장치에 기록하는 방식으로만 동작하지 않는다.
애플리케이션이 데이터를 쓰면 먼저 메모리의 Page Cache에 반영될 수 있다.
Application
↓
write()
↓
Page Cache
↓
Dirty Page
↓
Kernel Writeback
↓
SSD / HDD
아직 실제 저장장치에 반영되지 않은 변경된 페이지를 Dirty Page라고 한다.
커널은 일정 조건이 충족되면 Dirty Page를 저장장치에 기록한다. 이것이 writeback이다.
현재 Dirty Page 상태는 다음과 같이 확인할 수 있다.
grep -E 'Dirty|Writeback' /proc/meminfo
예를 들어 다음과 같다.
Dirty: 12400 kB
Writeback: 512 kB
Dirty는 수정됐지만 아직 저장장치에 기록되지 않은 메모리이며, Writeback은 현재 저장장치로 기록되는 중인 메모리를 나타낸다.
따라서 애플리케이션의 write()가 빠르게 끝났다고 해서 반드시 실제 디스크 기록까지 완료됐다는 의미는 아니다.
데이터 영속성이 중요한 프로그램이 fsync() 같은 동기화 메커니즘을 사용하는 이유도 여기에 있다.
Page Cache 때문에 나타날 수 있는 실제 서버 증상
Page Cache는 성능을 높이는 중요한 기능이지만 메모리와 디스크 상태를 해석할 때 혼란을 만들기도 한다.
대표적인 현상이 서버 재부팅 직후 성능이 일시적으로 떨어지는 경우다.
재부팅하면 기존 Page Cache가 사라진다.
따라서 서비스가 다시 시작된 직후에는 필요한 데이터를 디스크에서 다시 읽어야 한다.
서버 재부팅
↓
Page Cache 초기화
↓
Cache Miss 증가
↓
Disk Read 증가
↓
I/O Latency 증가
↓
응답속도 일시 저하
시간이 지나면서 자주 사용하는 데이터가 다시 Page Cache에 올라오면 디스크 접근이 줄어들고 성능이 안정될 수 있다.
데이터베이스나 검색 서버처럼 대량의 데이터를 반복적으로 읽는 시스템에서는 이런 Cache Warm-up 현상이 더 크게 나타날 수 있다.
또 다른 상황은 대용량 파일 작업이다.
백업이나 로그 처리 과정에서 매우 큰 파일을 연속적으로 읽으면 Page Cache 구성이 크게 바뀔 수 있다.
그 결과 기존에 자주 사용되던 데이터가 캐시에서 밀려나고 이후 서비스 요청에서 다시 디스크 읽기가 발생할 수 있다.
따라서 단순히 메모리 사용률만 봐서는 이런 문제를 찾기 어렵다.
Page Cache 문제는 어떻게 분석해야 할까?
가장 먼저 전체 메모리 구조를 확인한다.
free -h
여기서는 used 하나만 보지 말고 다음을 함께 확인해야 한다.
available
buff/cache
swap
더 자세한 상태가 필요하면 /proc/meminfo를 확인한다.
cat /proc/meminfo
특히 다음 항목이 유용하다.
MemAvailable
Cached
Buffers
Dirty
Writeback
SwapTotal
SwapFree
하지만 Page Cache가 많다는 사실만으로 문제라고 판단해서는 안 된다.
다음 단계에서는 실제 디스크 I/O를 함께 확인한다.
iostat -xz 1
여기서 await, r_await, w_await, %util 등의 변화를 확인한다.
예를 들어 메모리의 available이 충분하고 디스크 지연도 낮다면 Page Cache가 많이 사용되고 있어도 정상일 가능성이 높다.
반대로 available이 지속적으로 감소하고 Swap 사용량이 증가하면서 디스크 지연까지 발생한다면 단순한 캐시 사용이 아니라 실제 메모리 압박을 의심해야 한다.
운영 환경에서는 메모리 지표와 I/O 지표를 분리해서 보지 않는 것이 핵심이다.
Page Cache를 강제로 비우는 것이 해결책일까?
Linux에서는 다음 인터페이스를 이용해 캐시를 강제로 회수하도록 요청할 수 있다.
sync
echo 3 > /proc/sys/vm/drop_caches
하지만 운영 서버에서 메모리가 많이 사용된다는 이유만으로 이 명령을 실행하는 것은 좋은 대응 방법이 아니다.
Page Cache는 불필요하게 메모리를 차지하는 데이터가 아니라 디스크 접근을 줄이기 위한 성능 자원이기 때문이다.
캐시를 강제로 비우면 이후 요청에서 Cache Miss가 증가한다.
drop_caches
↓
Page Cache 감소
↓
Cache Miss 증가
↓
Disk Read 증가
↓
I/O 부하 증가
결국 메모리 숫자는 보기 좋게 낮아졌지만 실제 서비스 성능은 오히려 떨어질 수 있다.
drop_caches는 일반적인 메모리 최적화 방법이 아니라 벤치마크처럼 캐시 영향을 제거해야 하는 특수한 상황에서 제한적으로 사용하는 것이 적절하다.
운영 서버에서는 먼저 왜 메모리가 사용되고 있는지 분석하는 것이 우선이다.
Page Cache 문제를 해결할 때 중요한 운영 원칙
첫 번째는 높은 메모리 사용률 자체를 장애로 판단하지 않는 것이다.
Linux 서버에서 RAM은 비워두기 위한 자원이 아니다. 사용하지 않는 메모리를 Page Cache로 활용하는 것은 정상적인 커널 동작이다.
두 번째는 free보다 available을 함께 확인하는 것이다.
세 번째는 Swap과 디스크 I/O를 같이 보는 것이다.
free -h
vmstat 1
iostat -xz 1
vmstat에서는 특히 si, so를 확인할 수 있다.
si = swap in
so = swap out
이 값이 지속적으로 발생한다면 실제 메모리 압박 가능성을 확인해야 한다.
네 번째는 애플리케이션의 메모리 사용량과 Page Cache를 구분하는 것이다.
프로세스의 RSS가 계속 증가하는 상황과 Cached가 증가하는 상황은 원인이 전혀 다를 수 있다.
따라서 서버 메모리 문제를 해결할 때는 단순히 “메모리 사용률이 90%다”라는 숫자에서 시작하는 것이 아니라 누가 메모리를 사용하고 있고 그 메모리가 회수 가능한가를 확인해야 한다.
정리
Linux의 Page Cache는 남는 RAM을 이용해 파일 데이터를 저장하고 느린 디스크 접근을 줄이는 핵심 메커니즘이다.
파일을 처음 읽을 때는 저장장치 접근이 필요하지만 이후 동일한 데이터가 Page Cache에 존재한다면 RAM에서 빠르게 가져올 수 있다. 파일 쓰기 역시 Page Cache와 Dirty Page, writeback 과정을 거칠 수 있다.
따라서 다음과 같은 상황은 반드시 구분해야 한다.
메모리 사용량이 높다
↓
Page Cache가 많은가?
↓
MemAvailable은 충분한가?
↓
Swap이 발생하는가?
↓
Disk I/O 지연이 발생하는가?
↓
실제 Memory Pressure인가?
운영자가 봐야 할 핵심은 **”메모리를 얼마나 사용하고 있는가”가 아니라 “필요할 때 회수할 수 있는 메모리가 얼마나 남아 있는가”**다.
Page Cache를 이해하면 free -h에서 메모리가 거의 가득 찬 것처럼 보이는 현상뿐 아니라 서버 재부팅 후 성능 저하, 대용량 파일 처리 후 I/O 증가, Dirty Page와 writeback으로 인한 쓰기 지연까지 하나의 흐름으로 이해할 수 있다.
결국 Linux 서버에서 메모리와 디스크 I/O는 서로 독립된 자원이 아니다. Page Cache를 중심으로 긴밀하게 연결되어 있으며, 이 관계를 함께 분석해야 실제 서버 병목을 정확하게 찾을 수 있다.




댓글 0
첫 댓글을 남겨보세요.