ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • CPU 부하와 I/O 부하
    도서/대규모 데이터를 다루는 기술 2022. 8. 31. 22:10

    웹 애플리케이션이 수행될 때 HTTP 요청을 받아 DB 질의하고, 가공해 클라이언트에 응답을 주는 일은

    기본적으로 CPU 부하만 소요된다. 

    반면 DB 측면에서는 I/O 부하가 걸린다.

     

    CPU 에 부하를 주는 프로그램을 CPU 바운드한 프로그램, 반면 I/o 에 부하를 주는 프로그램을 I/O 바운드한 프로그램이라 한다.

     

    CPU 부하에 대해서는 새로운 서버를 원래 있던 서버와 동일한 구성으로 복사해 추가하고 로드밸런서로 이를 분산하는 것으로 해결이 비교적 어렵지 않다.

    하지만 I/O 부하일 경우 DB 서버 한대를 더 추가한다면 데이터 동기화는 어떻게 할 것인가라는 문제가 생긴다.

     

    대규모 프로그램을 작성하는 요령3

    1. 디스크 seek 횟수 최소화, 메모리 사용

    2. 데이터량에 강한 알고리즘 사용, 선형검색을 이분검색으로 ex) Log Order

    3. 데이터 압축과 검색기술 

     

    3대 전제 지식

    1. OS캐시 

    2. 분산을 고려한 RDBMS 운용

    3. 대규모 환경에서 알고리즘과 데이터 구조 

     

    1. OS 캐시 

    - Libux 페이징 구조 

    - OS의 가상메모리 구조 

    OS 의 가상메모리 구조는 논리적 선형 어드레스는 물리적 어드레스로 변환하는 것이다.

    가상메모리 구조는 물리적 하드웨어를 OS 에서 추상화하기 위해서다.

     

    프로세스에서 물리 어드레스로 바로 접근하지 못하고 OS 를 통해 메모리의 논리적 주소 받아 사용한다.

    디스크를 OS가 4kb 정도씩 한번에 읽어내는 것처럼메모리도 1바이트씩 액세스 하지 않고 4KB 정도를 블록으로 확보해 프로세스에 넘긴다. 이 1개의 블록을 페이지라고 한다.

     

    OS는 하드웨어에서 디스크를 읽어 메모리상에 위치시킨다. 그리고 메모리 주소를 프로세서에 넘겨주고 메모리에 데이터는 보관한다. 그럼 다른 프로세스가 동일한 페이지를 필요로 할 때 디스크로 다시 갈 필요 없이 메모리에 읽어 주는데 이것이 페이지 캐시이다.

    LRU (Least Recently Used) 알고리즘을 통해 메모리가 부족하면 오래된 것을 파기하기 때문에 OS, DB 를 멈추지 않고 계속 구동시키면 캐시가 점점 최적화되며 I/O 부하가 줄어든다.

     

    페이지 캐시를 사용해 데이터를 완전히 메모리에 올릴 수 있다면 더의 모든 액세스를 메모리로부터 읽어낼 수 있으므로, 

    DB 메모리를 늘려 I/O 부하를 줄일 수 있다.

     

    최근 일반적인 서버 한 대의 메모리 구성은 8~16GB 이다. 

    AP 서버는 메모리가 그렇게 많이 필요하지 않아 4GB정도이지만, DB 는 그 정도 메모리가 탑재되어 있다. 

     

    단순히 DB 대수만 늘려서는 확장성을 확보할 수 없다.

    동일한 스팩의 서버를 추가하면 애초에 대용량 데이터에 캐시 용량이 부족한 부분은 추가된 서버에서도 동일하게 가져가기 때문에, 증설비용에 비해서 성능향상이 극히 부족하다.

    확장성을 확보하기 위해 서버를 늘리면 10배~100배정도는 빨라져야 한다.

     

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

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

    캐시가 없기 때문에 디스크 액세스만 발생한다. 대규모 데이터에 디스크 액세스가 몰리면 시스템이 다운될 수 있다.

    OS 를 시작해 기동하면 자주 사용되는 DB 파일을 한 번 cat 해줘 메모리에 전부 올린 후 로드밸러서에 편입시킨다.

    성능평가나 부하시험도 캐시가 최적화된 후 실시할 필요가 있다.

     

    부하분산 노하우를 배울 수 있으려면 OS 의 동작원리에 대해 숙지하고 있어야한다.

    OS캐시, 멀티스레드나 멀티프로세스, 가상 메모리 구조, 파일시스템에 대해 이해하면 OS 의 장단점과 함께 시스템 전체를 최적화하는데 도움이 된다.

     

     

    2. 분산을 고려한 RDBMS 운용

    국소성(locality) 를 고려해 I/O 를 분산하면 캐시 효율을 높일 수 있다.

    DB접근이 필요한 액세스 패턴에 따라 DB 서버를 나누는 것이다.

    예를 들어 인기글 DB의 캐시 테이블은 액세스가 내 북마크 테이블에 액세스하는 것보다 훨씬 많고 액세스 패턴이 다르다고 할 수 있다.

    이 패턴에 따라 서버1에  인기글 캐시테이블을, 서버2에 북마크 테이블을 두면 서버1에서 북마크 데이터를 캐싱할 필요가 없으니 그만큼 캐시 영역을 다른 곳에 사용할 수 있고, 이는 시스템 전체로서는 메모리에 올라간 데이터량이 많아졌다고 볼 수 있다.

     

    국소성에 따른 분산을 위해 파티셔닝은 자주 사용되는 방법이다.

    파티셔닝을 할 수 있는 기준에는 아래와 같이 예시를 들 수 있다.

    1. 테이블 단위 분할

    2. 테이블 데이터 분할

    3. 용도에 따른 분할 

     

    테이블 분할은 연관성이 높은 테이블끼리 묶어 파티셔닝하는 것이다. 

    MYSQL 의 경우 다른 서버에 있는 테이블끼리 JOIN 을 할 수 없으니 함께 조회가 필요한 테이블끼리 같은 서버에 두고 

    연관성이 낮은 테이블을 다른 서버에 둔다.

    그럼 애플리케이션은 테이블에 따라 다른 서버로 보내 처리할 수 있도록 구현해줘야한다.

     

    테이블 데이터 분할은 말그대로 데이터로 분할의 기준을 정해 분할하는 것이다.

    예를 들어 데이터 ID 의 첫문자를 기준으로 A~C 는 서버1에, D~F는 서버2로 나눠 파티셔닝 할 수 있다. 

     

    용도에 따라 시스템을 나눌 수 있는데, 예를 들어 HTTP 요청의 User-Agent 다 URL 을 보고 서버를 분리하는 것이다.

    사용자로부터의 요청은 서버1, 일부 API 요청으면 서버2, 검색 봇의 접근이면 서버3으로 나누는 것이다.

     

    하지만 파티셔닝, 여러 대의 서버를 운용하는 것은 복잡하고 고장확률이 높아진다는 단점도 있다.


    DB 의 스키마도 OS 캐시와 관련이 있다.

    8바이트짜리 컬럼 1개가 있는 테이블에 데이터가 3억개가 있다면 3GB 를 차지한다.

    스키마를 조금 변경하는 것만으로 기가바이트 단위로 데이터가 증감하는 것이다.

    대량의 데이터를 저장하려는 테이블은 레코드가 가능한 한 작아지도록 설계해야한다. 

    정수int 형은 32 비트=4바이트, 

    문자열은 8비트=1바이트 같은 수치를 기억해두는 것이 좋다.

     

    정규화를 통해 컬럼에 따라 테이블을 분리하는 것이 용량을 많이 줄일수도 있지만, 쿼리가 복잡해지면 애플리케이션 속도가 떨어지는 trade-off 에 대해서도 고려해야한다.

     

    데이터베이스의 인덱스는 데이터를 탐색할 때 디스크 seek 횟수를 줄이기 위해 기본적으로 트리 구조를 자료구조로 갖고 있다. MYSQL 의 경우 B+트리라는 데이터 구조를 사용한다.

    이분트리는 반드시 자식 노드가 2개인데 B 트리의 노드 수를 조정할 수 있다.

    이 노드 수는 디스크 페이지와 밀접한 관련이 있는데, OS는 디스크에서 데이터를 읽을 때 블록단위만큼 읽어내기 때문에, 

    노드 1개를 디스크 1개 블록만큼 할당하면 디스크 seek 횟수를 노드를 찾아갈 때만으로 최소화할 수 있다.

    OS 가 한 번에 읽어내서 메모리에 캐싱하니 같은 노드 내의 데이터는 더 이상의 디스크 seek 이 불필요하다.

     

     

     

     

     

     

     

     

    반응형
Designed by Tistory.