Buffer/Page Cache 이미지

버퍼/페이지 캐시 차이와 튜닝 및 비우기 방법 (부가 : Dirty page 설명)

Table of Contents


학습 목표

리눅스 시스템의 Cache 메모리 관리에 대해 이해하고 상용 환경에서 어떻게 관리해야 할 지 이해하는 것을 목표로 한다.

  1. 메모리 관리 기초 이해 – 버퍼와 캐시의 차이점과 역할 이해
  2. Dirty Page 개념 이해 – Dirty Page의 정의와 cache 메모리와의 연관성
  3. 캐시 메모리 커널 튜닝 방법 학습 – 시스템 성능 향상을 위한 메모리 튜닝 방법 습득
  4. 캐시 메모리 비우기 – drop_caches의 위험성과 사용 예시

01. 메모리에서의 캐싱 영역

커널은 블록 디바이스라고 부르는 디스크로부터 데이터를 읽거나 사용자의 데이터를 디스크에 저장한다. 하지만 디스크는 다른 장치들에 비해 매우 느리기 때문에 디스크에 대한 요청을 기다리는 시간이 상당히 많이 소요되고, 이로 인해 시스템에 부하가 일어나기도 한다.

커널은 이렇게 상대적으로 느린 디스크에 대한 요청을 좀 더 빠르게 하기 위해 메모리의 일부를 캐싱 영역으로 할당해서 사용한다. 즉, 한 번 읽은 디스크의 내용을 메모리에 저장해 두어 동일한 내용을 다시 읽고자 할 때 디스크로 요청하지 않고 메모리에 요청하게 된다.

이런 캐싱 기능을 통해서 커널은 다수의 디스크 요청을 좀 더 빠르게 처리할 수 있다. 그리고 이때 사용되는 캐싱 영역을 buffers, cached라고 부른다.


02. free 명령어 결과 설명

컬럼설명
free어느 누구도 사용하고 있지 않은 메모리
available누군가 사용하고 있지만 어플리케이션에서 필요로 하다면 실질적으로 할당 가능한 메모리
buffer블록 디바이스에 가지고 있는 파일 시스템의 메타 데이터를 저장하는 영역
cache파일의 내용을 저장하는 영역

03. buffers와 cached의 상세 설명

버퍼 캐시와 페이지 캐시의 차이

03-1. Buffer Cache

블록 디바이스에 가지고 있는 파일 시스템의 메타 데이터를 저장하는 영역

파일의 내용이 아닌 파일 시스템을 관리하기 위한 super block, inode block같은 메타 데이터를 읽어올 때는 bio 구조체를 사용하지 않고 _get_blk()와 같은 내부 함수를 통해 블록 디바이스와 직접 통신 한다. 이때 가져온 블록 디바이스의 특정 블록 내용을 Buffer Cache 영역에 저장해 둔다.

03-2. Page Cache

파일의 내용을 저장하는 영역

커널이 읽어야 할 데이터가 파일의 내용이라면 커널은 bio 구조체를 만들고 해당 구조체에 Page Cache 용도로 할당한 메모리 영역을 연결해준다. 이후 bio 구조체는 디바이스 드라이버와 통신해서 디스크로부터 데이터를 읽어 page Cache에 파일의 내용을 채운다.


04. 페이지 캐시 동작 과정을 통한 이해

04-1. Page Cache가 없을 때

페이지 캐시가 없을 경우 블록 장치에서 직접 어플리케이션으로 데이터를 읽어 들인다.
처음 데이터를 읽어들이는 상황에서는 페이지 캐시가 있든 없든 속도의 차이가 없을 것이다.

Page Cache가 없을 때

그러나 동일한 데이터를 2회 이상 읽어 들이는 경우, 매번 속도가 느린 디스크로 부터 데이터를 가져와야 하기 때문에 성능 문제가 발생한다.

Page Cache가 없을 때

04-2. Page Cache가 있을 때

페이지 캐시가 있을 때는 데이터를 읽어들여 페이지 캐시에 저장한다. 그리고 어플리케이션이 페이지 캐시에서 데이터를 가져간다. 여기까진 처음 데이터를 읽어들이는 상황이기에 페이지 캐시가 없는 경우와 큰 차이가 없다

Page Cache가 있을 때

하지만 동일한 데이터를 2회 이상 읽어 들이는 경우, 블록 디바이스보다 빠른 속도를 가진 메모리에 데이터가 페이지 캐시 형태로 저장되어 있으니 CPU(또는 어플리케이션)에서 더욱 빠르게 읽어 들일 수 있다.

