Redis Data Types

Redis 자료구조

Redis의 자료구조는 여러 가지가 있다.

기본 자료구조와 그 특성, 그리고 간단한 예제를 알아보자.

간단한 명령어도 같이 다루고, stream, pub/sub, hyperlog등에 대해서도 알아보자.

Strings

String 타입은 Redis의 가장 기본적인 type이다.

일반적으로 우리가 알고 있는 Key-value 형식을 따르고 있다.

Key와 value 모두 binary safe 하기 때문에 어떠한 데이터의 종류도 key, value의 값이 될 수 있다.

127.0.0.1:6379> set somekey somevalue
OK
127.0.0.1:6379> get somekey
"somevalue"
  • 값의 최대 길이는 512MB이다. (거의 신경쓸 필요 없을 듯 하다)

  • INCR, DECR, INCRBY 명령어를 통해 Atomic counters(Thread safe counter)를 구현할 수 있다.

  • APPEND 명령어를 통해 APPEND가 가능하다.

  • GETRANGE, SETRANGE 명령어를 통해 값에 랜덤 액세스가 가능하다.

    • 값의 어느 위치든 인덱스를 통해 접근할 수 있다.

예제

Append

127.0.0.1:6379> append somekey value
(integer) 14
127.0.0.1:6379> get somekey
"somevaluevalue"

INCR, DECR, INCRBY

127.0.0.1:6379> set counter 1
OK
127.0.0.1:6379> get counter
"1"
127.0.0.1:6379> incr counter
(integer) 2
127.0.0.1:6379> get counter
"2"

127.0.0.1:6379> decr counter
(integer) 1
127.0.0.1:6379> get counter
"1"

127.0.0.1:6379> incrby counter 3
(integer) 4

Lists

Lists는 Linked List와 유사한 형태로 데이터가 저장되는 Redis에서 제공하는 자료구조다.

따라서 처음과 마지막 부분에 element를 추가 / 삭제 / 조회하는 것은 O(1)의 속도를 가지지만 중간의 특정 index 값을 조회할 때는 O(N)의 속도를 가지는 단점을 가지고 있다.

중간에 있는 index값을 가져와야 할 경우가 많다면 Sorted Set을 사용하자

LPUSH, LTRIM, LPOP, RPOP, RTRIM, RPUSH, LRANGE 등 많은 명령어가 있다.

예제

127.0.0.1:6379> rpush mylist A
(integer) 1
127.0.0.1:6379> lpush mylist B
(integer) 2
127.0.0.1:6379> lrange mylist 0 -1
1) "B"
2) "A"

LRANGE의 0과 -1은 파이썬 List의 인덱스와 비교하면 기억하기 편하다.

127.0.0.1:6379> rpush mylist C
(integer) 3
127.0.0.1:6379> lrange mylist 0 -1
1) "B"
2) "A"
3) "C"
127.0.0.1:6379> rpop mylist 1
1) "C"
127.0.0.1:6379> lrange mylist 0 -1
1) "B"
2) "A"

LTRIM과 같은 명령어로 Lists의 크기를 고정할 수 있다.

127.0.0.1:6379> lpush mylist D
(integer) 4
127.0.0.1:6379> lrange mylist 0 -1
1) "D"
2) "C"
3) "B"
4) "A"
127.0.0.1:6379> ltrim mylist 0 2
OK
127.0.0.1:6379> lrange mylist 0 -1
1) "D"
2) "C"
3) "B"

Sets

Redis sets는 순서가 보장되지 않는 Strings의 집합이다.

기본적으로 추가, 삭제, element의 존재 유무확인 등에 대해서 O(1)의 속도를 보장한다.

또한 Set 이기 때문에 동일한 value는 추가한다고 해서 2개가 공존하지 않는다.

중복을 허용하지 않고, 빠른 시간 복잡도를 지니고 있어서 유용하게 활용할 수 있다.

또한 Sets는 Sets간의 합집합, 차집합 등을 지원해준다.

Set의 명령어는 sadd, smembers 등이 있다.

127.0.0.1:6379> sadd myset 1 2 3
(integer) 3
127.0.0.1:6379> smembers myset
1) "1"
2) "2"
3) "3"
127.0.0.1:6379> spop myset 1
1) "1"
127.0.0.1:6379> srandmember myset 1
1) "3"
127.0.0.1:6379> smembers myset
1) "2"
2) "3"

Hashes

Redis의 Hashes는 value로 또 다른 key-value Map을 가지는 자료구조이다.

127.0.0.1:6379> hmset primrose name primrose birth 1996 superuser true
OK
127.0.0.1:6379> hget primrose name
"primrose"
127.0.0.1:6379> hget primrose birth
"1996"
127.0.0.1:6379> hgetall primrose
1) "name"
2) "primrose"
3) "birth"
4) "1996"
5) "superuser"
6) "true"

Sorted sets

Redis의 Sorted sets 자료구조는 Sets 자료구조에 Score를 추가로 기록하여 Score가 낮은순서부터 높은 순서대로 정렬되는 자료구조다.

동일한 값(Key)은 오지 못하며 Score(Value)는 동일할 수 있다.

