공통/인프라 & 시스템 설계

[대규모 서비스를 지탱하는 기술] OS 캐시 구조

반응형

대규모 서비스를 지탱하는 기술

OS 캐시 구조


페이지 캐시

OS에는 디스크 내의 데이터에 빠르게 액세스 할 수 있도록 하는 구조가 갖춰져 있다.

OS는 메모리를 이용해서 디스크 액세스를 줄인다. ⇒ OS 캐시

Linux의 경우는 페이지 캐시라고 하는 캐시 구조를 갖추고 있다.

 

페이지 구조

가상 메모리

OS는 가상 메모리 구조를 갖추고 있다. 가상 메모리 구조는 논리적인 선형 어드레스를 물리적인 물리 어드레스로 변환하는 것.

  • 가상 메모리 구조가 존재하는 가장 큰 이유는 물리적인 하드웨어를 OS에서 추상화하기 위해서이다. 가상 메모리를 통해 OS가 커널 내에서 메모리를 추상화하고 있다.
  • 이러한 이유로 프로세스에서 메모리를 다루기 쉽게하는 이점을 제공한다. (프로세스에게는 0x000과 같이 반드시 메모리의 특정 번지부터 시작한다고 정해져 있는 편이 다루기 쉽기 때문)

 

페이지

  • OS에서 디스크를 읽을때 메모리 1바이트씩 액세스 하는 것이 아니라 적당히 4KB 정도를 블록으로 확보해서 프로세스에 넘긴다. (여기서 1개의 블록을 페이지라고 한다.)
  • 즉 페이지란 OS가 물리 메모리를 관리하는 단위라고 할 수 있다.

 

Linux의 페이지 캐시 원리

OS는 확보한 페이지를 메모리상에 계속 확보해두는 기능을 갖고 있다.

프로세스가 디스크로부터 데이터를 읽어내는 과정

  • OS는 디스크로부터 4KB 크기의 블록을 읽어내서 메모리상에 위치시킨다. (프로세스는 디스크에 직접 액세스 할 수 없기 때문에, 프로세스가 액세스 할 수 있는 것은 가상 메모리이다.)
  • OS는 읽어낸 블록을 메모리에 쓰고, OS는 그 메모리 주소를 프로세스에 알려준다.
  • 그러면 프로세스가 해당 메모리에 액세스하게 된다.

 

이제 이후의 과정을 보자.

  • 데이터 읽기를 마친 프로세스가 더이상 데이터가 필요하지 않게 되었어도 OS는 페이지를 해제하지 않고 남겨둔다.
  • 그러면 다음에 다른 프로세스가 같은 디스크에 액세스 할 때는 남겨두었던 페이지를 사용할 수 있으므로 디스크를 읽으러 갈 필요가 없게 된다.
  • 이것이 페이지 캐시로, 커널이 한 번 할당한 메모리를 해제하지 않고 계속 남겨두는 것이 페이지 캐시의 기본이다.

 

페이지 캐시의 효과

  • 예외인 경우를 제외하고 모든 I/O에 투과적으로 작용한다.
  • Linux에서는 디스크에 데이터를 읽으러 가면 꼭 한 번은 메모리로 가서 데이터가 반드시 캐싱된다. 이러한 이유로 두 번째 이후의 액세스가 빨라진다. (Linux 뿐만 아니라 대부분의 현대 OS에서 비슷한 구조를 가짐)

 

정리하면

  • OS에서 디스크의 내용을 페이지 단위로 메모리에 읽어 들인다.
  • 작성된 페이지는 파기되지 않고 남는다. (페이지 캐시)
  • 이러한 구조로 OS에서는 디스크 I/O까지 가지 않고 메모리에서 처리할 수 있도록 한다.

 

VFS

