NoSQL Cache 솔루션 – Redis 사용법

다음은 마이크로 소프트웨어에서 Redis 를 소개한 문구를 옮겨본 것이다.
"주목받는 NoSQL & Cache 솔루션인 메모리 기반의 Key/Value Store인 Redis는 Cassandra나 HBase와 같이 NoSQL DBMS로 분류되기도 하고 memcached와 같은 인메모리(In-memory) 솔루션으로 분리되기도 한다.
성능은 memcached에 버금가면서 다양한 데이터 구조체를 지원해 Message Queue, Shared memory, Remote Dictionary 용도로도 사용될 수 있다. 이 덕분에 Redis는 현재 인스타그램, 네이버 재팬의 LINE 메신저 서비스, 스택오버플로우(StackOverflow), 블리자드 등 여러 소셜 서비스에 널리 사용되고 있다."
지금부터 이렇게 이미 널리 사용되고 있는 솔루션인 Redis 를 간단히 사용해보면서 공부해보자.
그림 짤린 것이 있어서 파일로도 첨부한다.
cfile1.uf.23538C39527A509703DB14.doc

설치

설치는 매우 간단하다. Source 를 다운받고 make 만 하면 끝이다.

$ wget http://redis.googlecode.com/files/redis-2.6.4.tar.gz
$ tar xzf redis-2.6.4.tar.gz
$ cd redis-2.6.4
$ make

구동

make 가 성공적으로 수행되었다면 src 디렉토리에 실행파일들이 생겨있는데 이를 통해 기동하면 된다.

Server 구동

다음과 같이 하면 Server 가 구동되는데, 기본 포트인 6379 로 Listen 하는 Server 를 기동한다. Command line 옵션이나 redis.conf 파일을 설정하여 Property 값을 변경할 수 있다.

$ redis-server

Client 접속

$ redis-cli

위와 같이 하면 기본 포트인 6379 로 접속을 수행한다.

기본 개념

Key-value Store 인데, 일반적인 DBMS 개념과는 약간 다르다. Redis 의 Key 는 하나의 Redis Server 안에서 유일하다. 하나의 Key 가 가질 수 있는 Value 는 String 형태의 값 또는 자료구조를 추상화한 Container 를 가질 수 있다.
이러한 Container 는 또 값들로 이루어진 여러개의 String 이나 또다른 Container 를 가질 수 있다.

Key 에서 가질 수 있는 Value 의 형태 , 즉  Data Type 은  다음과 같다.

  • Strings  : 문자열인데 Bytes Array 라고 보는게 맞겠다. 별도의 숫자 형태따위는 없다. Redis 에서 제공하는 유일한 Primitive Type 이다.
  • Lists  : 일반적인 List 자료구조를 추상화한 Container 이며, LPUSH / RPUSH 등의 명령을 통해서 String 들을 왼쪽 오른쪽등에 넣어줄 수 있다.
  • Sets : 중복을 허용하지 않는 자료구조
  • Sorted Sets  : Sets 이나 정렬된 자료구조
  • Hashes : element-value 형태로 이루어진 자료구조를 나타내며, 일반적인 사전 구조를 나타낼 수 있는 구조.
  • Pub/sub : Key 를 통신 채널로 하여 각각의 Client 들이 Publish / Subscribe 할 수 있는 구조

이를 도식화 하면 다음과 같다.

다시 말하면 Key 는 일종의 Generic Form 이며 , 이 Key 를 통해서 실제 사용자가 원하는 자료구조를 접근할 수 있다 정도로 이해하면 될 듯 하다. (pointer -> value 구조 )

Architecture

Model

cfile21.uf.25025E3C527A504833EB07.doc

Redis 는 single threaded Key Value  store 이다. event 방식으로 Request 를 queuing 했다가 (Kernel land) 이를 하나씩 꺼내서 처리하는 방식으로 수행한다. MFC 의 Event Loop 를 와 거의 비슷한 방식이다.

main 에서 Looping 을 돌다가 이벤트를 하나씩 꺼내서 처리하는데, 이 방법을 User Land 에서 사용하는게 아니라 Kernel 단에서 제공하는 방식을 사용해서 했다고 한다. (여기서 이해가 잘 안되는게 이런 구조면 왜 Atomic operation  을 사용했는지 잘 모르겠다. )

Transaction Support

Transaction 접근 제어

