Arcus란?

아커스 (Arcus)는 memcached와 ZooKeeper를 기반으로 네이버 서비스들의 요구 사항을 반영해 개발한 메모리 캐시 클라우드 입니다.

아커스는 memcached 프로토콜을 지원하고 다음의 memcached 기본 성능 혜택은 그대로 유지합니다.

  • 아커스는 백엔드 저장소인 데이터베이스의 앞단에 위치하여 hot-spot 성격의 데이터를 캐싱하여, 서비스 응용에게 빠른 응답성 제공하고 데이터베이스 부하 감소.

  • 복잡한 계산에 의한 결과물 또는 웹 처리상의 중간 데이터 등을 신속히 저장, 조회.

  • 캐시를 통하여 여러 프로세스들 간에 데이터 공유.

아커스는 memcached를 확장해서 다음의 추가 기능을 제공합니다.

  • ZooKeeper 기반의 cache cloud 관리

  • Collection 자료구조 (List, Set, B+tree) 지원

  • B+tree의 다양한 기능들Item attibute 조회 및 설정 기능

    • 다양한 크기의 B+tree key (bkey)
    • Element flag 및 filtering
    • Bkey 기반의 range scan
    • 여러 B+tree들에 대한 sort-merge-get (smget)
    • B+tree position 기반의 range scan
  • Sticky item(not evicted & oot expired) 지원

  • Collection 자료구조를 위한 small memory allocator

Arcus Cache Cloud

arcus 네이버 사에 의해 개발 된 memcached를 기반 cache cloud입니다. ARCUS-memcached는 네이버 서비스의 기능 및 성능 요구 사항을 지원하도록 수정되었습니다. arcus는 key-value 쌍의 기본적인 memcached 데이터 구조를 지원하는 것 이외에도 데이터를 저장/구줄할 수있는 여러 값을 위한 B+tree, list, set과 같은 컬렉션 데이터 구조를 지원합니다.

Arcus는 memcached 노드들의 다중 클러스터들을 Zookeeper를 사용해 관리합니다. 각 클러스터 또는 클라우드는 서비스 코드로 식별이 됩니다. 서비스 코드를 클라우드의 이름으로 생각하면 됩니다. 사용자는 바로 memcached 노드나 클라우드를 추가하거나 삭제할 수 있습니다. 그리고 Arcus는 장애 node를 감지하고 자동으로 제거합니다.

arcus의 전체적인 구조는 다음과 같습니다. memcached의 노드는 IP 주소:port로 식별이 됩니다. ZooKeeper는 memcached 노드 이름의 database와 이것들이 속해 있는 서비스 코드(cloud)를 유지합니다. ZooKeeper는 또한 각 cloud (cache list)에 살아있는 node의 리스트를 유지합니다. 처음 시작될 때, 각 memcached 노드는 ZooKeeper와 접촉하고 속해있는 서비스 코드를 찾습니다. 그 다음, 해당 노드는 arcus 클라이언트가 볼 수 있도록 캐시 list에 추가합니다. ZooKeeper는 주기적으로 캐시 node가 살아있는지 확인을 하고 cache cloud에서 장애가 난 node를 삭제한 뒤, cache 클라이언트에게 업데이트가 된cache list를 알립니다. 가장 최신의 cache list를 가지고 arcus 클라이언트는 각 key-value 명령을 위한 cache node를 찾기 위해 consistent hashing을 실행합니다. Hubble(모니터링서버)은 수집하고 cache cloud의 통계를 보여줍니다.

Arcus 개발배경


Large-scale 웹서비스

  • 데이터 양과 요청량의 증가

성능이슈

  • Throughput
  • Response time

DB 확장성 이슈

Arcus(memory cache cloud) 사용구조


성능해결

  • Low latency
  • high throughput

DB부하 경감

Arcus Architecture


Arcus 특징

memcached 대비 arcus 개발 사항

  • ZooKeeper 기반의 cloud 형성 및 관리
    • elastic - node 추가 및 삭제, 자동 장애조치
  • prefix 기능 확장 - delete, stats
    • key string의 prefix : key  -> prefix:subkey
  • collection : list, set, B+Tree
  • small memory allocator
  • sticky item
  • eager item expiration
  • 동적 configuration(memlimit, maxconns, ...) 변경
  • item 속성 조회 및 변경