Page Cache가 있을 때

05. buff/cache 영역이 해제되는 경우

buff/cache는 I/O 성능 향상을 위해 항상 존재한다. 그러나 어플리케이션에서 메모리를 필요하다면, buff/cache 영역을 해제하여 어플리케이션이 사용할 수 있는 영역으로 변환한다. 이는 메모리 부족으로 인한 OOM(Out Of Memory) 현상을 방지하기 위해서이다.

available 메모리 영역은 보통 free + buff/cache 로 계산하면 얼추 비슷하다.

또한, buff/cache가 높은 서버는 I/O가 많이 발생하는 환경일 가능성이 크다. 이런 경우 메모리 증설을 고려하는 것이 좋다.


06. Buffer cache 및 Page cache와 Dirty page의 관계 설명

이번 섹션 들어가기전에 페이지(Page)를 이해하자
메모리를 관리하기 위해 고정된 크기로 나눈 작은 블록이다. 메모리를 효율적으로 관리하기 위해 메모리를 페이지 단위로 나누며, 일반적으로 4KB, 8KB, 2MB 등의 고정 크기를 가진다.

페이지의 여러 역할이 있지만 여기서는 메모리 할당의 효율성만 설명하겠다. 페이지를 사용하면 메모리를 고정 크기로 나누어 관리할 수 있으므로, 메모리 할당 및 해제가 효율적으로 이루어 질 수 있다. 이는 메모리 단편화를 줄이는데 도움이 된다.

06-1. 더티 페이지(Dirty page)란 ?

더티 페이지란  메모리에서 데이터가 수정되었지만 아직 디스크에 기록되지 않은 데이터를 의미한다. 이러한 페이지는 메모리 캐시의 일부로 존재하며, 주로 Buffer 캐시와 Page 캐시에 나타난다.

예를 들면 어플리케이션이나 시스템 프로세스가 데이터를 수정했을 때 데이터가 메모리에만 존재하고 아직 디스크에 반영되지 않은 상태를 나타낸다.

즉, 더티(Dirty)라는 용어는 수정되었다는 의미로 사용된다. 메모리에 있는 Clean한 상태의 데이터가 수정되면 Dirty 상태가 된다고 이해하면 될 것 같다.


06-2. Buffer cache에서의 Dirty page

버퍼 캐시는 파일 시스템의 메타 데이터를 포함한 블록을 저장한다고 설명하였다.

이 때 발생될 수 있는 더티 페이지는 파일 시스템의 메타 데이터가 수정되었을 때이다. 이 수정된 블록은 버퍼 캐시에 더티 페이지로 표시된다.

예를 들면 inode 또는 superblock이 변경되면 변경 사항을 Buffer cache에 dirty page로 저장시킨다.


06-3. Page cache에서의 Dirty page

페이지 캐시는 실제 데이터를 저장한다.

버퍼 캐시때와 동일하게 페이지 캐시에서도 파일의 내용이 수정되면 더티 페이지가 된다. 수정된 데이터는 페이지 캐시에 더티 페이지로 표시된다.

예를 들면 vi 어플리케이션에서 텍스트 파일을 수정하면 이 수정된 내용은 Page cache에 Dirty page로 저장된다.


06-4. Dirty page가 중요한 이유

더티 페이지는 수정된 데이터를 의미하기에 디스크에 기록되지 않으면 갑작스러운 시스템 종료 등의 상황에 디스크에 저장하지 못하여 데이터 손실이 발생할 수 있다. 

이를 방지하기 위해 커널은 주기적으로 더티 페이지를 디스크에 기록하는 작업을 수행한다.

더티 페이지가 저장되는걸 눈으로 보는 방법
/proc/meminfo에서 Dirty라는 항목을 보면 된다. Buffer/Page cache에서 수정된 내용이 아직 디스크에 저장되지 않았다면 값이 표시될 것이다. 이를 0으로 만드려면 sync 명령어를 사용하면 된다. watch로 관찰하게 되면 값이 0으로 되는 것을 볼 수 있을 것이다.

07. 커널 파라미터를 이용한 Dirty page 관리

앞선 내용에서 Buffer / Page cache에서 수정된 데이터가 Dirty page로 분류된다는 것을 배웠다. 그렇다면 더티 페이지가 더 많이 쌓이지 않게 조절하는 방법을 배워보자.

여기서 배울 내용은 메모리의 사용량이 너무 높아지는 것을 방지하고, 데이터의 일관성을 유지하기 위해 설정한다.

사용하는 커널 파라미터

  • vm.dirty_ratio
  • vm.dirty_background_ratio

