본문 바로가기

데이터베이스(DA, AA, TA)/NoSQL

[memcached] Arcus 구조 이해

Arcus는 Memcached 를 확장한 구조이며, Memcached에 대한 공식사이트 설명은 아래와 같다.

 

무료 및 오픈 소스, 고성능 분산 메모리 객체 캐싱 시스템이다. 데이터베이스 부하를 완화하여 동적 웹 응용 프로그램의 속도를 높이는데 사용하는것이 일반적이다. Memcached는 데이터베이스 호출, API 호출 또는 페이지 렌더링 결과에서 임의의 작은 데이터 청크(문자열, 객체)를 위한 메모리 내 키-값 저장소라고 볼수 있다.

 

Arcus는 서비스코드로 Arcus cloud를 구분한다. 클라이언트는 서비스코드에 해당하는 cloud에만 접속할 수 있다. 서비스코드가 즉 Arcus Cache 클라우드의 이름이 되는 것이다. 한 개의 arcus 클라이언트는 하나의 arcus 클러스터에만 접속할 수 있게 되는 구조이다.


Arcus의 물리적 구성

 

Arcus 서버

Cache 데이터 저장소

Arcus 서버 프로세스가 start/stop될때 Zookeeper 통보.

문제가 발생한 Arcus 프로세스는 스스로 중지 (fail-stop)

 

Arcus ZooKeeper ensemble (Admin)

서비스코드 별로 Arcus 서버 목록을 관리하고 변경이 있을때 클라이언트에게 통지한다. (서버 노드 추가 또는 제거)

Arcus 서버와의 세션이 만료되면 Arcus 서버 목록에서 해당 서버를 제거한다. (목록에서 가지고 있던 문제 노드를 삭제)

 

Arcus 클라이언트

Zookeeper로부터 서비스 코드에 할당된 사용가능한 Arcus 서버 목록을 받아온다 (zookeeper 노드를 watch하고 있다가 추가/제거 여부를 묻게된다)

Arcus 서버 목록의 변경을 지속적으로 감시한다. (zk watcher)

Arcus 서버 목록 변경시, 최신 서버 목록 기반으로 요청을 rehashing한다.

Arcus 서버와 통신

 

https://github.com/naver/nbase-arc/blob/master/doc/images/overview.png

 

Arcus 서버가 Sevice Cluster Node에 참여하는 방법

(1) Arcus 서버의 IP:PORT를 사용하여 자신이 서비스되어야 하는 코드를 조회 - IP 인덱싱

(2) 서비스 코드 노드 아래 자신의 IP:PORT를 ephemeral node로 생성

        ephemeral node: zookeeper 와의 세션이 유효한 동안에만 존재하는 node.

        Cloud에 참여하는 서버 정보 관리를 위해 ZooKeeper의 znode 구조를 이용.

 

Arcus 클라이언트가 Service Cluster Node에 연결하는 방법

(1) 서비스 코드에 등록된 Arcus 서버 목록을 조회한다.

(2) Arcus 서버로 접속을 시도한다.

(3) 자신의 서비스 코드의 변경사항을 감시한다. 자식 znode에 변경사항이 생기면 변경사항을 통지받아 서버 목록을 갱신다.

        서비스 코드명을 가진 노드를 주기적으로 watch한다.

 

Arcus 서버가 Service Node에서 탈퇴하는 방법

(1) ZooKeeper 서버에 ephemeral znode를 생성하여 connection timeout, disconnect, 세션 만료가 되면 자동으로 znode가 삭제되도록 한다.

(2) 네트워크/서버/OS 등은 잘 동작하고 있지만 Arcus가 응답하지 못하는 상황을 감지하기 위해 ZooKeeper HeartBeat를 할때 자기 자신에게 dummy 요청을 하여 응답이 오는지 확인한다.

(3) client는 znode의 변경사항을 감지하여 변경사항이 있을경우 변경된 내용으로 서버 목록을 갱신한다.

 