Arcus cloud 관리

service code

service code (cloud name)

  • arcus cloud(또는 cluster)를 구분하는 유일한 식별자
  • arcus 클라이언트는 service code로 특정 cloud에 접근


server의 ZK node 등록


client의 ZK node 조회


Arcus 데이터 분산

key-value 아이템을 cloud의 어떤 cache node로 분산할 것인지

key-to-node mapping


consistent hashing

  1. arcus cache node들의 hash값을 모두 구함
  2. key의 hash 값을 구함
  3. key hash값을 기준으로 시계 방향으로 가장 처음 만나는 node hash값이 가리키는 arcus cache node를 선택


cache node 분산

cache node 분산 이슈 (1 hash point / cache node)


cache node 분산 이슈 -> 약 160개 hash points / 1 cache node


Arcus key-value모델

key

key-value item 식별자

Format: prefix:subkey (max 250 characters)

  • prefix 단위로 key들을 그룹화하여 관리 (ex. delete, stats)
  • subkey 단위로 prefix내의 특정 item 식별

value

key-value item에서 value

single value (max size: 1MB)

value collection : list, set, B+Tree

  • max 50000 elements
  • 각 element마다 max 4KB value

Arcus collections

유형구조 및 특징
ListDouble linked list 구조
Set

unique data의 정렬되지 않은 모음

membership 확인 (친구정보, 구독정보 등)

B+Tree

B+Tree기반의 정렬된 데이터의 모음

element 구조: bkey, [eflag], value

  • bkey(b+tree key): 8바이트의 integer 또는 1~31 바이트 어레이
  • eflag(element flag): 1~31 바이트 어레이

주요연산

  • bkey 기반 range scan & eflag filter & offset, count
  • 여러 b+tree에 대한 smget(sort-merge get)
  • b+tree position 조회 / position 기반 element 조회

arcus 대표 성능 이슈 - SNS

친구 글 또는 구독 글 모아보기

IN 리스트가 커질수록, 질의 응답이 급격히 느림

SELECT * 
FROM posts
WHERE user IN (friend-1, friend-2, ...friend-N)
	AND create_time < sysdate()
ORDER BY create_time DESC
LIMIT 20;

SNS 사용자 증가 및 활성화 -> DB튜닝만으로는 어려움

  • 관계(relationship)증가
  • 데이터 규모와 조회 요청량 증가

Arcus B+Tree - bkey:create_time, value:postid


Push 방식Pull 방식
Cache사용자 별 inbox cache 유지사용자 별 outbox cache 유지
Post 작성

모든 친구의 inbox cache에 post 정보를 push delivery

친구 수만큼 write 발생

작성자의 outbox cache에만 post 정보를 보관

1회 write 발생

Post 모아보기

자신의 inbox cache만 조회

B+tree get 연산 사용

모든 친구의 outbox cache를 조회하여 sort-merge 수행

B+Tree smget 연산 사용


arcus 대표 성능 이슈 - Game Ranking

대표 조회 요청

  • 사용자의 score에 대한 순위를 조회하고 그 score 보다 앞뒤에 있는 N개 score 및 user 조회

B+Tree - top game scores 저장

  • bkey:score, value: userinfo

B+Tree position 관련 연산들

  • Find position: <key, bkey, order>   -> position
  • Get by position: <key, position_range, order>  -> elements
  • Find position with get: <key, bkey, order, count>  ->  <position, elements>
    • 주어진 bkey의 position을 조회하고 그 position의 element를 포함하여 양방향 (forward&backward)로 count 갯수의 element 조회 (release 예정 기능)


ARCUS - Small Memory Allocator


ARCUS - Eager Item Expiration

기존 item expiration

  • search 범위
    • 각 LRU 리스트의 tail에 위치한 N개 items만 검사
  • 이슈
    • LRU 상위에 expired items가 존재하더라도 reclaim 되지 않고, LRU tail에 있는 valid item이 먼저 evict됨
    • valid items의 memory 사용량 확인이 어려움

Eager Item Expiration

  • search 범위
    • 각 LRU 리스트의 Tail에 위치한 N개 item들의 검사 외에도 LRU 리스트를 점진적 traverse하면서 N개 items 검사
  • 효과 - memory 효율성 증가

