-
Notifications
You must be signed in to change notification settings - Fork 1
6 의사결정_Redis 도입 이유
Jina Ham edited this page Feb 18, 2026
·
1 revision
- 초고빈도/짧은 TTL 데이터를 DB에 쌓으면 I/O + 인덱스 부하가 커짐 → Redis가 적합
- 원자적 증가/감소(incr/decr), 키 만료(TTL), 정렬 컬렉션(ZSET) 같은 기능이 바로 제공됨
- “조회/검증”을 DB로 보내기 전에 캐시/게이트 역할로 사용 → DB 트래픽 절감 + 응답 지연 감소
- 만료 가능한 데이터(오늘/7일/30일/최근검색어) 특성상 자동 정리가 핵심 → TTL로 운영 단순화
- 사용자별/취미별 일일 AI 추천 호출 제한(DAILY_LIMIT=3) 을 DB 없이 빠르게 강제
- Key:
ai:activity:recommend:{userSocialId}:{hobbyId}:{yyyyMMdd} - 날짜를 키에 포함 → 매일 새 카운터로 자연스럽게 분리
- 구조: String(Value=Int) (Redis String)
- 연산:
INCR(opsForValue().increment)- Redis INCR는 원자적이라 동시 호출에도 카운트가 꼬이지 않음
- 최초 생성(count==1) 시에만 TTL 설정
- TTL:
secondsUntilMidnight()→ 자정 기준 자동 초기화 - 장점: “하루 제한” 정책을 코드/배치 없이 TTL로 해결
- 초과(count > limit) 시 예외 발생으로 즉시 차단
- 실패한 요청(예: FastAPI 호출 실패)에서 보정 필요 시
decrease()로 되돌림- 단, 감소는 보정용이므로 0 미만 방지 가드가 있음
- “오늘 이 취미로 기록을 남겼는지”를 빠르게 판단해서
- 홈 대시보드/뱃지/알림/미션 같은 UI를 즉시 구성
- DB에서 매번
exists(select 1...)치지 않도록 최적화
- Key:
record:{yyyy-MM-dd}:{userId}:{hobbyId} - 값은 의미 없으므로
"true"같은 더미
- 구조: String(Boolean 의미만)
- 연산:
- 기록 생성 시:
SET key "true" EX secondsUntilMidnight - 조회 시:
EXISTS key - 삭제(기록 삭제/롤백) 시:
DEL key
- 기록 생성 시:
- TTL:
secondsUntilMidnight()→ 하루 단위 플래그 자동 소멸 - 장점: “오늘 여부”는 하루가 지나면 의미가 사라지므로 TTL이 정답
- FastAPI로 생성한 요약문을 7일간 재사용
- 동일 요청이 반복될 때 매번 AI 호출/비용/지연이 발생하는 걸 방지
- Key:
ai:user:summary:text:{userSocialId}:{hobbyId} - 날짜를 키에 포함하지 않음 → 7일 동안 동일 키로 캐시 히트 유도
- 구조: String(Text) (Redis String)
- 연산:
- 존재 여부:
EXISTS - 저장:
SETEX (TTL=7 days) - 조회:
GET
- 존재 여부:
- TTL: 7일 고정
- 장점:
- “요약”은 매일 바뀌는 데이터가 아니고, 적당한 기간 단위로 갱신하면 충분
- TTL로 자동 갱신 주기를 강제하여 운영이 단순
- FastAPI 실패 시 빈 문자열 반환(기본값)
- 장애 시에도 서비스 전체가 막히지 않게 “캐시/AI는 부가 기능”로 취급
- refreshToken은 보안/폐기/갱신이 빈번하고 TTL이 명확함
- DB 저장 시:
- 테이블 증가 + 정리 배치 필요 + 로그인/재발급 트래픽 증가
- Redis에 두면:
- TTL로 자동 만료
- 토큰 교체/폐기가 빠름
-
@RedisHash(value="refreshToken")+@Id(username=socialId) - 결과적으로 “사용자별 refreshToken 1개” 모델에 적합
- 재발급 시
update()로 덮어쓰기
- 재발급 시
- timeToLive = 7일
- 장점: 만료 정책을 Redis가 자동 수행 → 별도 정리 로직 불필요
- 사용자별 최근 검색어 “최신순” 유지 + 중복 제거 + 최대 N개 유지
- DB로 만들면:
- INSERT/DELETE 빈번 + 정렬/페이징 비용 + 청소 배치 필요
- Redis ZSET이 딱 맞음:
- score로 시간 정렬
- 멤버 중복 시 score 갱신으로 “자동 최신화”
- Key:
recent:{userId}
- 구조: ZSET(member=keyword, score=timestamp)
- 연산:
- 저장/최신화:
ZADD key keyword score(now)- 동일 keyword면 score만 갱신 → “중복 제거 + 최신화” 동시 달성
- 조회(최신 5개):
ZREVRANGE WITHSCORES 0..4 - 용량 제한(20개 유지): size 초과분
ZREMRANGEBYRANK - 만료: key TTL 30일
- 저장/최신화:
- TTL: 30일 → 사용 안 하는 유저의 데이터 자동 정리
- MAX_SIZE: 20 → 메모리 사용 예측 가능
- VIEW_SIZE: 5 → 응답 payload 최소화
- “핫한 기록(Hot Feed)”을 DB에서 매번 집계하면 비용이 큼
- reaction 테이블은 증가 속도가 빠르고, 집계는 join/group by가 무거움
- Redis ZSET으로 점수(반응 수)를 유지하면:
- 쓰기(리액션 발생) 시 O(logN) 업데이트
- 읽기(랭킹 조회) 시 상위 N개를 빠르게 제공
- Key:
reaction:ranking:30days - member:
recordId - score: “최근 30일 반응 점수”
- 리액션 추가:
ZINCRBY +1 - 리액션 취소: 점수 조회 후
ZINCRBY -1(0 미만 방지) - 핫피드 조회:
-
ZREVRANGEBYSCORE로 score 기준 상위 추출 - 동점(score 동일) 정렬/커서 정밀화는 DB에서 처리하도록 설계(혼합 전략)
-
- 매일 00:05 배치:
- 31일 전 하루치 반응 집계값을 DB에서 가져옴
- 해당 recordId에 대해 Redis score를
-count로 차감
- score <= 0 인 항목 제거:
removeRangeByScore(-inf, 0) - 결과: Redis에는 항상 “최근 30일” 점수만 남도록 유지
- 실시간(증가/감소) + 배치(만료분 차감)를 조합해
- “정확한 30일 슬라이딩 윈도우”를 비교적 단순하게 구현
- 읽기 트래픽이 커질수록 DB를 보호하는 효과가 커짐
- 일일 제한 카운터: String(Int) + 자정 TTL + 날짜 포함 키
- 오늘 여부 플래그: String(Boolean) + 자정 TTL + 날짜 포함 키
- AI 요약 캐시: String(Text) + 7일 TTL + 날짜 미포함 키
- refreshToken: RedisHash + 7일 TTL + 사용자 1:1 키
- 최근 검색어: ZSET(keyword, timestamp) + 30일 TTL + size cap(20)
- 30일 리액션 랭킹: ZSET(recordId, score) + (실시간 incr + 배치 차감) + score<=0 제거
Active Development
Helping users stick to their hobbies with AI recommendations and sticker rewards.
GitHub: https://github.com/HamJina Email: hamjina61@gmail.com
All rights reserved.
Last Updated: 2026-02