실시간 리더보드 - 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:2501172개 키를 합해서 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 1key 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 ANDuv:* 키에서 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 111user1가 사용한 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 박스 검색

레퍼런스
개발자를 위한 레디스 책
글 읽어주셔서 언제나 감사합니다. 좋은 피드백, 개선 피드백 너무나도 환영합니다.
'SearchDeveloper > 개발자를 위한 레디스' 카테고리의 다른 글
| 6장 레디스를 메시지 브로커로 사용하기 (1) | 2026.02.08 |
|---|---|
| 5장 레디스를 캐시로 사용하기 (0) | 2026.02.07 |
| 3장 레디스 기본 개념 (0) | 2026.01.16 |
| 2장 레디스 시작하기 (1) | 2026.01.16 |
| 1장 마이크로서비스 아키텍처와 레디스 (1) | 2026.01.16 |