지원 OS

현재 arcus는 64-bit 리눅스만 지원하는 상태입니다. (아래 두 OS에서만 테스트가 진행되었습니다.)

  • CentOS 6.x 64bit
  • Ubuntu 12.04 LTS 64bit

참고 사이트

http://www.slideshare.net/deview/3aruc

http://www.slideshare.net/deview/ots2014-arcuscollectionopen-source

https://github.com/naver/arcus

https://naver.github.io/arcus/

'캐시시스템' 카테고리의 다른 글

Arcus  (0) 2016.07.25
Memcached, Redis  (0) 2016.06.25

Memcached

Memcached란?

무료로 사용할 수 있는 오픈소스이며 분산 메모리 캐싱 시스템

데이터 베이스의 부하를 줄여 동적 웹 어플리케이션의 속도개선을 위해 사용되기도 함

DB나 API호출 또는 렌더링 등으로부터 받아오는 결과 데이터를 작은 단위의 key - value 형태로 메모리에 저장하는 방식

Memcached는 필요량보다 많은 메모리를 가졌을 때, 시스템으로부터 메모리를 사용하고 필요로하는 메모리가 부족한 경우 이를 더 쉽게 가져다 사용할 수 있도록 만들어 줌

Memcached 사용여부에 따른 메모리 운영방식



Memcached를 사용하지 않을 땐 분리되어 있는 메모리에 대해 각각의 서버에서 사용할 수 있는 것은 할당된 메모리 크기만큼인데 Memcached를 적용할 경우에는 논리적으로 결합되어 있기 때문에 각 웹서버는 전체 메모리 캐시만큼의 용량을 사용할 수 있다. 즉 효율성있게 메모리 운영이 가능해진다는 것이다.

여기서 Memcached를 사용하지 않았을 때와 Memcached를 사용했을 때의 시나리오를 가정할 수 있다.

Memcached를 사용하지 않을 경우

각 노드는 완벽하게 독립적임

이 경우 고전적으로 사용되던 방식으로 총 캐시 크기가 웹팜(여러 대를 사용해서 웹사이트를 구축한 형태)의 실제 용량의 일부분으로만 사용이 가능하다는 점에서 낭비가 심하다. 각각의 서버에 할당된 캐시 크기만큼 사용할 수 있으므로 웹팜의 캐시 사이즈는 128MB이지만 각 서버에서 사용할 수 있는 사이즈는 65MB이다.

Memcached를 사용할 경우

Memcached를 사용할 경우 Memcached로 묶인 모든 서버는 동일한 가상 메모리 풀을 공유한다. 이것은 특정한 항목이 주어졌을 때, 전체 웹 클러스터에서 항상 동일한 위치에 저장되고 검색되어짐을 뜻한다. 또한 응용프로그램에 대한 수요가 증가하여 서버증설에 대해 필요성을 느낄 때, 정기적으로 접근되어져야 하는 데이터의 관점에서도 수요가 증가한다고 볼 수 있다.

Memcached를 적용하면 분산 메모리 캐시를 적용하게 되는 것이므로 캐싱을 통해 DB나 API 호출에 대한 횟수를 줄일 수 있고 이로 인해 응용프로그램의 수요나 DB 데이터 접근에 대한 부하를 줄여 성능을 향상할 수 있다.

Redis

Redis란?

redis는 오픈소스로서 데이터베이스(NOSQL DBMS)로 분류가 되기도 하고 Memcached와 같이 인메모리 솔루션으로 분류되기도 한다.

성능은 memcached에 버금가면서 다양한 데이터 구조체를 지원함으로써 Message Queue, Shared memory, Remote Dictionary 용도로도 사용될 수 있으며, 이런 이유로 인스탄트그램, 네이버 재팬의 LINE 메신져 서비스, StackOverflow,Blizzard,digg 등 여러 소셜 서비스에 널리 사용되고 있다.

NoSQL관점에서 봤을 때 redis는 가장 단순한 키-밸류 타입을 사용하고 있다. 데이터 모델을 복잡할수록 성능이 떨어지므로 redis는 단순한 구조를 통해 높은 성능을 보장한다고 할 수 있다.