07-1. vm.dirty_ratio

설명

시스템 전체 메모리 중 더티 페이지(수정된 페이지)가 차지할 수 있는 최대 비율을 설정

수정된 페이지들이 시스템 메모리의 몇 퍼센트까지 차지할 수 있는지를 지정

작동 원리

설정된 비율을 초과하게 되면, 커널은 가에로 더티 페이지들을 디스크로 기록(플러시)한다.

예를 들어 메모리가 64GB이고 vm.dirty_ratio가 10으로 설정되어 있다면, 더티 페이지가 6.4GB를 초과할 때 커널은 강제로 이 페이지들을 기록(플러시)한다.

설정 방법

일시적으로 설정

sudo sysctl -w vm.dirty_ratio=10

영구적으로 설정

sudo vi /etc/sysctl.conf

# 다음  추가
vm.dirty_ratio = 10

# 설정 적용
sudo sysctl -p

07-2. vm.dirty_background_ratio

설명

시스템 전체 메모리 중 더티 페이지가 차지할 수 있는 비율을 설정하는 또 다른 커널 파라미터

이 비율은 백그라운드에서 더티 페이지들을 디스크로 플러시하기 시작하는 기준 비율을 의미

사용자 애플리케이션의 성능에 영향을 주지 않으면서, 데이터가 디스크로 꾸준히 기록되도록 하기 위한 것

작동 원리

이 비율을 초과하면, 커널은 백그라운드에서 자동으로 더티 페이지들을 디스크로 기록(플러시)한다.

예를 들어 시스템 메모리가 64GB이고 vm.dirty_background_ratio가 5로 설정되어 있다면, 수정된 페이지가 3.2GB를 초과할 때 커널은 백그라운드에서 이 페이지들을 디스크로 기록한다.

설정 방법

일시적으로 설정

sudo sysctl -w vm.dirty_background_ratio=5

영구적으로 설정

sudo vi /etc/sysctl.conf

# 다음  추가
vm.dirty_background_ratio = 5

# 설정 적용
sudo sysctl -p

07-3. vm.dirty_ratio와 vm.dirty_background_ratio 차이점

차이점

vm.dirty_ratio는  더티 페이지의 최대 비율을 초과할 때 강제로 디스크로 플러시하는 기준이다.
반면 vm.dirty_background_ratio는  백그라운드에서 플러시 작업을 시작하는 기준이다.

성능 영향

vm.dirty_ratio는 강제로 기록(플러시)하기에 성능에 급격한 영향을 줄 수 있다.
반면 vm.dirty_background_ratio는 백그라운드에서 작업이 이루어지므로 성능에 영향을 덜 준다.


07-4. vm.dirty_ratio와 vm.dirty_background_ratio의 적정 값 찾기

위에서 설명한 예시는 Dirty page가 쌓이는 용량이 크게 느껴질 수 있다. 따라서 실제 시스템에 최적화된 값을 찾기 위해서는 실제 운영중인 서버에서 모니터링하고 조정하는 방법으로 적절한 값을 찾을 수 있다.

  • 기본 값으로 시스템을 운영하면서 I/O 성능 및 메모리 사용량을 모니터링

모니터링 방식

vmstat, iostat, free, /proc/meminfo 등의 도구를 사용하여 메모리와 I/O 성능을 모니터링

적정 값으로 낮춰서 설정할 경우의 장점

  • 더티 페이지가 자주 디스크에 기록되기에 Kernel panic이나 전원 장애 등의 장애 상황 시 데이터 손실 가능성이 줄어든다.
  • 중요한 데이터를 더 빨리 디스크에 기록할 수 있기에 데이터 일관성이 중요한 어플리케이션에서 유리할 수 있다.
  • 적절한 값을 찾았다면 어플리케이션에서 디스크 쓰기 작업을 대기하는 시간이 줄어들기에 시스템의 응답 시간이 개선될 수 있다.
  • 더티 페이지가 많이 쌓이는 경우에 플러시 작업 중 일시적인 부하가 발생할 수 있는데, 자주 기록하게 되면 일시적 부하 상황을 피할 수 있다.