파일 시스템

  • 위의 내용처럼 디스크의 캐시는 페이지 캐시에 의해 제공되지만, 실제 이 디스크를 조작하는 디바이스 드라이버와 OS 사이에는 파일 시스템이 끼어 있다.
  • Linux에는 ext3, ext2, ext4, xfs 등 여러 파일 시스템이 있는데 그 하위에 디바이스 드라이버가 있으며, 이 디바이스 드라이버가 실제로 하드디스크 등을 조작한다.


VFS

  • 파일 시스템 위에는 VFS (Virtual File System, 가상 파일 시스템)이라는 추상화 레이어가 존재한다.
  • 파일 시스템에는 다양한 함수를 갖추고 있는데, 그 인터페이스를 통일하는 것이 VFS의 역할이다.
  • VFS가 페이지 캐시의 구조를 지니고 있음. 이러한 이유로 어떤 파일 시스템을 이용하더라도, 어떤 디스크를 읽어내더라도 반드시 동일한 구조로 캐싱된다.

 

정리하면

VFS는 파일 시스템 구현의 추상화와 성능에 관련된 페이지 캐시 부분의 역할을 수행한다.


페이징 캐싱

디스크 상에 4GB 정도의 매우 큰 파일이 있고, 메모리가 2G 밖에 없는데 그중 500MB 정도를 OS가 프로세스에 할당했다고 하자.

이때 1.5GB 정도의 여유 메모리가 있는 상황에서 4G 파일을 캐싱할 수 있을까?

  • 파일 캐시라고 생각하면 파일 1개 단위로 캐싱하고 있어서 4GB는 캐싱할 수 없다고 생각할 수 있지만, 실제로는 그러지 않다.
  • OS는 읽어낸 블록 단위만으로 캐싱할 수 있는 범위가 정해진다. 디스크상에 배치되어 있는 4KB 블록만을 캐싱하므로 특정 파일의 일부분만, 읽어낸 부분만을 캐싱할 수 있다. 이렇게 디스크를 캐싱하는 단위가 페이지다.

 

LRU

  • 메모리 여유분이 1.5GB이 있고, 파일을 4GB 전부 읽게 되면 구조상으로는 LRU(Least Recently Used), 가장 오래된 것을 파기하고 가장 새로운 것을 남겨놓는 형태로 최근에 읽은 부분이 캐시에 남고 과거에 읽은 부분이 파기되어간다.

 

메모리를 늘려서 I/O 부하 줄이기

  • 메모리를 늘리면 캐시에 사용할 수 있는 용량이 늘어나고, 캐시에 사용할 수 있는 용량이 늘어나면 보다 많은 데이터를 캐싱할 수 있고, 많이 캐싱되면 디스크를 읽는 횟수가 줄어든다. (반드시 가능한 것은 아님)

 

I/O 부하를 줄이는 방법


캐시를 전제로 한 I/O 줄이는 방법

캐시에 의한 I/O 경감 효과는 매우 크다. 이 기본으로부터 도출할 수 있는 포인트가 두 가지 있다.

다루고자 하는 데이터의 크기에 주목하자.

  • 데이터 규모에 비해 물리 메모리가 크면 전부 캐싱할 수 있다.
  • 또한 대규모 데이터 처리에서 데이터를 압축해서 저장해두면 디스크 내용을 전부 그대로 캐싱해둘 수 있는 경우가 많다.

경제적인 비용과의 밸런스를 고려하자.

 

복수 서버로 확장.

메모리를 늘려서 전부 캐싱할 수 없는 규모인 경우 복수 서버로 확장시키는 방안을 생각해볼 수 있다.

웹 애플리케이션 3 계층 (프록시 - AP 서버 - DB)에서 DB 서버를 늘려야 할 때는 반드시 부하 때문만은 아니고 오히려 캐시 용량을 늘리고자 할 때 혹은 효율을 높이고자 할 때인 경우가 많다.

 

DB 확장 시 주의 사항

캐시 용량을 늘려야 한다고 했는데, 단순히 대수를 늘리는 것만으로는 안된다. 왜냐하면 캐싱할 수 없는 비율은 변함없이 그대로이기 때문이다.