NoSQL에는 다양한 제품이 있지만 이 중, redis가 주목받는 이유는 다음과 같다.

  • 데이터 저장소로 가장 입/출력이 빠른 메모리를 채택
  • 단순한 구조의 데이터 모델인 키- 밸류 방식을 통해 빠른 속도를 보장
  • 캐시 및 데이터스토어에 유리
  • 다양한 API지원

redis, memcached, 구아바 라이브러리등 인메모리 캐시방식을  적용한 제품 중 redis는 global cache 방식을 채택하였다. global cache 방식은 네트워크 트래픽이 발생하기 때문에 java heap영역에서 조회되는 local cache의 성능이 더 낫지만, WAS 인스턴스가 증가할 경우엔 캐시에 저장되는 데이터 크기가 커질수록 redis 방식이 더 유리하다.

redis는 급격한 사용자가 집중되는 상황이나 대규모의 확장이 예정되어 있는 환경에 적합하다. global cache 방식이 적용되어 was 인스턴스 확장에는 유리하지만 cache 및 redis 세션 등 관리 포인트가 늘어난다는 단점이 존재한다.

Redis 특징

Key - Value 저장방식

redis는 특정 키 값을 저장하는 방식으로 구성되어 있고 PUT/GET 명령어를 지원

데이터는 메모리에 존재해서 write와 read의 속도가 매우 빠름. 그래서 전체 저장이 가능한 데이터 용량은 물리적은 메모리 크기를 넘어설 수 있다.  (물론 OS의 disk swapping 영역등을 사용하여 확장은 가능하겠지만 성능이 급격하게 떨어지기 때문에 의미가 없다.) 데이타 허용은 메모리에서 일어나지만 server restart 와 같이 서버가 꺼지는 상황에 데이타를 저장을 보장하기 위해서 Disk를 persistence store로 사용한다.

다양한 데이터 타입

단순하게 key-value 타입기반이라면 이미 memcached가 존재한다. redis는 Key-Value Store이기는 하지만 저장되는 Value가 단순한 Object가 아니라 자료구조를 갖기 때문에 큰 차이를 보인다. redis가 지원하는 데이타 형은 크게 아래와 같이 5가지가 있다.

1)string

일반적인 문자열로 최대 512mbyte 길이 까지 지원한다. Text 문자열 뿐만 아니라 Integer와 같은 숫자나 JPEG같은 Binary File까지 저장할 수 있다.

2)set

 

set은 string의 집합이다. 여러개의 값을 하나의 Value 내에 넣을 수 있다고 생각하면 되며 블로그 포스트의 태깅(Tag)등에 사용될 수 있다. set간의 연산을 지원하는데, 집합인 만큼 교집합, 합집합, 차이(Differences)를 매우 빠른 시간내에 추출할 수 있다.


set
members
members
members


3)sorted set

set 에 "score" 라는 필드가 추가된 데이타 형으로 score는 일종의 "가중치" 정도로 생각하면 된다. sorted set에서 데이타는 오름 차순으로 내부 정렬되며, 정렬이 되어 있는 만큼 score 값 범위에 따른 쿼리(range query), top rank에 따른 query 등이 가능하다.
sorted set
scoremember
scoremember
scoremember

4)hashes

 

hash는 value내에 field/string value 쌍으로 이루어진 테이블을 저장하는 데이타 구조체이다. RDBMS에서 PK 1개와 string 필드 하나로 이루어진 테이블이라고 이해하면 된다.

5)list

list는 string들의 집합으로 저장되는 데이타 형태는 set과 유사하지만, 일종의 양방향 Linked List라고 생각하면 된다. List 앞과 뒤에서 PUSH/POP 연산을 이용해서 데이타를 넣거나 뺄 수 있고, 지정된 INDEX 값을 이용하여 지정된 위치에 데이타를 넣거나 뺄 수 있다.

정리

  • Value가 일반적인 string 뿐만 아니라, set,list,hash와 같은 집합형 데이타 구조를 지원한다.
  • 저장된 데이타에 대한 연산이나 추가 작업 가능하다. (합집합,교집합,RANGE QUERY 등)
  • set은 일종의 집합, sorted set은 오름차순으로 정렬된 집합, hash는 키 기반의 테이블, list는 일종의 링크드 리스트 와 같은 특성을 지니고 있다.
  • 이러한 집합형 데이타 구조 (set,list,hash)등은 redis에서 하나의 키당 총 2^32개의 데이타를 이론적으로 저장할 수 있으나, 최적의 성능을 낼 수 있는 것은 일반적으로 1,000~5,000개 사이로 알려져 있다.