너무 낮은 값으로 설정했을 경우의 단점

  • 디스크 I/O 증가
    • 잦은 기록(플러시) 작업 발생
    • 디스크 대시 시간 증가 : 잦은 플러시 작업으로 인해 디스크에 대한 I/O 요청이 많아지면서 디스크 대기 시간이 길어질 수가 있다. 특히 디스크가 느리거나 I/O를 많이 사용하는 서버인 경우 성능 저하 발생할 수 있다.
  • CPU 사용량 증가
    • 플러시 작업은 CPU 리소스를 사용하기에 추가적인 CPU 작업이 발생하여 CPU 사용량이 증가할 수 있다.
  • 어플리케이션 성능 저하
    • 잦은 플러시 작업으로 I/O 대기 시간이 증가하여 어플리케이션이 디스크 I/O를 대기하는 시간이 증가할 수 있다. 특히 어플리케이션에서 Page cache에 대한 Dirty page를 발생 시키는 것을 잊지 말자.
    • 더티 페이지가 메모리에 오래 머무르지 못하고 자주 디스크로 기록되면 어플리케이션이 실제로 사용할 수 있는 메모리 효율성이 떨어질 수  있다.

08. drop_caches를 이용한 Cache 메모리 비우기

이번 섹션을 설명하기 전에 drop_caches를 이용한 캐시 비우기는 상용 환경에서는 권장하지 않는다는 것을 강조하고 싶다.

아래는 레드햇 리눅스 사이트에서 발췌한 내용이다.

리눅스에서 drop_cache 발췌한 내용

08-1. Production 환경에서 drop_caches가 권장되지 않는 이유

성능 저하 영향

캐시는 자주 사용되는 데이터를 메모리에 저장하는 공간이다. 이를 drop_caches를 이용하여 비우게 되면 어플리케이션이 디스크에서부터 데이터를 읽어와야 하기에 응답 시간이 증가된다. 또한 자주 사용되는 데이터를 다시 읽어와야 하기에 디스크 I/O도 증가될 수 있다. 특히 디스크가 느리거나 I/O 활용을 많이 하는 서버 환경의 경우 큰 성능 저하가 발생될 수 있다.

일시적인 해결책

drop_caches가 근본적인 문제 해결책이 아닌 일시적인 해결책이기 때문도 있다. 만약 메모리 부족에 의해 drop_caches를 사용했다면 일시적으로 메모리를 확보하는 것이며, 결국 시스템이 다시 데이터를 캐시로 가져오기에 원래 상태로 되돌아간다.

데이터 일관성 및 안정성 문제

마지막으로 Dirty page를 이해했다면, 데이터 일관성 및 안정성 문제도 발생할 수 있다.

Buffer 및 Page cache가 dirty page로 되었을 경우 디스크에 기록이 되어야 한다. sync를 사용해서 디스크로 플러시 하여도 엄청난 속도로 작업을 수행하는 컴퓨터가 잠깐의 사이에 중요한 데이터를 dirty page로 만들었을 수 있기 때문이다.

예를 들면 데이터베이스 같이 중요한 트랜젝션을 처리하는 시스템에서는 데이터를 디스크에 제대로 쓰지 못하는 문제가 발생하여 데이터 무결성 문제를 발생시킬 수 있다. 그 외에도 여러 예상치 못한 문제를 초래할 수 있다.

필자의 실제 경험 – 장애 상황

실제 필자가 경험한 경우는 Kubenetes 를 운영하는 테스트 환경에서 drop_caches를 사용했다가 일시적으로 node 상태를 파악하지 못하여 노드가 순간적으로 떨어진 경우가 있다. drop_caches를 사용하면서 시스템 메모리 관리 혹은 기타 장치(CPU, 디스크 I/O 등)에 일시적인 부하가 발생하여 노드 상태를 파악하지 못하였을 수 있을 것이다. 아마 Kubernetes API 서버나 kubelet이 메모리 부족 상태에 빠지면서 클러스터의 노드 상태를 정상적으로 상태 파악이 되지 않았을 수 있었을 것으로 예상된다.


08-2. 어떠한 상황에서 drop_caches를 사용하는가 ?

성능 테스트

상용 환경에서는 권장되지 않지만 테스트 환경이라면 말이 달라진다. 시스템 성능 테스트를 수행할 때 캐시를 비워서 깨끗한 상태에서 시작하여 테스트의 일관성 및 재현성을 확보할 수 있다.

또한 특정 작업에서 캐시의 영향을 평가하기 위해 캐시를 비우고 성능 변화를 관찰할 수 있다.

필자의 실제 경험 – 성능 저하 원인 분석

빅데이터 어플리케이션이 올라간 서버에서 간할적으로 성능 저하가 되는 문제가 있었다. 분석해보니 Buffer / Page cache 영역이 과도하게 많이 사용하고 있었다. 이에 주기적으로 drop_caches를 사용하게 설정을 하니 문제가 해결되었다.

