2015년 6월 10일 수요일

The Little Redis Book 요약

Chapter 1 - The Basics

a. Databases

Redis도 database의 개념을 가지고 있다. 처음에는 database 0로 시작한다. 이 database를 다른 것으로 바꾸고 싶으면 select 명령어를 사용한다. select 1을 입력하면 1번 database로 옮겨갈 것이다. 돌아가고 싶으면 select 0를 입력하면 된다.

b. Commands, Keys and Values

Redis를 잘 사용하려면 key와 value의 개념을 파악하고 있는 것이 중요하다. key는 data를 구별해주는 것이고 value는 key와 관련있는 실제 data이다. Redis는 value를 byte array로 다루기 때문에 value가 어떤 값이던지 상관하지 않는다.

아래의 명령어를 입력해보자.

> set users:leto "{name: leto, planet: dune, likes: [spice]}"

user:leto를 key로, 2번째 파라메터를 value로 저장하라는 명령어이다. key에서의 ':'는 Redis의 입장에서는 아무런 의미도 없다. 우리가 봤을 때 leto라는 이름의 user라는 것을 짐작할 수 있게 해주는 key일 뿐이다. value를 가져오는 명령어는 아래와 같다.

> get user:leto

c. Querying

key로 value를 찾을 수 있지만 value를 가지고 무언가를 할 수는 없다. value를 가지고 문언가를 하는 것(예: dune이라는 planet에 살고 있는 유저 찾기)은 Redis의 용도가 아니다.

d. Memory and Persistence

Redis는 in-memory persistent store이다. 기본적으로 Redis는 적당한 수 이상으로 key가 변할 때(예: key가 9번보다 적게 변하면 15분마다 disk에 저장하기) disk에 database를 복제한다. 또는 append mode로 동작하여 key가 변할 때마다 append-only file에 업데이트를 할 수도 있다.

e. Putting It Together

query limitation과 data structure와 memory에 data 저장하기 때문에 Redis는 무척이나 빠르다. 따라서 DB를 쓸 때 db에 접속량을 줄이기 위해 고민하는 것 같은 것을 할 필요가 없다.
Redis는 만능이 아니다. 필요한 용도에 적합하게 사용해야 한다.

Chapter 2 - The Data Structures

a. Strings

위에서 String을 저장하는 set를 살펴봤다. 다른 명령어들도 있다.

> strlen users:leto
> getrange users:leto 27 40
> append users:leto " OVER 9000!!"

strlen은 key의 value의 길이(length)를 가져오고 getrange는 value의 지정한 영역(range)을 가져온다. append는 기존의 value에 새 value를 더한다. 물론 지금 설명한 명령어들은 string의 경우에 의미가 있을 것이다.

> incr stats:page:about
> incr stats:page:about
> incrby ratings:video:12333 5
> incrby ratings:video:12333 3

incr은 value의 값을 1 증가시키고 decr은 1 감소시킨다. incrby는 지정한 수만큼 증가시키고 decrby는 지정한 수만큼 감소시킨다. users:leto에 incr을 적용하면 에러가 발생할 것이다. 각자 setbit과 getbit도 한번 살펴보자.

b. Hashes

key와 value 사이에 field라는 것을 두어 field를 가지고 value를 처리한다.

> hset users:goku powerlevel 9000
> hget users:goku powerlevel

hset은 key로 field와 value를 설정한다. hget으로 key에서의 field의 value를 얻어온다.

>hmset users:goku race saiyan age 737
>hmget users:goku race powerlevel
>hgetall users:goku
>hkeys users:goku
>hdel users:goku age

hmset은 한번에 여러 field를 설정할 수 있도록 해주고 hmget은 한번에 여러 field를 가져올 수 있게 해준다. hgetall은 한번에 모든 field와 이의 value를 가져오고, hkeys는 모든 field를 list로 보여준다. hdel은 지정한 field를 지운다.

이처럼 hash를 통해 좀 더 세밀한 처리를 할 수 있게 된다.

c. Lists

List는 주어진 key로 value의 배열을 처리할 수 있게 해준다.

> lpush newusers goku
>ltrim newusers 0 50

lpush는 value를 list에 더하고 ltrim은 range 사이의 value만을 유지하게 한다. ltrim은 O(N) operation을 가지는 명령어(여기서 N은 제거하는 value의 수이다.)이기 때문에 위의 두 명령어를 항상 같이 실행 함으로서 O(1)로 유지할 수 있게 된다.

d. Sets

중복되지 않는 value를 저장하고 이와 관련한 명령어들을 제공한다.

> sadd friends:leto ghanima paul chani jessica
> sadd friends:duncan paul jessica alia
> sismember friends:leto jessica
> sismember friends:leto vladimir

