[Redis] 자료구조 명령어 종류 & 활용사례 정리 📃

Ungs·2023년 12월 19일
0

Redis

목록 보기
3/4

Redis 데이터 타입 (Collection)

String, Set, Sorted Set, Hash, List 등 다양한 타입 지원.

Redis Collections 를 사용할 때 주의점하나의 컬렉션에 너무 많은 아이템을 담으면 좋지 않다.가능하면 10000개 이하의, 몇천개 수준의 데이터 셋을 유지하는게 Redis 성능에 영향주지 않는다.

Redis - Strings

  • 일반적인 문자열
  • 값은 최대 512MB, String으로 될 수 있는 binary data도, JPEG 이미지도 저장 가능
  • 단순 증감 연산에 좋음
  • string-string 매핑을 이용하여 연결되는 자료 매핑을 할 수 있따. HTML매핑도 가능

Strings 명령어 리스트

  • SET: SET, SETNX, SETEX, SETPEX, MSET, MSETNX, APPEND, SETRANGE
  • GET: GET, MGET, GETRANGE, STRLEN
  • INCR: INCR, DECR, INCRBY, DECRBY, INCRBYFLOAT
  • Enterprise: SETS, DELS, APPENDS (subquery)
# 한개 조회
set <key> <value>
get <key> <value>

# 여러개 조회
mset <key> <value> <key> <value> ...
mget <key> <key> <key> ...
127.0.0.1:6379> set hello "world!"
OK

127.0.0.1:6379> get hello
"world!"

127.0.0.1:6379> get count
"-351"

127.0.0.1:6379> set count 50
OK

127.0.0.1:6379> incr count
(integer) 51

127.0.0.1:6379> get count
"51"

127.0.0.1:6379> incrby count 100
(integer) 151

127.0.0.1:6379> decr count
(integer) 150

127.0.0.1:6379> decrby count 500
(integer) -350

127.0.0.1:6379> mset a "hello" b "world"
OK

127.0.0.1:6379> mget a b
1) "hello"
2) "world"

Redis - Bitmaps

  • bitmaps은 string의 변형
  • bit 단위 연산 가능하다.
  • String이 512MB 저장 할 수 있듯이 2^32 bit까지 사용 가능하다.
    저장할 때, 저장 공간 절약에 큰 장점이 있다.

Bits 명령어 리스트

Redis - Lists

  • array 형식의 데이터 구조. 데이터를 순서대로 저장 
  • 추가 / 삭제 / 조회하는 것은 O(1)의 속도를 가지지만, 중간의 특정 index 값을 조회할 때는 O(N)의 속도를 가지는 단점이 있다.
  • 즉, 중간에 추가/삭제가 느리다. 따라서 head-tail에서 추가/삭제 한다. (push / pop 연산)
  • 메세지 queue로 사용하기 적절하다.

Lists 명령어 리스트

  • SET (PUSH): LPUSH, RPUSH, LPUSHX, RPUSHX, LSET, LINSERT, RPOPLPUSH

  • GET: LRANGE, LINDEX, LLEN

  • POP: LPOP, RPOP, BLPOP, BRPOP

  • REM: LREM, LTRIM

  • BLOCK: BLPOP, BRPOP, BRPOPLPUSH

  • Enterprise: LREVRANGE, LPUSHS, RPUSHS (subquery)

    List 명령어 페이지

# 왼쪽에 삽입
lpush <key> <value>

# 오른쪽에 삽입
rpush <key> <value>

# 삭제
lpop <key>
rpop <key>
# LPUSH를 통한 list 생성
127.0.0.1:6379> LPUSH myList "a"
(integer) 1
127.0.0.1:6379> LRANGE myList 0 -1
1) "a"

# LPUSH , RPUSH를 통한 요소 삽입 결과.
127.0.0.1:6379> LPUSH myList "b"
(integer) 2
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"

# LPUSHX , RPUSHX 사용 예
# key가 없는곳에 추가할려고 하는 경우 0을 반환.
127.0.0.1:6379> LPUSHX myList2 "a"
(integer) 0
127.0.0.1:6379> LPUSH myList "d"
(integer) 4


# 기존에 있던 myList요소들은 [ d , b , a , c ] 순으로 되어있음.
# LPOP , RPOP을 통해 맨 좌 우측 요소 한개씩 제거 
127.0.0.1:6379> LPOP myList
"d"
127.0.0.1:6379> RPOP myList
"c"
127.0.0.1:6379> LRANGE myList 0 -1
1) "b"
2) "a"

# 현재 List의 요소 길이를 출력.
127.0.0.1:6379> LLEN myList
(integer) 2