그러나 이는 일시적인 해결책이기에 다음과 같은 해결 과제를 제안하였다.

  • 버퍼/페이지 캐시 사용 공간이 여유있을 정도의 메모리 증설 검토
  • 어플리케이션에서 캐시 메모리를 효율적으로 관리하도록 최적화 개발 검토
어플리케이션 문제였는지 확인하는 방법은 간단하다. 어플리케이션 서비스를 종료한 상태에서 캐시 메모리가 얼마나 증가하는지 확인하고, 어플리케이션 서비스를 올린 상태에서 캐시 메모리 상태를 관찰하면 된다. 필자의 경우 빅데이터 어플리케이션이 캐시 메모리를 증가시킨다는 것을 확인 하였다.

일시적인 메모리 확보

시스템에서 메모리를 과도하게 많이 사용하여 성능이 저하되는 경우 캐시 메모리를 비워서 일시적으로 문제를 해결할 수 있다. 

대규모 작업 후 캐시 메모리 정리 

백업이나 대규모 파일 복사 작업 후 사용하지 않는 캐시를 비울 수 있다.

메모리 누수 문제 분석 (선택 사항)

어플리케이션에서 메모리 사용 후 제대로 해제하지 않아서 메모리 누수가 발생하는 경우 캐시 메모리를 비워서 사용량을 리셋하고 분석할 수 있다. 그러나 Memory leak 문제 분석을 위한 하나의 방법이기에 Case에 따른 선택 사항에 불과하다.

만약 Production 환경에서 메모리 누수가 발생한다면, drop_caches로 일시적으로 메모리를 확보하여 임시 방편으로 대응할 수도 있을 것이다.

참고로 메모리 누수는 일반적으로 어플리케이션에서 발생하기에 사용자 공간(User Space) 에서 관찰할 수 있다. 그러나 커널도 프로그램이기에 버그가 있는 버전이면 메모리 누수가 발생할 수 있다.

08-3. drop_caches 사용 방법

리눅스는 /proc/sys/vm/drop_caches 파일을 통해 캐시 데이터를 삭제할 수 있다.

앞서 설명한 Dirty page를 잘 이해하였다면, 캐시 데이터를 삭제하기 전에 sync 명령을 사용하여 디스크의 Dirty page를 플러시(기록)하는 것이 좋다는 것을 이해했을 것이다.

따라서 캐시 메모리 비우기는 아래와 같이 진행하면 된다.

Step 01. 메모리에서 수정된 내용을 강제로 디스크로 기록 (데이터 동기화)

sync

Step 02. 필요한 값으로 캐시 데이터 삭제

# 페이지 캐시 삭제
echo 1 | sudo tee /proc/sys/vm/drop_caches

# dentries와 inodes 삭제
echo 2 | sudo tee /proc/sys/vm/drop_caches

# 페이지 캐시, dentries, inodes 모두 삭제
echo 3 | sudo tee /proc/sys/vm/drop_caches

‘1’은 Page cache를 삭제
‘2’는 디렉토리 엔트리(dentries)와 파일 시스템 객체(inode)를 삭제 (buffer cache를 비우는 것)
‘3’은 Page cache 및 dentries, inodes 모두를 삭제


09. 어떤 어플리케이션이 캐시 메모리를 많이 사용할까 ?

Page cache를 많이 사용하는 어플리케이션은 주로 대규모 데이터 처리, 검색, 분석, 저장을 수행하는 시스템이다. 이러한 어플리케이션들은 높은 성능을 위해 page cache를 활용하여 디스크 I/O를 줄이고 메모리 접근 속도를 극대화 시킨다.

대표적으로 아래와 같은 어플리케이션이 있다.

  • Elasticsearch (엘라스틱서치)
  • MongoDB
  • Redis
  • Hadoop HDFS
  • 등등

반면, Buffer Cache를 많이 사용하는 어플리케이션은 파일 시스템의 메타 데이터(inode 등) 및 실제 데이터 블록을 저장하여 디스크 I/O를 줄이고 성능을 최적화 시킨다. 즉, 파일 시스템과 관련된 작업을 많이 수행하는 어플리케이션 서버가 버퍼 캐시를 많이 사용하게 된다.

대표적으로 아래와 같은 어플리케이션이 있다.

  • 파일 서버 (Samba, NFS)
  • 웹 서버 (웹 페이지, 이미지, 스크립트를 자주 읽고 쓰기 때문)
  • 백업 및 복구 솔루션 (대량의 파일을 주기적으로 읽고 쓰기 때문)
  • 등등

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다