value가 key의 멤버인지 아닌지 알려주는 sismember는 O(1) operation이므로 매우 효율적이다.

> sinter friends:leto friends:duncan
> sinterstore friends:leto_duncan friends:leto friends:duncan

sinter는 두 key가 같이 가지고 있는 value를 알려준다. sinterstore는 sinter의 결과를 바로 새 key에 저장한다.
중복되는 value가 없는 집합에서 value의 특정한 집합을 찾거나 지정하는데 Set은 유용하다.

e. Sorted Sets

score를 가지는 set이라 할 수 있다.

> zadd friends:duncan 70 ghanima 95 paul 95 chani 75 jessica 1 vladimir

score가 90 이상인 친구를 찾고 싶으면 아래와 같이 한다.

> zcount friends:duncan 90 100

chani의 rank를 알고 싶으면 아래와 같이 한다.

> zrevrank friends:duncan chani


Chapter 3 - Leveraging Data Structures

a. Big O Notation

다수의 Redis 명령어는 O(1)이다. zadd는 O(log(N))이다. ltrim은 O(N)이지만 N이 전체 item의 수가 아니라 제거할 아이템의 수이므로 앞에서 설명했듯이 사용하면 O(1)이 된다. zremrangebyscore는 O(log(N) + M)이다.(N은 set할 element의 수이고 M은 제거할 element의 수이다.) sort는 O(N + M*log(M))이다. 이러한 내용을 인지하고 가능한 한 느린 명령어를 사용하지 않는 방향으로 해야 한다.

b. Pseudo Multi Key Queries

email과 id로 유저의 정보를 찾기 위해 아래처럼 저장하는 것은 좋지 않다.

> set users:leto@dune.gov "{id: 9001, email: 'leto@dune.gov', ... }"
> set users:9001 "{id: 9001, email: 'leto@dune.gov', ... }"

hash를 사용하면 중복을 제거할 수 있다.

> set users:9001 "{id: 9001, email: 'leto@dune.gov', ... }"
> hset user:lookup:email leto@dune.gov 9001

c. References and Indexes

앞에서 한 value가 다른 value를 reference하는 것을 봤다. 만약에 reference하고 있는 것이 바뀌면 어떻하지? Redis는 이에 대한 간편한 해결책이 없다. 수동으로 관리를 해야한다.

> sadd friends:leto ghanima paul chani jessica
> sadd friends_of:chani leto paul

d. Round Trips and Pipelining

Redis는 mget이나 sadd 같은 여러 value를 한번에 전달하는 명령어를 통해 round trip를 줄일 수 있게 해준다.
보통은 클라이언트가 한 명령을 Redis에 보내고 나면 reply를 기다린다. 하지만, Redis는 여러 명령을 먼저 보내고 나중에 response를 받는 pipelining을 지원한다. Ruby같은 경우 pipelined 함수를 통해 pipelining을 할 수 있다.

e. Transactions

여러 명령어를 atomic하게 동작시키고 싶을 때가 있다. 아래와 같이 한다.

> multi
> hincrby groups:1percent balance -9000000000
> hincrby groups:99percent balance 9000000000
> exec

지정한 key를 watch하고 있다가 그 key가 변하면 transaction을 할 수 있다. 그냥 multi와 exec만을 쓰면 모든 명령어가 한번에 실행되 버리기 때문에 이와 같은 상황에 쓸 수 없다.

redis.watch('powerlevel')
current = redis.get('powerlevel')
redis.multi()
redis.set('powerlevel', current + 1)
redis.exec()

f. Keys Anti-Pattern

keys 명령어는 pattern을 인식한다. 하지만, 이 명령어는 매우 느리다.

> keys bug:1233:*

따라서 위처럼 1233에 관련된 value를 찾고 싶은 경우 위처럼 해서 모든 key를 뒤지게 하는 것보다는 아예 처음에 저장할 때 아래처럼 하는 것이 좋은 선택이다.

> hset bugs:1233 1 "{id:1, account:1233, subject: '...'}"
> hset bugs:1233 2 "{id:1, account:1233, subject: '...'}"

Chapter 4 - Beyond The Data Structures

a. Expiration

key에 expiration을 지정할 수 있다. expire할 앞으로의 시간(second)이나 날짜(timestamp since Jan 1, 1970)를 준다.

> expire pages:about 30
> expire pages:about 1356933600

첫번째는 30초 후 key를 지우고 두번째는 2012년 12월 31일 아침 12시에 key를 지운다. ttl은 남아있는 시간을 알려주고 persist는 expiration을 제거한다. setex는 value를 set하면서 바로 expiration time도 지정한다.

> setex pages:about 30 '<h1>about us</h1>'

b. Publication and Subscriptions

