❐ Description
Redis의 자료구조를 적절히 활용해 애플리케이션 성능을 향상 시키며,
동시에 개발의 단순성과 편의성을 증대할 수 있는 방법에 대해서 알아보자.
+ ) 실무에서 발생할 수 있는 요구 사항을 생각해서 직접 예제를 만들고 실습까지 해보자.
❐ sorted set을 이용한 사례
ChatGPT에게 예제 데이터를 만들어 달라고 했다.
Redis의 자료구조 sorted set으로 리더보드 예시를 만들꺼야.
2024년 12월 1일 부터 18일까지, 예제 데이터를 만들어 줘.
template : ZADD daily-score:<yyyymmdd> <point> user:<id>
각 요일마다 데이터수는 10개 이상이고 같은 날에 user의 id는 중복될 수 없어.(다른 날은 상관 없어)
1. 실시간 리더보드 & 랭킹 합산
sorted set은 저장된 데이터를 오름차순으로 반환하는데, 이를 응용하면 실시간 리더보드를 쉽게 구현할 수 있다.
REV 커맨드를 추가하면 내림차순으로 데이터를 반환한다.
127.0.0.1:6379> ZRANGE daily-score:20241211 0 -1 WITHSCORES
user:475
29
user:284
110
user:195
163
user:344
445
user:19
531
user:325
561
user:252
736
user:375
744
user:115
808
user:471
873
또한, Redis에서는 랭킹 합산과 같은 연산은 `ZUNIONSTORE` 커맨드를 사용해 간단하게 구현할 수 있다.
ZUNIONSTORE <생성할 키 이름><합산할 키 갯수><합산할 키>...<합산할 키>
🧩 단순 합산 랭킹 ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯
ChatGPT가 만들어준 예시 중, 7일(12월1일 ~ 7일)의 랭킹을 합산해봤다.
7일치 데이터 합산
127.0.0.1:6379> ZUNIONSTORE weekly-score:12-1 7 daily-score:20241201
daily-score:20241202 daily-score:20241203 daily-score:20241204
daily-score:20241205 daily-score:20241206 daily-score:20241207
12.01 ~ 12.07 기간동안 1등 ~ 10등 조회
127.0.0.1:6379> ZRANGE weekly-score:12-1 0 9 WITHSCORES REV
user:53의 데이터 검증 : (788 + 740) = 1528
user:52
1528
user:383
1201
user:485
1071
user:484
1013
user:174
999
user:331
935
user:298
899
user:384
897
user:190
877
user:147
865
🧩가중치 합산 랭킹 ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯
가중치를 부여하기 위해선 `weights` 커맨드를 사용하면 된다.
ZUNIONSTORE weekly-score:12-mon 3
daily-score:20241202 daily-score:20241209 daily-score:20241216
WEIGHTS 1 2 1
ZRANGE weekly-score:12-mon 0 9 REV WITHSCORES
✅ user:106 데이터 검증
12월 9일 점수 = 989점
가중치 = 2
결과 = 989 * 2 = 1978
user:106
1978
user:361
1910
user:295
1910
user:82
1637
user:455
1510
user:332
1430
user:395
1172
user:30
954
user:484
937
user:67
922
2. 최근 검색 기록
실습을 위해 ChatGPT를 활용해서 예제를 직접 만들어 봤다.
127.0.0.1:6379> ZADD search-keyword:123 20241206200753 핸드폰
127.0.0.1:6379> ZADD search-keyword:123 20241206041701 노트북
127.0.0.1:6379> ZADD search-keyword:123 20241201090857 청소기
127.0.0.1:6379> ZADD search-keyword:123 20241210021952 에어팟
127.0.0.1:6379> ZADD search-keyword:123 20241217121701 책상
127.0.0.1:6379> ZADD search-keyword:123 20241211112843 의자
127.0.0.1:6379> ZADD search-keyword:123 20241207213755 모니터
127.0.0.1:6379> ZADD search-keyword:123 20241209044331 키보드
127.0.0.1:6379> ZADD search-keyword:123 20241212233922 헤드셋
127.0.0.1:6379> ZADD search-keyword:123 20241204081511 스피커
🧩 ZRANGE ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯
최근 검색 기록을 조회해보면 다음과 같다.
127.0.0.1:6379> ZRANGE search-keyword:123 0 4 REV
책상
헤드셋
의자
에어팟
키보드
여기서 사용자가 에어팟을 추가로 다시 검색했다고 가정 해보자.
ZADD search-keyword:123 20241217173413 에어팟
그리고 다시 최근 검색 기록을 조회해보면 `에어팟`이 가장 상위에 위치한 것을 확인할 수 있다.
127.0.0.1:6379> ZRANGE search-keyword:123 0 4 REV
에어팟
책상
헤드셋
의자
키보드
최대로 보관할 수 있는 검색 기록이 5개라면 어떻게 될까?
ZADD search-keyword:123 20241217203413 아이폰
🧩 ZREMRANGEBYRANK ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯
물론 `ZRANGE`를 사용해서 갯수를 필터링 해줄 수 있지만, 불필요하게 많은 데이터가 저장이 될 것이다.
이럴 때는 ZREMRANGEBYRANK 커맨드를 사용하면 불필요한 데이터 저장을 방지할 수 있다.
최근 검색 기록 5개 초과
127.0.0.1:6379> ZRANGE search-keyword:123 0 -1
청소기
노트북
핸드폰
에어팟
책상
아이폰
sorted set의 minus-index를 사용해서 가장 오래된 검색 기록 제거
127.0.0.1:6379> ZREMRANGEBYRANK search-keyword:123 -6 -6
1
결과적으로 O(n)으로 항상 5개의 검색결과 유지
127.0.0.1:6379> ZRANGE search-keyword:123 0 -1
노트북
핸드폰
에어팟
책상
아이폰
3. 태그 기능
만약 내가 채용공고 사이트를 운영하는 사람이라면, 사용자의 기술 스택을 Redis의 tag 기능을 사용해서
구현해볼 수 있을 것 같다.
🧩 사용자를 기준으로 태그 추가 ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯
127.0.0.1:6379> SADD user:123:tags springboot jpa redis
127.0.0.1:6379> SADD user:124:tags react javascript
127.0.0.1:6379> SADD user:125:tags redis express node.js
127.0.0.1:6379> SMEMBERS user:125:tags
redis
express
node.js
🧩 태그를 기준으로 사용자 추가 ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯
127.0.0.1:6379> SADD tag:springboot:user 100
127.0.0.1:6379> SADD tag:springboot:user 200
127.0.0.1:6379> SMEMBERS tag:springboot:user
100
200
🤔 현재 가입한 사용자가 가장 많이 보유한 스킬셋은? ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯
위와 같은 기능을 구현해야 한다면, sorted set을 응용하면 될 것으로 생각된다.
사용자 태그 선택
SADD tag:springboot:user 100
태그 popularity 갱신
ZINCRBY tag:popularity 1 springboot
4. 랜덤 데이터 추출
"이벤트 응모 결과 추첨" 같은 요구사항을 해결할 수 있다.
❐ 다양한 카운팅 방법
1. 좋아요 처리하기
set 자료구조를 사용하면 RDB의 부하를 방지할 수 있다.
2. 읽지 않은 메시지 카운팅하기
이 부분이 굉장히 흥미로웠다. 기존 wewoot 서비스에서 채팅은 sendbird를 사용했었고, 사용자가 읽지 않은
메시지의 수를 RDB로 관리했었다. 그러다보니 코드가 복잡해졌던 기억이 난다.
다음과 같이 Redis를 사용하면 복잡한 로직을 단순화할 수 있을 것 같다.
- A ➔ B 메시지 발송 (B가 읽지 않은 메시자 카운트 +1)
- B가 메시지를 읽는다. (B가 읽지 않은 메시자 카운트 -1)
- B ➔ A 메시지 발송 (A가 읽지 않은 메시자 카운트 +1)
user 123과 124가 읽지 않은 채팅 데이터 셋팅
127.0.0.1:6379> HSET user:123:chat user:124 10 user:125 3
127.0.0.1:6379> HSET user:124:chat user:123 0
User123 : 메시지 읽고, User124에게 메시지 전송
# 메시지 읽고
127.0.0.1:6379> HSET user:123:chat user:124 0
127.0.0.1:6379> HGETALL user:123:chat
user:124
0
# 메시지 전송
127.0.0.1:6379> HINCRBY user:124:chat user:123 1
# User124 읽지 않은 메시지 카운트 증가
127.0.0.1:6379> HGETALL user:124:chat
user:123
1
3. DAU 구하기
책에서는 사용자의 PK가 0부터 순차적으로 증가하는 것을 바탕으로 설명하고 있는 것 같다.
그러나 PK 값을 다른 규칙을 적용해서 사용하면 BitMap을 사용해서 DAU를 구할 수 있을까?
➔ 아마도 Hashing을 통해 PK 값을 변경해줘야 할 것같다.
4. hyperloglog를 이용한 애플리케이션 미터링
필요할 때 다시 읽어보기.
❐ Geospatial Index를 이용한 위치 기반 애플리케이션 개발
TODO : 내 위치 기준 1km 반경에 있는 스탬프 장소 목록
geo set은 위치 공간 관리에 특화된 데이터 구조로, 각 위치 데이터는 경도와 위도의 쌍으로 저장된다.
이 데이터는 내부적으로 sorted set 구조로 저장된다.
GEOADD nowon 127.063205 37.656324 7
127.0.0.1:6379> GEOPOS nowon 23
127.05789059400558472
37.65012329205499952
lat, log 기준으로 반지름 500m 이내 장소
127.0.0.1:6379> GEOSEARCH nowon fromlonlat 127.051729 37.652193 byradius 500 m
26
14
12
2
15
16
13번 장소를 중심으로 사각형(좌우 400m, 상하 800m) 이내 장소
127.0.0.1:6379> GEOSEARCH nowon FROMMEMBER 13 BYBOX 400 800 M
7
13
🤔 내 위치 근방 맛집 마커 찍기 ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯
127.0.0.1:6379> GEOADD nowon 127.055194 37.657318 40
127.0.0.1:6379> HSET restaurant:40 name "현대옥" category "한식" rating "4.5"
- 위와 같이 GEOADD에는 PK만 저장하고, 추가적인 정보는 Redis의 Hash 자료구조로 관리한다.
- 장소에 대한 추가적인 정보가 필요할 경우 RDB에서 Place Entity를 조회한다.
이렇게 하면 RDB 접근은 최소화하고 빠르게 사용자에게 근처 맛집 정보를 제공해줄 수 있을 것 같다.
'Back-End > Redis' 카테고리의 다른 글
레디스 데이터 백업 방식 (0) | 2025.01.08 |
---|---|
Redis를 캐시로 사용하기 (0) | 2024.12.20 |
Redis 기본 개념 (0) | 2024.12.18 |
Redis 설정하기 (0) | 2024.12.17 |
마이크로서비스 아키텍쳐와 레디스 (0) | 2024.12.16 |