국소성을 살리는 분산


국소성을 고려한 분산이란?

캐시 용량을 늘리기 위해 여러 대의 DB 서버로 확장시키려면 국소성(locality)을 고려해서 분산시켜야 한다.

  • 데이터를 그대로 복제하는 것이 아니라, 데이터에 대한 액세스 패턴을 고려해서 분산시키는 것을 국소성을 고려한 분산이라고 한다.

 

파티셔닝

국소성을 고려한 분산을 실현하기 위해서는 파티셔닝이라는 방법을 자주 사용한다.

  • 파티셔닝이란 한 대 였던 DB 서버를 여러 대의 서버로 분할하는 방법을 말한다

 

테이블 단위 분할

  • 테이블 단위로 분할하는 방법
  • 예를 들어 entry, bookmark, tag, keyword 테이블이 존재할 때, entry, bookmark와 tag, keyword로 분할해서 각기 다른 서버로 관리하도록 하는 분할 방법이다.
  • 테이블 단위로 분할했으면 entry, bookmark 테이블로의 요청은 어떤 서버로 tag나 keyword로의 요청은 다른 서버로 보내 처리될 수 있도록 애플리케이션을 변경할 필요가 있다.

 

테이블 데이터 분할

  • 테이블 하나를 여러 개의 작은 테이블로 분할하는 방법이다.
  • 예를 들어서 ID의 첫 문자로 파티셔닝을 하는 경우 a~c인 경우는 서버 1, d~f인 데이터는 서버2.. 이런 식으로 분할하는 방법이다.
  • 이 분할의 문제점은 분할의 입도를 크거나 작게 조절할 때 데이터를 한 번 병합해야 한다는 번거로움이 있다.
  • 이 점을 제외하면 애플리케이션에서 할 일은 ID의 첫 문자를 보고 액세스 할 DB서버를 분배하는 처리를 살짝 넣기만 하면 돼서 구현상으로는 간단하다.

 

요청 패턴을 '섬'으로 분할

용도별로 시스템을 섬으로 나누는 방법도 존재한다..

예를 들어 HTTP 요청의 User-Agent나 URL 등을 보고 통상의 사용자면 섬 1, 일부 API 요청이면 섬 2, 검색 봇이면 섬 3과 같은 식으로 나누는 방법.

  • 검색 봇은 사람의 경우라면 좀처럼 액세스 하지 않을 아주 오래된 웹 페이지에도 액세스 하고, 광범위하게 액세스 한다. 이러한 경우 캐시가 작용하기 어렵다. (동일한 페이지에 잇따라 방문하는 경우에는 캐시로 성능을 끌어올리기 쉽지만, 이처럼 광범위한 액세스에는 그럴 수가 없다)
  • 검색 봇에 대해 그렇게 빨리 응답할 필요는 없으며 따로 섬으로 나누고, 봇 이외에 액세스 (사용자로부터의 액세스)는 특정 페이지들에 액세스가 집중되므로 빈번하게 참조되는 부분은 캐싱하기 쉽다.

 

이렇게 캐싱하기 쉬운 요청, 캐싱하기 어려운 요청을 처리하는 섬으로 나누게 되면, 전자는 국소성을 인해 안정되고 높은 캐시 적중률을 낼 수 있게 된다.

 

페이지 캐시를 고려한 운용의 기본 규칙

OS 기동 직후에 서버를 투입하지 않는다.

  • 갑자기 배치하면 캐시가 없으므로 오직 디스크 액세스만 발생하게 된다.
  • 해결 방법으로는 OS를 시작해서 기동 하면 자주 사용하는 DB의 파일을 한 번 cat 해준다. 그렇게 하면 전부 메모리에 올라가므로 이후에 로드밸런서에 편입시킨다.

 

성능평가나 부하시험 때는 캐시가 최적화되었을 때 한다.

반응형