# LREM을 통핸 해당 요소 삭제 , count를 0 으로 해서 요소중에 "a"랑 매칭되는 값을 삭제
127.0.0.1:6379> LREM myList 0 "a"
(integer) 1
127.0.0.1:6379> LRANGE myList 0 -1
1) "b"

# 해당 key에 해당되는 index값을 입력받은 값을 수정.
# 현재 b로 남아있던 요소값을 z로 변경
127.0.0.1:6379> LSET myList 0 "z"
OK
127.0.0.1:6379> LRANGE myList 0 -1
1) "z"

# RPOPLPUSH 
127.0.0.1:6379> RPOPLPUSH myList hello
"z"
127.0.0.1:6379> LRANGE hello 0 -1
1) "z"

Redis - Hashes

  • field-value로 구성 되어있는 전형적인 hash의 형태 (파이썬의 딕셔너리나 js객체 정도로 이해하면 된다)
  • key 하위에 subkey를 이용해 추가적인 Hash Table을 제공하는 자료구조
  • 메모리가 허용하는 한, 제한없이 field들을 넣을 수가 있다.

Hashes 명령어 리스트

  • SET: HSET, HMSET, HSETNX

  • GET: HGET, HMGET, HLEN, HKEYS, HVALS, HGETALL, HSTRLEN, HSCAN, HEXISTS

  • REM: HDEL

  • INCR: HINCRBY, HINCRBYFLOAT

    Hashes 명령어 페이지

# 한개 값 삽입 및 삭제
hset <key> <subkey> <value>
hget <key> <subkey>

# 여러 값 삽입 및 삭제
hmset <key> <subkey> <value> <subkey> <value> ...
hnget <key> <subkey> <subkey> <subkey> ... 

# 모든 subkey와 value 가져오기, Collection에 너무 많은 key가 있으면 장애의 원인이 됨
hgetall <key>

# 모든 value값만 가져오기
hvlas <key>
# field - value : name - jinmin / year - 1995 / month - 3
127.0.0.1:6379> hset hh name jinmin year 1995 month 3
(integer) 3

127.0.0.1:6379> hget hh name
"jinmin"

127.0.0.1:6379> hget hh year
"1995"

127.0.0.1:6379> hdel hh year
(integer) 1

127.0.0.1:6379> hlen hh
(integer) 2

127.0.0.1:6379> hgetAll hh
1) "name"
2) "jinmin"
3) "month"
4) "3"

127.0.0.1:6379> hkeys hh
1) "name"
2) "month"

127.0.0.1:6379> hvals hh
1) "jinmin"
2) "3"

Redis - Sets

  • 중복된 데이터를 담지 않기 위해 사용하는 자료구조 (js의 set이라고 생각하면 된다)
  • 유니크한 key값
  • 정렬되지 않은 집합
  • 중복된 데이터를 여러번 저장하면 최종 한번만 저장된다.
  • Set간의 연산을 지원. 교집합, 합집합, 차이를 매우 빠른 시간내에 추출할 수 있다.
  • 단, 모든 데이터를 전부 다 갖고올 수 있는 명령이 있으므로 주의해서 사용해야 한

Sets 명령어 리스트

  • SET: SADD, SMOVE

  • GET: SMEMBERS, SCARD, SRANDMEMBER, SISMEMBER, SSCAN

  • POP: SPOP

  • REM: SREM

  • 집합연산: SUNION, SINTER, SDIFF, SUNIONSTORE, SINTERSTORE, SDIFFSTORE

  • Enterprise: SLS, SRM, SLEN, SADDS (subquery)

    Sets 명령어 페이지

    sadd <key> <item>

존재 여부를 체크, 있으면 1 없으면 0 반환

sismember

삭제

srem

key의 모든 item 조회

smembers


