레디스 자료구조 활용사례 #
리더보드 #
경쟁자들의 순위와 현재 점수를 보여주는 순위표를 의미한다. 스코어로 정렬되어 상위 경쟁자의 순위를 보여준다.
절대적 리더보드 서비스의 모든 유저를 정렬시켜 상위권의 목록만을 표시
상대적 리더보드 사용자의 스코어를 기반으로 그들을 다른 사용자와 비교해 순위를 결정 ex) 유저와 인접해있는 경쟁자들의 스코어를 보여주는 리더보드, 특정 그룹 내에서의 순위를 보여주는 리더보드, 주간 리더보드
리더보드는 기본적으로 사용자의 스코어를 기반으로 데이터를 정렬하는 서비스이기 때문에 사용자의 증가에 따라 가공해야할 데이터가 몇 배로 증가한다. 또한 리더보드는 실시간으로 반영돼야하는 데이터다. 데이터가 실시간으로 계산되어, 자신의 순위 변동이 바로 확인되어야한다.
sorted set을 이용한 리더보드 레디스의 sorted set 은 데이터가 저장될 때부터 정렬된다. 유저의 스코어를 가중치로 설정하여 스코어 순으로 유저를 정렬할 수 있다.
▶ daily-score:<날짜> 를 이용해 sorted set 키를 만들고, 사용자의 스코어를 가중치로 사용해서 데이터를 입력해보자.
ZADD daily-score:228017 28 player:286
ZADD daily-score:228017 400 player:234
ZADD daily-score:228017 45 player:101
ZADD daily-score:228017 357 player:24
ZADD daily-score:228017 199 player:143
위와 같이 데이터를 저장했다. 데이터는 스코어 순으로 정렬되어 있을 것이다.
ZRANGE daily-score:228017 0 -1 withscores
▶ 상위 유저 3명만 출력 ZREVRANGE는 sorted set에 저장된 데이터를 내림차순으로 반환한다. 0번 인덱스 ~ 2번 인덱스(세번째 데이터)까지 출력한다.
ZREVRANGE daily-score:228017 0 2 withscores
▶ 데이터 업데이트 sorted set은 데이터가 중복으로 저장되지 않으므로, 같은 아이템을 저장하고자 할때 스코어가 다르면 기존 데이터의 스코어만 업데이트한다. 이와 동시에 재정렬된다.
ZADD daily-score:228017 200 player:286
▶ ZINCRBY 커맨드 스코어를 증감시킬 수 있다.
ZINCRBY daily-score:228017 100 player:24
랭킹 합산 #
주간 리더보드가 매주 월요일마다 초기화된다고 가정하자. 레디스에서 주간 누적 랭킹은 ZUNIONSTORE 커맨드로 간단하게 구현할 수 있다.
▶ ZUNIONSTORE 커맨드 지정한 키에 연결된 각 아이템의 스코어를 합산하는 커맨드다. 해당하는 일자의 키를 지정하기만 한다면 손쉽게 주간 리더보드 데이터를 얻을 수 있다.
- 22년 8월 15일 ~ 17일까지의 데이터 합산
// <생성할 키 이름><합산할 키 개수><합산할 키>...
ZUNIONSTORE weekly-score:2208-3 3 daily-score:228015 daily-score:228016 daily-score:228017
- 합산한 결과 조회
ZRANGE weekly-score:2208-3 0 -1 withscores
ZREVRANGE weekly-score:2208-3 0 -1 withscores // 역순정렬
- 스코어에 가중치 주기 8월 16일에 스코어 두배 이벤트가 있었다면, 두배로 늘려 계산해야한다.
// 순서대로 15일, 16일, 17일에 x1, x1, x2의 결과값으로 합산된 랭킹을 구할 수 있다.
ZUNIONSTORE weekly-score:2208-03 3 daily-score:228015 daily-score:228016 daily-score:228017 weights 1 1 2
sorted set을 이용한 최근 검색 내역
- 유저 별로 다른 키워드 노출
- 검색 내역은 중복 제거
- 가장 최근 검색한 5개의 키워드만 사용자에게 노출
sorted set은 중복을 허용하지 않으며, 스코어를 시간으로 사용한다면 최근 검색 기록을 정렬할 수 있다. 데이터를 저장할때 유저가 검색한 시간을 스코어로 저장한다면 검색 시간 순으로 정렬된 데이터가 저장된다.
▶ user id가 123인 유저의 검색 기록 저장
ZADD search-keyword:123 20221106143501 코듀로이
ZADD search-keyword:123 20221105220913 실버
ZADD search-keyword:123 20221105221002 반지갑
ZADD search-keyword:123 20221105220954 에나멜
ZADD search-keyword:123 20221106152734 기모후드
▶ 최근 데이터 조회
ZREVRANGE search-keyword:123 0 4 withscores
▶ 반지갑이라는 키워드 재검색
ZADD search-keyword:123 20221106160104 반지갑
▶ 오래된 데이터 삭제 sorted set의 음수 인덱스를 사용해서 데이터를 삭제한다면, 아이템의 개수(6개가 되었는지)를 확인해야하는 번거로운 작업을 줄일 수 있다. 음수 인덱스는 아이템의 제일 마지막 값을 -1로 시작해서 역순으로 증가하는 값이다. ex) 데이터가 6개라면, 인덱스 0 또는 음수인덱스 -6이 제일 오래된 아이템이다.
- 6번째 데이터를 삽입한다.
ZADD search-keyword:123 20221106165302 버킷햇
- 전체 데이터를 조회한다.
ZREVRANGE search-keyword:123 0 -1 withscores
- 0번째 인덱스 또는 -6 인덱스인 ‘실버’를 삭제한다.
ZREMRANGEBYRANK search-keyword:123 -6 -6 // -6부터 -6까지
만약 아이템의 개수가 5개보다 많지 않을때에는 -6번째 인덱스는 존재하지 않기 때문에 삭제된 데이터가 없으므로 영향을 주지 않는다. 이로써 굳이 아이템의 개수를 체크할 필요없이 최근 데이터 5개만 유지할 수 있다.
sorted set을 이용한 태그 기능 ▶ 각 포스트가 사용하는 태그를 레디스의 set을 이용해 저장해보자. 태그는 IT, REDIS, DataStore 이라고 하자.
SADD post:47:tags IT REDIS DataStore
SADD post:22:tags IT python
태그 기능을 사용하는 이유는 특정 게시물이 어떤 태그와 연관돼 있는지 확인하기 위함과, 특정한 태그를 포함한 게시물들만 확인하기 위해서다.
▶ 태그를 기준으로 하는 set에 각각 데이터를 넣어보자.
SADD tag:DataStore:posts 53
SADD tag:IT:posts 53
SADD tag:MySQL:posts 53
▶ SMEMBERS 커맨드 특정 태그를 갖고있는 포스트를 쉽게 확인할 수 있다.
SMEMBERS tag:IT:posts
▶ SINTER 커맨드 IT, DataStore 태그를 모두 포함하는 게시물을 확인하고 싶은 경우 set의 교집합을 구하면된다.
SINTER tag:IT:posts tag:DataStore:posts
좋아요 처리하기 #
댓글 id를 기준으로 set을 생성한뒤, 좋아요를 누른 유저의 id를 저장하면 중복 없이 데이터를 저장할 수 있다.
- 좋아요를 누른 유저 967 저장
SADD comment-like:12554 967
- 건수 조회
SCARD comment-like:12554
hash를 이용한 읽지 않은 메시지 수 카운팅하기 #
채널에 새로 추가된 메시지의 개수를 확인하면 된다. 사용자의 ID를 키로 사용하고, 채널의 ID를 아이템의 키로 활용해 숫자 형태의 메시지 카운트를 관리한다.
- ID가 234인 사용자 -> 4234 채널에서 새로운 메시지를 수신
HINCRBY user:234 channel:4234 1
- 전송한 메시지를 삭제했다면 데이터 감소
HINCRBY user:234 channel:4234 -1
DAU 구하기 #
DAU(Daily Active User)는 하루 동안 서비스에 방문한 사용자의 수를 의미한다. 하루에 여러번 방문했다 하더라도 한번으로 카운팅되는 값으로, 실제 서비스를 이용한 사용자의 유니크한 수를 파악할 수 있는 지표다.
레디스의 비트맵을 이용하면 메모리를 효율적으로 줄이면서도 실시간으로 서비스의 DAU를 확인할 수 있다. 사용자 ID는 string 자료구조에서 하나의 비트로 표현될 수 있다.
- 2022년 11월 6일에 방문한 유저 id를 구한다. uv:20221106 데이터를 만든 뒤, 접속한 유저 id의 bit를 1로 설정한다. id가 14인 유저가 접근했을때 오프셋 14를 1로 설정해준다.
SETBIT uv:20221106 14 1
- 유저 수 확인
BITCOUNT uv:20221106
▶ BITOP AND 커맨드 출석 이벤트를 진행하기 위해 특정 기간 11월 1일부터 11월 3일까지 매일 방문한 사용자를 구해보자.
// 11월 1일 ~ 11월 3일
BITOP AND event:202211 uv:20221101 uv:20221102 uv:20221103
▶ 위 이벤트 결과 확인
GET event:202211