127.0.0.1:6379> zadd hackers 1940 "Alan Kay"
(integer) 1
127.0.0.1:6379> zadd hackers 1957 "Sophie"
(integer) 1
127.0.0.1:6379> zadd hackers 1953 "Richard"
(integer) 1
127.0.0.1:6379> zadd hackers 1949 "Anita"
(integer) 1
127.0.0.1:6379> zadd hackers 1965 "Yuki"
(integer) 1
127.0.0.1:6379> zadd hackers 1914 "Hedy"
(integer) 1
127.0.0.1:6379> zadd hackers 1916 "Calude"
(integer) 1
127.0.0.1:6379> zadd hackers 1969 "Linus"
(integer) 1
127.0.0.1:6379> zadd hackers 1912 "Turing"
(integer) 1
127.0.0.1:6379> zrange hackers 0 -1
1) "Turing"
2) "Hedy"
3) "Calude"
4) "Alan Kay"
5) "Anita"
6) "Richard"
7) "Sophie"
8) "Yuki"
9) "Linus"
127.0.0.1:6379> zrange hackers 0 3
1) "Turing"
2) "Hedy"
3) "Calude"
4) "Alan Kay"

다음과 같이 특정 Member의 순위를 가져올 수도 있다.

127.0.0.1:6379> zrank hackers Turing
(integer) 0
127.0.0.1:6379> zrank hackers Yuki
(integer) 7

Stream

Redis 5.0에 새롭게 도입된 데이터 유형이다.

이 글은 사실상 Stream을 위해 쓰는 글이므로 조금 상세하게 다룰 예정이다.

127.0.0.1:6379> xadd primrose * name "primrose"
"1676829361208-0"

일단 XADD 명령어로 스트림을 하나 추가했다.

*을 이용하면 자동으로 ID를 부여해주고, 이 ID는 milliseconds - sequence의 형태로 나온다.

현재 시간의 timestamp 숫자와 같은 시간내에 여러개의 데이터가 올 수 있기 때문에 numbering이 되어있다.

127.0.0.1:6379> xadd primrose * name "primrose"
"1676829361208-0"
127.0.0.1:6379> xadd primrose * name "primrose"
"1676829496497-0"
127.0.0.1:6379> xlen primrose
(integer) 2

이제 XREAD를 이용해서 읽어보자.

127.0.0.1:6379> xread block 5000 streams primrose $

$는 다양한 뜻이 있다. jsonpath 구문을 사용한다는 뜻으로도 쓰이고 $123 은 123bytes를 사용했다는 뜻으로 쓰이기도 하고 여기서는 새로 생긴 streams data만 받겠다는 뜻이다.

127.0.0.1:6379> xread block 5000 streams primrose $
1) 1) "primrose"
   2) 1) 1) "1676829720128-0"
         2) 1) "id"
            2) "100"
(1.49s)
127.0.0.1:6379> xread block 5000 streams primrose $
1) 1) "primrose"
   2) 1) 1) "1676829731446-0"
         2) 1) "id"
            2) "200"

위와 같이 다른 터미널에서 XADD 를 통해서 데이터가 추가되면 XREAD하던 터미널에서 정보를 받아올 수가 있다.

다음은 XGROUP이다. 레디스 키와 함께 그룹을 생성할 수 있다.

127.0.0.1:6379> xgroup create newstream mygroup $ MKSTREAM
OK

다시 스트림에 데이터를 추가해보자.

127.0.0.1:6379> xadd mystream * message apple
"1676830015352-0"
127.0.0.1:6379> xadd mystream * message banana
"1676830020324-0"
127.0.0.1:6379> xadd mystream * message orange
"1676830024374-0"
127.0.0.1:6379> xadd mystream * message strawberry
"1676830027779-0"
127.0.0.1:6379> xadd mystream * message apricot
"1676830032520-0"

다음과 같이 XRANGE 명령어를 이용해서 스트림에 담겨있는 전체 내역을 출력할 수 있다.

-, + 부분에는 Entry ID를 넣을 수 있는데, -+로 하면 가장 작은 아이디부터 가장 큰 아이디까지 보여준다.

127.0.0.1:6379> xrange newstream - +
(empty array)

이제 그냥 READ가 아니라 XGROUPREAD를 해보자.

127.0.0.1:6379> xreadgroup group mygroup Alice count 1 streams newstream >
1) 1) "newstream"
   2) 1) 1) "1676830550872-0"
         2) 1) "message"
            2) "apple"
  • <group-name> <consumer-name> 순서대로 명시해서 사용한다.

  • 여기 예제에서 group-name은 mygroup, consumer-name은 Alice로 사용했다.

  • 끝에 > 는 다른 소비자에게 전달된 적이 없는 데이터를 꺼내올 때 사용하는 특수 ID

만약 스트림을 바라보고 대기하려면 아래와 같이 BLOCK <millisecondsTime> 을 옵션을 사용하면 된다.

127.0.0.1:6379> XREADGROUP GROUP mygroup Alice BLOCK 2000 COUNT 1 STREAMS newstream >
1) 1) "newstream"
   2) 1) 1) "1676830563238-0"
         2) 1) "message"
            2) "strawberry"

추출된 데이터는 다음과 같이 확인할 수 있다.

127.0.0.1:6379> xreadgroup group mygroup Alice streams newstream 0
1) 1) "newstream"
   2) 1) 1) "1676830550872-0"
         2) 1) "message"
            2) "apple"
      2) 1) "1676830558321-0"
         2) 1) "message"
            2) "orange"
      3) 1) "1676830563238-0"
         2) 1) "message"
            2) "strawberry"

Last updated