Redis 는 단순한 Key Value Store 이기 때문에 실제 DB에서 이야기하는 Transaction 속성으로 1:1 비교하는 것은 많은 어려움이 있으나 다음과 같은 방식으로 Atomic 한 연산을 보장한다.

  • INCR / DECR 등을 사용한 Atomic Operation 지원
  • MULTI  + EXEC 를 통한 Transaction 개념 지원

INCR / DECR 은 여러개의 Client 가 동시에 동일한 키 값을 변경할때 LOCK 이니 이딴거 지원해서 사용하는게 아니라 걍 Atomic Operation 을 통해서 수행한다.

여기서 Redis 만의 재밌는 구현은 MULTI + EXEC 를 통한 Transaciton 구현 방식인데 자세히 설명하면 다음과 같다.

  • MULTI 는 DB 로 보면 Start Transaction 의 개념으로 이후의 명령들을 Client Side 에 Queue 하라는 지시이다.
  • MULTI 다음에 수행되는 Operation 들은 Server 로 전송되지 않고 Client 에 Queuing 된다.
  • EXEC 는 DB 입장에서 보면 COMMIT 과 비슷한 개념으로 이 명령이 들어오면 Client 는 Queue 에 저장된 명령어들을 한 번에 묶어서 수행
  • DISCARD 는 ROLLBACK 과 비슷한 개념인데  Client 에 Queuing 된 정보를 날린다.

물론 위와 같이 MULTI - EXEC 하는 방식은 DB 에서 사용하는 Transaction 개념과는 다르다. Altibase 로 따지면 Autocommit + addBatch + Execute 정도로 보면 되겠다. 즉 Atomic 하게 수행된다는 점이 전혀 보장되지 않는다. 또 중간에 오류가 났을때 (e.g. Syntax Error )  전체를 Rollback 시키는 기능 따위는 없다. 걍 간놈은 간거고 에러난놈은 에러난거다.

여기서 Redis 의 변명은 다음과 같다. 실제 MULTI 를 수행하고 보내는 Command 가 실패될 경우는 두가지 밖에 없다.

 - Command 가 아예 실패한 경우
 - Key 가 가르키는 Type 이 주어진 Operation 과 맞지 않는 경우. (ex. Hash 형에서 LPOP 등의 명령을 썼다던가 ... )
위 두가지 오류는 개발자가 Careful 하게 잘 작성하면 미리 예방할 수 있다. 
뭐 이런 논지. 우리는 성능을 위해서 Rollback 을 포기했다라는 구절도 있다.

Rollback ??

Redis 는 Rollback 이라는 개념이 존재하지 않는다. 일단 Queuing 된 데이터가 보냈다면  해당 데이터는 에러가 났던지 안났던지 일단 보내진것이다. 해당 데이터가 중간에 오류가 났다고 Atomic 하게 Transaction 전체를 Rollback 시키는 행위는 없다. 성능적인 고민이라고 말하는데 이건 심대한 약점이다.

Watch

그렇다고 얘네들이 아예 Transaction Support 에 대해서 배짼것은 아니고,  WATCH 라는 커멘드를 통해서 웹 어플 정도에서 필요한 동시성 정도는 처리할 수 있게 해놨다. WATCH 의 동작 원리는 다음과 같다.

  • WATCH 를 통해 모니터링 해야 할 Key 를 정의 한다. 각 Key 는 Key 별로 Change number 를 관리하며, WATCH 를 수행 한 순간 Client 단에 해당 Key 의 Change number 를 저장한다 .
  • MULTI 를 통해서 변경되는 연산에 대한 Queuing 을 수행한다.
  • EXEC 를 할 때가 WATCH 로 지정한 Key 의 change number 를 다시 가지고 온다. 만약 변했으면 실패 해버린다. (절대 서버로 보내고 오는 일 따위 없다)
  • 이러한 오류를 복구 하기 위해서 WATCH 부터 다시 Operation 을 수행하면 된다. 대표적인 Optimistic Locking 방식이다.

WATCH 를 통해서는 Check & set 이라는 Atomic operation 으로 구현되어있기 때문에 동시에 두 Client 가 동시에 세팅 할 수 없다.

Backup & Recovery

Redis 에서 Persistency 를 제공하는 방식은 크게 두가지이다.

  • RDB
  • AOF

뭐 그렇다고 이 두가지를 같이 사용할 수 없는 것은 아니고, 동시에 구성 가능.

RDB

  • 특정 주기 마다 전체 메모리 이미지를 파일로 Dump 한다.
  • SAVE 명령을 통해서도 Manual 하게 수행시킬 수 있다.
  • 임시 파일에 dump 를 수행하고, 해당 Backup 이 끝나면 기존의 파일로 Replace 시키는 방식
  • 실제 주기를 어떻게 설정하느냐에 따라 다르지만 실제 Redis 가 구동될때 File I/O 없이 진행할 수 있는 방식

