본문 바로가기

SearchDeveloper/개발자를 위한 레디스

4장 레디스 자료 구조 활용 사례

실시간 리더보드 - sorted set

실시간 리더보드에 sorted set이 좋은 이유

  • 실시간으로 정렬돼야하는데 sorted set은 저장할때부터 정렬되므로 조회가 빠름

 

ZADD, ZREVRANGE, ZINCRBY, ZUNIONSTORE

127.0.0.1:6379> ZADD score:250116 100 player:1 
(integer) 1
127.0.0.1:6379> ZADD score:250116 500 player:2
(integer) 1
127.0.0.1:6379> ZADD score:250116 700 player:3
(integer) 1

127.0.0.1:6379> ZREVRANGE score:250116 0 2 withscores
1) "player:3"
2) "700"
3) "player:2"
4) "500"
5) "player:1"
6) "100"

127.0.0.1:6379> ZADD score:250116 300 player:3
(integer) 0
127.0.0.1:6379> ZREVRANGE score:250116 0 2 withscores
1) "player:2"
2) "500"
3) "player:3"
4) "300" // 300으로 덮어쓰기
5) "player:1"
6) "100"

127.0.0.1:6379> ZINCRBY score:250116 400 player:3
"700"
127.0.0.1:6379> ZREVRANGE score:250116 0 2 withscores
1) "player:3"
2) "700" // 400 더해서 700됨
3) "player:2"
4) "500"
5) "player:1"
6) "100"

127.0.0.1:6379> ZADD score:250117 100 player:3
(integer) 1
127.0.0.1:6379> ZUNIONSTORE score:2501 2 score:250116 score:250117 
(integer) 3
127.0.0.1:6379> ZREVRANGE score:2501 0 2 withscores
1) "player:3"
2) "800"
3) "player:2"
4) "500"
5) "player:1"
6) "100"
  • 리더보드 조회: ZREVRANGE score:250116 0 2 withscores (Reverse Range) : 점수 내림차순으로 [0]~[2] 출력
  • 점수 업데이트: ZADD 로 같은 필드면 값만 업데이트한다. 필드는 중복저장되지 않으니까.
  • 점수 증가: ZINCRBY
  • 점수 데일리 합산: ZUNIONSTORE score:2501 2 score:250116 score:250117 2개 키를 합해서 score:2501 sorted set을 새로 생성한다.
    • ZUNIONSTORE score:2501 2 score:250116 score:250117 weights 1 2 : score:250116 엔 값 곱하기 1, score:250117엔 값 곱하기 2해서 신규 생성한다.



최근 검색 기록 - sorted set

<요구사항>

  • 유저별로 다른 키워드 노출
  • 중복 제거
  • 최근 검색어 5개 노출

→ 💡sorted set으로 중복제거 만족, 시간을 스코어로 저장

 

  • 리더보드와 같이 ZADD, ZREVRANGE 활용
  • 5개 개수 판단 로직을 안 넣고 싶을 때, 레디스에 5개까지만 저장하려면?
    • ZREMRANGEBYRANK keyword -6 -6 : (remove range by rank) 최신 검색어는 [-1]이니까 6번째 검색어인 [-6] ~[6] 제거

 

태그 기능 - sorted set

포스트 단위로 tag 저장

post

ㄴ [post번호]

   ㄴtags (키 구조)
127.0.0.1:6379> SADD post:1:tags t1 t2 t3
(integer) 3
127.0.0.1:6379> SADD post:2:tags t1 t2
(integer) 2

 

tag 단위로 포스트 저장

tag

ㄴ [tag명]

       ㄴposts (키 구조)
127.0.0.1:6379> SADD tag:t1:posts 1 2
(integer) 2
127.0.0.1:6379> SADD tag:t2:posts 1 2
(integer) 2
127.0.0.1:6379> SADD tag:t:posts 3
(integer) 1

127.0.0.1:6379> SINTER tag:t1:posts tag:t2:posts
1) "1"
2) "2"
  • 태그명으로 포스트 필터링: SINTER tag:t1:posts tag:t2:posts



랜덤 데이터 추출 - RANDOMKEY, HRANDFIELD, SRANDMEMBER, ZRANDMEMBER

  • 게임에서 유저 랜덤 매핑
  • 유저, 아이템 랜덤 추출

 

127.0.0.1:6379> RANDOMKEY
"p:1"
127.0.0.1:6379> RANDOMKEY
"score:1"

127.0.0.1:6379> SRANDMEMBER post:1:tags 
"t2"
127.0.0.1:6379> SRANDMEMBER post:1:tags 2
1) "t1"
2) "t3"
127.0.0.1:6379> SRANDMEMBER post:1:tags -2
1) "t1"
2) "t1"
  • RANDOMKEY 레디스 전체 키 중 랜덤 반환
  • SRANDMEMBER key [count] set 자료구조인 key 중에서 랜덤으로 값 반환
    • count 가 양수면 중복 제거, 음수면 중복 가능
  • HRANDFIELD (hash 키에서 랜덤 반환), SRANDMEMBER (set 키에서 랜덤 반환), ZRANDMEMBER (sorted set 키에서 랜덤 반환)

 