Persistence

redis는 데이타를 disk에 저장할 수 있다. memcached의 경우 메모리에만 데이타를 저장하기 때문에 서버가 shutdown 된후에 데이타가 유실 되지만, redis는 서버가 shutdown된 후 restart되더라도, disk에 저장해놓은 데이타를 다시 읽어서 메모리에 Loading하기 때문에 데이타 유실되지 않는다. redis에서는 데이타를 저장하는 방법이 snapshotting 방식과 AOF (Append on file) 두가지가 있다.

1)snapshotting (RDB)

순간적으로 메모리에 있는 내용을 DISK에 전체를 옮겨 담는 방식이다. SAVE와 BGSAVE 두가지 방식이 있는데, SAVE는 blocking 방식으로 순간적으로 redis의 모든 동작을 정지시키고, 그때의 snapshot을 disk에 저장한다. BGSAVE는 non-blocking 방식으로 별도의 process를 띄운후, 명령어 수행 당시의 메모리 snaopshot을 disk에 저장하며, 저장 순간에 redis는 동작을 멈추지 않고 정상적으로 동작한다.

 

  • 장점 : 메모리의 snapshot을 그대로 뜬 것이기 때문에, 서버 restart시 snapshot만 load하면 되므로 restart 시간이 빠르다.
  • 단점 : snapshot을 추출하는데 시간이 오래 걸리며, snapshot 추출된후 서버가 down되면 snapshot 추출 이후 데이타는 유실된다.
    (백업 시점의 데이터만 유지)

2)AOF 

AOF(Append On File) 방식은 redis의 모든 write/update 연산 자체를 모두 log 파일에 기록하는 형태이다. 서버가 재 시작될때 기록된  write/update operation을 순차적으로 재 실행하여 데이타를 복구한다. operation 이 발생할때 마다 매번 기록하기 때문에, RDB 방식과는 달리 특정 시점이 아니라 항상 현재 시점까지의 로그를 기록할 수 있으며, 기본적으로 non-blocking call이다.

 

  • 장점 : Log file에 대해서 append만 하기 때문에, log write 속도가 빠르며, 어느 시점에 server가 down되더라도 데이타 유실이 발생하지 않는다.
  • 단점 : 모든 write/update operation에 대해서 log를 남기기 때문에 로그 데이타 양이 RDB 방식에 비해서 과대하게 크며, 복구시 저장된 write/update operation을 다시 replay 하기 때문에 restart속도가 느리다.

절충안

RDB와 AOF 방식의 단점을 서로 보안하기 위해 두 방식을 섞어서 사용하는 것이 좋다. 주기적으로 snapshot으로 백업하고, 다음 snapshot까지의 저장을 AOF 방식으로 수행한다. 이렇게 하면 서버가 restart될 때 백업된 snapshot을 reload하고, 소량의 AOF 로그만 replay하면 되기 때문에, restart 시간을 절약하고 데이타의 유실을 방지할 수 있다.

Publish / Subcribe 모델

redis는 1:1 형태의 Queue 뿐만 아니라 1:N 형태의 Publish/Subscribe 메세징도 지원한다.(Publish/Subscribe 구조에서 사용되는 Queue를 일반적으로 Topic이라고 한다.) 하나의 Client가 메세지를 Publish하면, 이 Topic에 연결되어 있는 다수의 클라이언트가 메세지를 받을 수 있는 구조이다. 

일반적인 Pub/Sub 시스템의 경우 Subscribe하는 하나의 Topic에서만 Subscribe하는데 반해서, redis에서는 pattern matching을 통해서 다수의 Topic에서 message 를 subscribe할 수 있다.
예를 들어 topic 이름이 music.pop, music.classic 이라는 두개의 Topic이 있을때, "PSUBSCRIBE music.*"라고 하면 두개의 Topic에서 동시에 message를 subscribe할 수 있다.

Replication Topology