AOF

  • 일반적으로 DB 가 따르는 Logging 방식으로 이해할 수 있다.
  • 이건 뭐 약점 강점 정리할 것도 없음.

Redis 는 위와 같은 구조를 가지고 존재하는 CS 환경이며, Client 와 Server 간의 통신은 TCP/IP 또는 IPC 기반의 통신위에서 움직인다. 이러한 점은 성능적인 면에서는 큰 손해로 볼 수 있다. 이는 Mem cached 등의 BMT 결과를 통해서 보면 확연하게 드러나는데, 실제 비슷한 Key-value store engine 인 memcached 과 비교하여 1/3 정도의 성능 저하가 있다.

HA Support

Redis 는 크게 Replica 라 불리는 이중화 기술과 Cluster 라 불리는 분산 저장 방식을 통해서 HA 를 구현한다.

  • Replica : 일반적으로 사용하는 이중화 기능으로 Master - slave 방식을 지원한다. Active - Active 따위 없다.
  • Cluster : 아직 stable 한 상황까지는 아니고 어느정도 Beta 까지 완성된것으로 보이는데, Key-Store 와 Value 와 분리 되어있는 강점으로 인하여 쉽게 Shading 이 가능하다.

Replica  방식에서 노드들은 크게 두가지 형태로 나뉜다.

  •  Master : Read write 를 모두 할 수 있는 노드
  •  Slave : Read operation 만 허용되는 노드

이를 보면 우리가 일반적으로 알고 있는 Active - StandBy 형태의 구현은 아니다. 즉 Failover 가 일어났다고 해도 해당  Failover 를 통해서 실제 모든 업무가  Perfect 하게 동작하지는 않는다. 이러한 약점을 극복하기 위해서 Redis 는 redis-sentinel 이라 불리는 별도의 Process 를 제공하는데, 이 유틸리티의 역할은 Master Redis 의 Failure 를 감지하여 Slave 를 Master 로 변신 시키는 역할을 한다. 이러면 뭐 비슷해질라나.

예제

실제 Key 는 Pointer 개념으로 해당 키를 어떤 자료구조로 세팅하여 사용하느냐에 따라 해당 key 가 string 인지 , Lists 등의 자료 구조 형태가 결정된다.

Strings
//  myKey 라는 Key 에 "Hello" 라는 Value 를 넣는다.
redis 127.0.0.1:6379 > SET myKey "Hello" 


// myKey 를 출력
redis 127.0.0.1:6379 > GET myKey 

// myKey 에 " World" 라는 String 을 더한다.
redis 127.0.0.1:6379 > APPEND myKey " World"

// Redis 는 String 으로 모두 다 처리한다. 만약 사용자가 넣은 값이 숫자형이라면 
// INCR 이나 DECR 와 같은 숫자형 명령이 모두 수행된다.
redis 127.0.0.1:6379 > SET integerType "1" 

// 1을 증가
redis 127.0.0.1:6379 > INCR integerType 

// 2를 리턴
redis 127.0.0.1:6379 > GET integerType
Lists
// 1개짜리 List 생성
redis 127.0.0.1:6379 > RPUSH mylist "This is string"

// 여기서 두개짜리 리스트가 생긴다.
redis 127.0.0.1:6379 > RPUSH mylist "This is string, too"
redis 127.0.0.1:6379 > LRANGE mylist 0 -1

You may also like...

0 0 votes
Article Rating
Subscribe
Notify of
guest
4 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
유익이

좋은정보 보고갑니다~
아쉽게도 첫번째 기본개념 설명하는 부분에서 이미지가 짤려있네요 ㅜ

구름과비21

아.. 그래요 ?
이상하네요. 제 PC 에서는 보이는데…
그리고, 혹시라도 그림이 스마트폰에서 안보이는 경우가 있어서 document 파일로도 첨부를 해두었습니다. ^^

유익이

좋은정보 보고갑니다~
아쉽게도 첫번째 기본개념 설명하는 부분에서 이미지가 짤려있네요 ㅜ

구름과ጆ

아.. 그래요 ?
이상하네요. 제 PC 에서는 보이는데…
그리고, 혹시라도 그림이 스마트폰에서 안보이는 경우가 있어서 document 파일로도 첨부를 해두었습니다. ^^

4
0
Would love your thoughts, please comment.x
()
x