```sql
127.0.0.1:6379> sadd myset a # 추가된 member 갯수 반환
(integer) 1

127.0.0.1:6379> sadd myset a
(integer) 0

127.0.0.1:6379> sadd myset b
(integer) 1

127.0.0.1:6379> sadd myset c
(integer) 1

127.0.0.1:6379> srem myset c # 삭제된 member 갯수 반환
(integer) 1

127.0.0.1:6379> smembers myset
1) "b"
2) "a"

127.0.0.1:6379> scard myset
(integer) 2

127.0.0.1:6379> sadd myset c d e f # 여러 member 삽입 가능
(integer) 4

127.0.0.1:6379> smembers myset
1) "d"
2) "c"
3) "a"
4) "f"
5) "b"
6) "e"

127.0.0.1:6379> spop myset 3 # 랜덤 member 삭제
1) "d"
2) "c"
3) "f"

127.0.0.1:6379> smembers myset
1) "a"
2) "b"
3) "e"

Redis - Sorted Sets

  • set에 score라는 필드가 추가된 데이터 형 (score는 일종의 가중치)

  • 일반적으로 set은 정렬이 되어있지않고 insert 한 순서대로 들어간다.그러나 Sorted Set은 Set의 특성을 그대로 가지며 추가적으로 저장된 member들의 순서도 관리한다. 

  • 데이터가 저장될때부터 score 순으로 정렬되며 저장

  • sorted set에서 데이터는 오름차순으로 내부 정렬

  • value는 중복 불가능, score는 중복 가능

  • 만약 score 값이 같으면 사전 순으로 정렬되어 저장 

    Sorted Sets 명령어 리스트

  • SET: ZADD

  • GET: ZRANGE, ZRANGEBYSCORE, ZRANGEBYLEX, ZREVRANGE, ZREVRANGEBYSCORE, ZREVRANGEBYLEX, ZRANK, ZREVRANK, ZSCORE, ZCARD, ZCOUNT, ZLEXCOUNT, ZSCAN

  • POP: ZPOPMIN, ZPOPMAX

  • REM: ZREM, ZREMRANGEBYRANK, ZREMRANGEBYSCORE, ZREMRANGEBYLEX
    INCR: ZINCRBY

  • 집합연산: ZUNIONSTORE, ZINTERSTORE

  • Enterprise: ZISMEMBER, ZLS, ZRM, SLEN, SADDS (subquery)

    Sorted Sets 명령어 페이지

 # ZADD : key에 score-member를 추가
127.0.0.1:6379> zadd fruit 2 apple
(integer) 1

127.0.0.1:6379> zadd fruit 10 banana
(integer) 1

# 복수개의 score-member를 추가할 수 있음
127.0.0.1:6379> zadd fruit 8 melon 4 orange 6 watermelon
(integer) 3

# 이미 추가 된 member를 add 시 score가 업데이트
127.0.0.1:6379> zadd fruit 15 apple
(integer) 0

# ZSCORE : member에 해당하는 score 값 리턴
127.0.0.1:6379> zscore fruit apple
"15"

# ZRANK : member에 해당하는 rank(순위) 값 리턴
127.0.0.1:6379> zrank fruit melon
(integer) 2

# ZRANGE : key에 해당하는 start - stop 내가 출력하고 싶은 요소를 추출
127.0.0.1:6379> zrange fruit 0 -1
1) "orange"
2) "watermelon"
3) "melon"
4) "banana"
5) "apple"

Redis - HyperLogLogs

  • 굉장히 많은양의 데이터를 dump할때 사용
  • 중복되지 않는 대용량 데이터를 count할때 주로 많이 사용한다. (오차 범위 0.81%)
  • set과 비슷하지만 저장되는 용량은 매우 작다 (저장 되는 모든 값이 12kb 고정)
  • 하지만 저장된 데이터는 다시 확인할 수 없다. (데이터 보호에 적절)

엄청 크고 유니크 한 값 카운팅 할 때 사용→ 웹 사이트 방문 ip 개수 카운팅, 하루 종일 크롤링 한 url 개수 몇개 인지, 검색 엔진에서 검색 한 단어 몇개 인지

HyperLogLogs 명령어 페이지

> PFADD crawled:20171124 "http://www.google.com/"
(integer) 1

> PFADD crawled:20171124 "http://www.redis.com/"
(integer) 1

> PFADD crawled:20171124 "http://www.redis.io/"
(integer) 1

> PFADD crawled:20171125 "http://www.redisearch.io/"
(integer) 1

> PFADD crawled:20171125 "http://www.redis.io/"
(integer) 1

> PFCOUNT crawled:20171124
(integer) 3

> PFMERGE crawled:20171124-25 crawled:20171124 crawled:20171125
OK

> PFCOUNT crawled:20171124-25
(integer) 4

Redis - Streams

  • Streams는 log를 저장하기 가장 좋은 자료 구조
  • append-only 이며 중간에 데이터가 바뀌지 않는다.
  • 읽어 올때 id값 기반으로 시간 범위로 검색
  • tail -f 사용 하는 것 처럼 신규 추가 데이터 수신

소비자별 다른 데이터 수신(소비자 그룹) 등 활용 가능하다.

Streams 명령어 페이지

> 127.0.0.1:6379> XADD customer * id "asdfdasf"
"1647231099585-0"

> 127.0.0.1:6379> XADD customer * id "asdfdasf"
"1647231112587-0"

> 127.0.0.1:6379> xlen customer
(integer) 2

> 127.0.0.1:6379> xdel customer 1647231396280-0
(integer) 1

Redis 자료구조 활용 사례

사례 1 - 좋아요 처리

하나의 게시글에는 좋아요 처리가 한 사용자에게 한번만 허용된다.

RDBMS에서 유니크 조건을 걸어 구현 가능하지만 이러하면 insert와 update가 자주 발생하여 RDBMS 성능 저하가 발생.

Redis의 set 자료구조를 이용하면 간단하게 구현 가능.

게시글의 번호를 key로하고 회원 ID를 아이템으로 추가하면 동일한 ID값을 저장할 수 없으므로 한 명의 사용자는 하나의 댓글에 한번 만 좋아요를 누를 수 있게 된다.

사례 2 - 일일 순 방문자 수

간단하게 중복 방문을 제거한 방문자의 지표
유저는 천만 명이라 가정하고, 일일 방문자 횟수를 집계하며 이 값은 0시를 기준으로 초기화 된다고 하자.

실제 서비스에서는  이를 구현하기 위해서는 아래와 같은 방법이 있다.

  • Google Analytics와 같은 외부 서비스 이용
  • access log 분석
  • 접속 정보를 로그파일로 작성하여 배치 프로그램 작성

그러나 밑에 두가지 방법은 실시간으로 데이터를 조회할 수 없다는 단점이 있다.
레디스의 비트 연산을 활용하면 간단하게 실시간 순 방문자를 저장하고 조회하는 방법을 구현할 수 있다.

사용자 ID는 0부터 순차적으로 증가된다고 가정하고, string의 각 bit를 하나의 사용자로 생각해 보자
그리고 사용자가 서비스에 방문할 때 사용자 ID에 해당하는 bit를 1로 설정한다.
1개의 bit가 1명을 의미하므로, 천만 명의 유저는 천만 개의 bit로 표현할 수 있고, 이는 곧 1.2MB정도의 크기이다.
레디스 string의 최대 길이는 512MB이므로 천만 명의 사용자를 나타내는 건 충분하다.

2023년 12월 19일에 ID가 6번인 사람이 방문했다고 해보자
그러면 자료의 여섯 번째 인덱스를 1로 설정한다.
그러면 이 날에 서비스에 방문한 총방문자 수를 조회하기 위해서는 이 문자열에서 1로 설정된 bit의 개수를 구하는 BITCOUNT 연산을 사용하면 간단하게 구할 수 있다.

사례 3 - 출석 이벤트

정해진 기간동안 매일 방문한 사용자를 구하는 서비스를 구현한다면 어떻게 할까?
사례 2번을 이용하여, 구해놓은 string 간의 비트를 비교하는 BITOP 커맨드를 사용하면 이 서비스를 구현할 수 있다.

날짜별로 bit 자료구조를 만든다. 6번인 사람이 방문했다면 10명의 회원이 있다고 한다면 11번째 비트값을 항상 1로 설정하여 AND 연산을 통해 1이나오면 매일 방문한 것을 알 수 있다.

사례 4 - 최근 검색 목록 표시

유튜브 검색창에 최근 검색한 목록이 드롭다운 메뉴로 보여지게 만들고 싶다. 만일 ID필드가 123 사용자가 최근 검색한 사용자 목록을 보고 싶다면, RDBMS로는 다음과 같이 쿼리를 날려야 한다.

select * from KEYWORD 
where ID = 123 
order by reg_date desc 
limit 5;

하지만 이렇게 RDBMS의 테이블을 이용해서 데이터를 저장한다면 중복 제거도 해야 하고, 멤버별로 저장된 데이터의 개수를 확인하고, 오래된 검색어는 삭제하는 작업까지 이루어져야 한다.

따라서 애초에 중복이 되지 않고, 정렬되어 있는 레디스의 sorted set 을 사용하면 간단하게 구현할 수 있다.
sorted set은 가중치를 기준으로 오름차순으로 정렬되기 때문에, 가중치로 시간을 사용한다면 이 값이 가장 큰, 나중에 입력된 아이템이 맨 마지막 인덱스에 저장된다.

단, 항상 다섯 명만 저장한다고 설계한다면 여섯명을 저장시키게 된다면 가장 마지막 값을 지워야 한다.

0번째 인덱스를 지우면 되기는 하는데, 아이템 개수가 6보다 작을 때에는 0번째 인덱스를 삭제하면 안 되기 때문에 매번 아이템의 수를 먼저 확인해야 하는 번거로움이 있게 된다.

이때 sorted set의 음수 인덱스를 사용하면 된다.
음수 인덱스는 인덱스의 마지막부터 큰 값부터 작은 값으로 매겨지는데, 데이터에 멤버를 추가한 뒤, 항상 -6번째 아이템을 지운다면 특정 개수 이상의 데이터가 저장되는 것을 방지 할 수 있게 된다.
인덱스로 아이템을 지우려면 ZREMRANGEBYRANK 커맨드를 사용하면 된다.

> ZREMRANGEBYRANK recent:member:123 -6 -6
profile
Hi I'm Ungs

0개의 댓글