> subscribe warnings
> publish warnings "it's over 9000!"

subscribe를 통해 channel에 가입하고 publish를 통해 channel에 메시지를 보낼 수 있다.

c. Monitor and Slow Log

monitor 명령어를 통해 Redis에 전달되는 모든 명령어를 모니터할 수 있다. slowlog 명령어를 통해 지정한 시간 이상이 걸리는 명령어를 log할 수 있다.
모든 명령어를 log하려면 아래와 같이 한다.

> config set slowlog-log-slower-than 0

아래의 모든 log를 보고 최근 10개의 log만 보고 log의 개수를 보는 명령이다.

> slowlog get
> slowlog get 10
> slowlog len

d. Sort

list, set or sorted set을 sort한다.

> rpush users:leto:guesses 5 9 10 2 4 10 19 2
> sort users:leto:guesses

> sadd friends:ghanima leto paul chani jessica alia duncan
> sort friends:ghanima limit 0 3 desc alpha

위의 명령은 descending order로 lexicographically 소트한 후 지정한 위치의 element를 리턴하는 명령이다.

> sadd watch:leto 12339 1382 338 9338
> set severity:12339 3
> set severity:1382 2
> set severity:338 5
> set severity:9338 4
> sort watch:leto by severity:* desc

위의 명령에서 *은 pattern에 의해 watch:leto의 value로 치환 되고 이 친환된 key의 value에 따라 sort가 된다.

Chapter 5 - Lua Scripting

a. Why?

전형적인 stored procedure를 쓰는 것이나 Lua script를 쓰는 것이나 편하지 않은 것은 똑같다. 하지만 Lua script를 잘 쓰면 성능을 향상 시킬 수 있고 코드도 간단해진다.

b. Eval

eval 명령어는 Lua script를 string으로 하는 파라메터를 받는다.

c. Script Management

eval로 실행되는 script를 매번 보내는 것은 좋은 선택이 아니다. Redis는 script를 cache하는 script load 명령을 제공한다.

redis = Redis.new
script_key = redis.script(:load, "THE_SCRIPT") // script의 sha1 digest를 리턴
redis.evalsha(script_key, ['friends:leto'], ['m'])

아래와 같은 명령어도 있다.

script kill - 실행중인 script 중단시키기
script flush - cache되어 있는 모든 script 제거하기
script exists - script가 cache에 이미 있는지 확인하기

d. Libraries

다양한 유용한 라이브러리들이 있다. table.lib, string.lib, math.ib, cjson.lib등이 있다.

e. Atomic

Redis는 single-threaded이므로 Lua script는 중간에 인터럽트 되지 않는다.

f. Administration

lua-time-limit 명령으로 Lua script가 얼마나 오래 실행될 수 있는지를 지정할 수 있다. 기본은 5초이고 보통의 Lua script는 milisecond 단위로 실행되기 때문에 이것은 꽤 긴 시간이다.

Chapter 6 - Administration

a. Configuration

redis.conf 파일을 통해 설정을 지정할 수 있다.

> config get *log*

위의 명령으로 모든 log가 들어가는 명령을 확인 할 수 있다.

b. Authentication

redis.conf 파일에 requirepass 명령으로 password를 지정할 수 있다. 이 값이 지정되면 클라이언트는 auth password 명령으로 인증해야 한다.
심각한 위험을 초래할 수 있는 명령어는 rename 할 수도 있다.

> rename-command CONFIG 5ec4db16...
> rename-command FLUSHALL 10412850...

c. Size Limitations

Redis는 수십억개의 data를 저장할 수 있다.

d. Replication

master에 data를 저장하면 하나 이상의 slave에 master의 내용을 up-to-date 한다. slave로 띄우려면 configuration 파일에 slaveof를 지정하거나 slaveof 명령을 사용한다.
Redis는 automated failover를 지원하지 않는다.

e. Backups

기본적으로 Redis는 dump.rdb라는 파일에 snapshot을 저장한다. snapshot을 뜨거나 append-only file에 저장하는 것을 하지 않고 slave에 저장하는 것을 선택할 수도 있다. 이것은 master에 부하를 줄여주는 장점이 있다.

f. Scaling and Redis Cluster

몇몇의 명령어들은 부하가 심할 수 있으므로 slave를 통한 replication은 부하를 줄여 줄 수 있다. key들을 여러 Redis instance에 분산 배치하는 것도 좋은 방법이다(consistent-hashing algorithm).
Redis Cluster는 현재 작업중에 있다.

댓글 없음:

댓글 쓰기

Building asynchronous views in SwiftUI 정리

Handling loading states within SwiftUI views self loading views View model 사용하기 Combine을 사용한 AnyPublisher Making SwiftUI views refreshable r...