문제가 생겨도 문제를 감지하기까지 zookeeper 세션타임아웃까지 길어질 수 있다. 세션 타임아웃을 너무 짧게 지정하면, 네트워크 순단에 의해 저절로 내려갈수도 있으며, 길게 지정하면 장애 detect 시간이 늦어지게 되므로 적정 파라미터값을 결정해야 한다.

 


Arcus의 요청 분산

Arcus는 Cache key를 해상한 결과에 따라 데이터를 분산하여 저장한다. Arcus 클라이언트는 특정 서버로 저장/조회하는 명령을 보내게 된다. 조회했는데 없다는건 캐시 미스이다. 클라이언트 내부에서 해싱하고, 해싱 결과에 따라 어느 서버로 명령을 보낼지 결정하는 방식이다.

 

1/N 분산이 서버 추가/회수 시 전체 영역에 파급되기 때문에 서버의 위치를 정하여 Hash Ring에 포인트를 찍는 방식을 사용한다.

(1) 서버를 Hashing하여 그 값을 포인트로 Hash ring에 배치한다.

(2) key의 hash 값을 구한다.

(3) hash값을 기준으로 시계방향으로 가장 처음만나는 포인트의 서버를 선택한다.

 

Arcus가 채택하는 포인트 방식의 문제점은 포인트 위치가 균등하지 않기 때문에 key들이 각 노드에 골고루 분산되지 않는 문제가 생긴다. 서버 하나를 투입하면 기존 서버들의 range를 균등하게 1/N의 부담하지 않는다. Arcus node hash 결과를 정렬된 형태로 늘어놓아보면 hash 값의 간격 차이가 균일하지 않아 key들이 각 Arcus 서버에 골고루 분산되지 않음을 확인할 수 있다. 이에 대응하여 Arcus는 데이터 분산을 위해 Ketama Consistent Hashing 방법을 채택하였다. 이 방안은 서버 목록에 변경이 있을 경우 데이터 재분산(클라이언트 요청 재분산?)을 최소화 할수 있다. 최초에 서버마다 4개의 해시값을 부여 받고, 이를 160개까지 부풀려서 무작위 배치하게 된다.

 

한 서버로부터 160개의 가상 포인트를 생성하여 Hash ring에 분포시킨다. 한 서버의 투입시 기존 노드의 Hash Range가 변경되는 일이 없고, 노드의 숫자가 많기 때문에 한 노드에 요청이 몰리는 일 또한 발생하지 않는다. 한 노드의 투입시 기존 서버들의 range를 거의 1/N씩 담당하게 되고, 회수시 기존 서버들에 자신의 range를 1/N씩만 부담시키실 수 있다.

 

Arcus의 rebalancing 문제

구조적으로 key-server 매핑이므로 rebalancing을 바로하기 어렵다. 서버 회수/투입은 cache miss를 동반한다. 설계 사상이 저장소보다 cache 본연의 역할을 목표로 하고 있다. (저장소로서 필요조건 - persistnece and rebalancing and replication)


Arcus 자료구조

Key/value

memcached의 기본 자료구조

key/value를 string 데이터 형식으로 처리하는 것이 설계사항

  - 실질적으로는 바이트 배열로 구현, 추가적인 type 속성을 참고하여 클라이언트 드라이버가 타입을 지정할 수 있다.

  - incr, decr의 value type은 문자열 (정수형이 아니다.)

  - 1 MB의 저장한계가 있다.

제한적인 부분 변경을 지원하지만, 기본적으로 value 전체를 get/set하는 구조.

  - 목록을 관리하고 싶으면 응용단에서 별도의 인코딩이 필요.

  - 부분 변경에 통신 비효율이 발생한다.

  - 부분 변경에 동시성 문제가 발생할수 있다. (CAS:Check And Set로 회피 가능)


https://github.com/naver/nbase-arc

https://github.com/naver/hubblemon/blob/master/Readme.kr.md

 

HUBBLE MON(arcus monitoring) architecture -  https://github.com/naver/hubblemon/blob/master/doc/img/rm_arch.png