좋아요 카운팅하기 - set

set으로 뉴스기사key에 좋아요 누른 유저id 넣기, SCARD(cardinality)로 갯수 세기

 

127.0.0.1:6379> SADD commnent-like:123 1  //기사: id 123, 좋아요 유저: id 1
(integer) 1
127.0.0.1:6379> SCARD commnent-like:123 
(integer) 1

 

읽지 않은 메시지 수 카운팅하기 - hash

127.0.0.1:6379> HINCRBY user:1 channel:1 1
(integer) 1
127.0.0.1:6379> HINCRBY user:1 channel:1 1
(integer) 2
127.0.0.1:6379> HINCRBY user:1 channel:1 -1
(integer) 1
  • HINCRBY user:1 channel:1 1 key field value
    • 음수면 빼기

 

DAU구하기 - bitmap

  • Daliy Active User: 하루동안 방문한 사용자 수
  • 하루에 천 만명이상이면 set으로 unique 유저를 저장하는게 성능 저하 일으킬 수 있다.
    • 키 하나단 저장하는 권장 아이템: 200~300만
  • 해결: bitmap으로. 유저 한 명 당 1bit라 1000만명이면 1000만bit = 1.2MB 수준이라 string 최대512MB보다 훨씬 적어서 문제 없음

 

SETBIT, BITCOUNT, BITTOP

127.0.0.1:6379> SETBIT uv:20250117 14 1
(integer) 0
127.0.0.1:6379> SETBIT uv:20250117 14 2
(error) ERR bit is not an integer or out of range // 1보다 크면 에러남
127.0.0.1:6379> SETBIT uv:20250117 15 1
(integer) 0
127.0.0.1:6379> BITCOUNT uv:20250117
(integer) 2

// 출석 이벤트로 3일간 연속 방문한 유저 구하기
BITTOP AND event:202501 uv:20250117 uv:20250118 uv:20250119
  • SETBIT uv:20250117 14 1 일 별 key에 유저14가 방문했으면 14번 비트를 1로 세팅
  • BITCOUNT uv:20250117 일 방문자 수
  • BITTIP AND uv:* 키에서 AND 연산 결과를 event:202501 키에 신규 저장
    • AND, OR, XOR, NOT 옵션 가능

 

애플리케이션 미터링 - hyperloglog

  • 미터링 솔루션: 클라우드 환경에서 pay as you go에 따라 서비스 얼마나 사용하는지 측정하는 솔루션
  • 아래 조건 만족하면 hyperloglog 고려
    • 집합 내의 유일한 데이터 개수를 카운팅해야 한다.
    • 1% 미만의 오차는 허용 가능
    • 카운팅할 때 사용한 데이터를 다시 확인하지 않아도 된다.

 

<유저가 호출한 API개수를 측정하는 시나리오> (같은 API는 여러 번 호출해도 과금 안 하나봄)

PFADD, PFCOUNT, PFMERGE

127.0.0.1:6379> PFADD 202201:user:1 111
(integer) 1
127.0.0.1:6379> PFADD 202201:user:1 222
(integer) 1
127.0.0.1:6379> PFADD 202201:user:1 111
(integer) 0
127.0.0.1:6379> PFCOUNT 202201:user:1 
(integer) 2

// 유저 연도별 합산
PFMERGE 2022:user:1 202201:user:1 202202:user:1
  • PFADD 202201:user:1 111 user1가 사용한 API id 111 더하기
  • PFCOUNT 202201:user:1 카티널리티 카운팅 (중복제거됨)

 

위치 기반 애플리케이션 - geospatial

127.0.0.1:6379> GEOADD restaurant 127.027 37.497 sikdang
(integer) 1

127.0.0.1:6379> GEOSEARCH restaurant fromlonlat 127.027 37.496 BYRADIUS 1 km
1) "sikdang"
127.0.0.1:6379> GEOSEARCH restaurant frommember sikdang BYRADIUS 1 km
1) "sikdang"
127.0.0.1:6379> GEOSEARCH restaurant frommember sikdang BYBOX 4 2 km
  • GEOSEARCH restaurant fromlonlat 127.027 37.496 BYRADIUS 1 km : 사용자 위치(fromlonlat)로부터 1km 반경 검색
  • GEOSEARCH restaurant frommember sikdang BYRADIUS 1 km : 멤버(sikdang)로부터 1km 반경 검색
    • GEOSEARCH restaurant frommember sikdang BYBOX 4 2 km : 멤버(sikdang)를 중심으로 가로 4km, 세로 2km 박스 검색

 

 

 

레퍼런스

개발자를 위한 레디스 책

글 읽어주셔서 언제나 감사합니다. 좋은 피드백, 개선 피드백 너무나도 환영합니다.