redis는 NoSQL 계열의 Key/Store Storage인데 반해서 횡적 확장성을 지원하지 않는다. 쉽게 말해서 2.4.15 현재 버전 기준으로는 클러스터링 기능이 없다. (향후 지원 예정) 그래서 확장성(scalability)과 성능에 제약사항이 있는데, 다행이도 Master/Slave 구조의 Replication(복제)를 지원하기 때문에 성능 부분에 있어서는 어느정도 커버가 가능하다.

Master/Slave replication

Master/Slave Replication이란, redis의 master node에 write된 내용을 복제를 통해서 slave node에 복제 하는 것을 정의한다. 1개의 master node는 n개의 slave node를 가질 수 있으며, 각 slave node도 그에 대한 slave node를 또 가질 수 있다.



이 master/slave 간의 복제는 Non-blocking 상태로 이루어진다. 즉 master node에서 write나 query 연산을 하고 있을 때도 background로 slave node에 데이타를 복사하고 있다는 이야기고, 이는 master/slave node간의 데이타 불일치성을 유발할 수 있다는 이야기이기도 하다. master node에 write한 데이타가 slave node에 복제중이라면 slave node에서 데이타를 조회할 경우 이전의 데이타가 조회될 수 있다.

Query Off Loading을 통한 성능 향상

이 master/slave replication을 통해서 성능을 향상시킬 수 있고 동시접속자수나 처리 속도를 늘릴 수 있다. (데이타 저장 용량은 늘릴 수 없다.) 이를 위해서 Query Off Loading이라는 기법을 사용하는데 Query Off Loading은 master node는 write only, slave node는 read only 로 사용하는 방법이다. redis에서만 사용하는 기법이 아니라, Oracle, MySQL과 같은 RDBMS에서도 많이 사용하는 아키텍쳐 패턴이다.

대부분의 DB 트렌젝션은 웹시스템의 경우 write가 10~20%, read가 70~90% 선이기 때문에, read 트렌젝션을 분산 시킨다면, 처리 시간과 속도를 비약적으로 증가 시킬 수 있다. 특히 redis의 경우 value에 대한 여러가지 연산(합집합,교집합,Range Query)등을 수행하기 때문에, 단순 PUT/GET만 하는 NoSQL이나 memcached에 비해서 read에 사용되는 resource의 양이 상대적으로 높기 때문에 redis의 성능을 높이기 위해서 효과적인 방법이다.

Sharding 을 통한 용량 확장

redis가 클러스터링을 통한 확장성을 제공하지 않아 데이터의 용량이 늘어났을 경우 Sharding이라는 아키텍쳐를 이용하여 해결한다. Sharding은 Query Off loading과 마친가지로, redis 뿐만 아니라 일반적인 RDBMS나 다른 NoSQL에서도 많이 사용하는 아키텍쳐로 내용 자체는 간단하다. 여러개의 redis 서버를 구성한 후에, 데이타를 일정 구역별로 나눠서 저장하는 것이다. 예를 들어 숫자를 key로 하는 데이타가 있을때 아래와 그림과 같이 redis 서버별로 저장하는 key 대역폭을 정해놓은 후에, 나눠서 저장한다. 데이타 분산에 대한 통제권은 client가 가지며 client에서 애플리케이션 로직으로 처리한다.


Expriation

redis는 데이타에 대해서 생명주기를 정해서 일정 시간이 지나면 자동으로 삭제되게 할 수 있다. redis가 expire된 데이타를 삭제 하는 정책은 내부적으로 Active와 Passive 두 가지 방법을 사용한다.
  1. Active 방식은 Client가 expired된 데이타에 접근하려고 했을 때, 그때 체크해서 지우는 방법
  2. Passive 방식은 주기적으로 key들을 random으로 100개만 (전부가 아니라) 스캔해서 지우는 방식
expired time이 지난 후 클라이언트에 의해서 접근 되지 않은 데이타는 Active 방식으로 인해서 지워지지 않고 Passive 방식으로 지워져야 하는데, 이 경우 Passive 방식의 경우 전체 데이타를 scan하는 것이 아니기 때문에, redis에는 항상 expired 되었으나 지워지지 않는 garbage 데이타가 존재할 수 있는 원인이 된다.


'캐시시스템' 카테고리의 다른 글

Arcus  (0) 2016.07.25
Memcached, Redis  (0) 2016.06.25

+ Recent posts