콘텐츠로 이동

Redis 자료구조 심화

Redis 자료구조 심화는 요구사항을 어떤 자료구조로 표현할지, 그리고 어떤 명령이 운영에서 위험해질 수 있는지를 판단하기 위한 문서입니다.

용어

용어 의미
Cardinality key 안에 들어 있는 원소 수
Big Key value 크기나 원소 수가 지나치게 큰 key
Hot Key 요청이 한 key에 과도하게 몰리는 상태
Time Complexity 명령 실행 비용이 데이터 크기에 따라 증가하는 방식
Cursor Scan 한 번에 전부 읽지 않고 cursor로 나누어 탐색하는 방식

질문

자료구조는 무엇을 기준으로 고르는가?

자료구조는 저장 모양보다 조회·수정 패턴으로 고릅니다.

요구사항 추천 자료구조 이유 주의
값 하나 저장 String 가장 단순 JSON 일부 수정 어려움
객체 필드 일부 수정 Hash 필드 단위 접근 HGETALL 주의
중복 없는 목록 Set 포함 여부가 빠름 SMEMBERS 주의
랭킹 Sorted Set score 정렬 지원 hot key 주의
간단한 큐 List push/pop이 쉬움 재처리 부족
이벤트 처리 Stream consumer group 지원 Kafka 대체 아님
boolean 대량 저장 Bitmap 메모리 효율 좋음 offset 설계 필요
대략적인 고유 수 HyperLogLog 메모리 절약 정확한 목록 불가
주변 위치 검색 Geospatial 거리 기반 검색 복잡한 공간 검색 한계

전체 명령어 비용 감각

자료구조 안전한 편 조심할 명령
String GET, SET, INCR value GET, 대량 MGET
Hash HGET, HSET, HINCRBY HGETALL, 큰 hash HSCAN
List LPUSH, RPOP, BRPOP LRANGE 0 -1, 긴 list trim
Set SADD, SISMEMBER, SCARD SMEMBERS, 큰 set 연산
Sorted Set ZADD, ZINCRBY, ZRANGE 0 N 큰 범위 조회, 큰 rank 계산
Bitmap SETBIT, GETBIT offset, 전체 BITCOUNT
HyperLogLog PFADD, PFCOUNT 정확한 목록 요구
Stream XADD, XREADGROUP COUNT N trim 없는 무한 증가
Geospatial GEOADD, 제한된 GEOSEARCH 큰 반경 검색

String

String은 가장 기본적인 자료구조입니다. value 하나를 통째로 읽고 쓰는 캐시, 인증 토큰, 인증번호, 카운터에 적합합니다.

주요 명령

명령 의미 사용 예
SET key value 값 저장 캐시 저장
SET key value EX seconds TTL과 함께 저장 인증번호
SET key value NX EX seconds 없을 때만 저장 중복 요청 방지
GET key 값 조회 캐시 조회
MGET key1 key2 여러 값 조회 목록 화면 캐시
INCR key 1 증가 조회수
INCRBY key n n 증가 포인트 임시 집계
DECR key 1 감소 재고 임시 차감

예시

SET auth:email:user-1 "123456" EX 180
GET auth:email:user-1

SET idempotency:payment:req-123 "processing" NX EX 300

INCR product:1001:view-count

String vs Hash

기준 String JSON Hash
전체 객체 조회 좋음 가능
필드 일부 수정 불편 좋음
직렬화 애플리케이션이 담당 Redis 필드로 분리
value 크기 객체 전체가 커질 수 있음 필드가 많아지면 big key
추천 통째로 읽고 쓰는 캐시 일부 필드 갱신이 잦은 객체

주의할 점

주의 설명
큰 JSON value 네트워크와 메모리 비용 증가
카운터 유실 DB 반영 전 Redis 장애 시 값이 사라질 수 있음
TTL 누락 인증번호·토큰이 계속 남을 수 있음

Hash

Hash는 한 객체를 여러 field로 나누어 저장합니다. 사용자 요약 정보, 설정, 장바구니 요약처럼 일부 필드를 자주 읽거나 바꿀 때 유용합니다.

주요 명령

명령 의미
HSET key field value 필드 저장
HGET key field 필드 조회
HMGET key f1 f2 여러 필드 조회
HINCRBY key field n 숫자 필드 증가
HDEL key field 필드 삭제
HLEN key 필드 개수
HSCAN key cursor 필드 점진 조회

예시

HSET user:1 name "kim" grade "gold" point 100
HGET user:1 grade
HINCRBY user:1 point 50
HMGET user:1 name grade point

주의할 점

HGETALL은 필드가 많은 Hash에서 위험합니다.

# 작은 hash면 편함
HGETALL user:1

# 큰 hash면 나누어 조회
HSCAN user:1 0 COUNT 100
실수 대안
사용자 전체 활동 이력을 Hash 하나에 계속 누적 날짜별 key 분리
매 요청마다 HGETALL 필요한 필드만 HMGET
field 수 제한 없음 cardinality 알림

List

List는 양쪽 끝에서 넣고 빼는 자료구조입니다. 간단한 큐나 최근 N개 목록에 적합합니다.

주요 명령

명령 의미
LPUSH 왼쪽에 추가
RPUSH 오른쪽에 추가
LPOP 왼쪽에서 꺼냄
RPOP 오른쪽에서 꺼냄
BRPOP 값이 들어올 때까지 blocking pop
LRANGE 범위 조회
LTRIM 범위 밖 제거
LLEN 길이 확인

예시

LPUSH recent:products:user-1 product-1001
LTRIM recent:products:user-1 0 49
LRANGE recent:products:user-1 0 9

LPUSH job:email "message-1"
BRPOP job:email 5

List vs Stream vs Kafka

기준 List Stream Kafka
구현 난도 낮음 중간 높음
ACK 없음 있음 offset commit
재처리 직접 구현 PEL/claim 활용 강함
여러 consumer 직접 구현 consumer group consumer group
추천 간단한 임시 큐 Redis 내부 작업 큐 서비스 간 핵심 이벤트

Set

Set은 중복 없는 집합입니다. 좋아요 여부, 권한, 태그, 중복 제거에 적합합니다.

주요 명령

명령 의미
SADD 원소 추가
SREM 원소 제거
SISMEMBER 포함 여부
SCARD 원소 수
SINTER 교집합
SUNION 합집합
SDIFF 차집합
SSCAN 점진 조회

예시

SADD post:1:likes user-1
SISMEMBER post:1:likes user-1
SCARD post:1:likes
SREM post:1:likes user-1

Set vs Sorted Set

기준 Set Sorted Set
중복 제거 좋음 좋음
포함 여부 좋음 가능
순서 없음 score 기준 정렬
랭킹 부적합 적합
비용 단순 O(log N) 계열 많음

주의할 점

SMEMBERS는 전체 원소를 반환합니다. 좋아요 사용자가 수백만 명이면 한 번에 가져오면 안 됩니다.

SSCAN post:1:likes 0 COUNT 100

Sorted Set

Sorted Set은 원소마다 score를 갖고, score 기준으로 정렬됩니다. 랭킹, 우선순위, 시간 정렬에 자주 씁니다.

주요 명령

명령 의미
ZADD score와 member 추가
ZINCRBY score 증가
ZRANGE 낮은 score부터 범위 조회
ZREVRANGE 높은 score부터 범위 조회
ZRANK 낮은 score 기준 순위
ZREVRANK 높은 score 기준 순위
ZSCORE member score 조회
ZREM member 제거
ZREMRANGEBYRANK 순위 범위 삭제
ZREMRANGEBYSCORE score 범위 삭제

랭킹 예시

ZINCRBY rank:daily:20260427 120 user-1
ZINCRBY rank:daily:20260427 90 user-2
ZREVRANGE rank:daily:20260427 0 9 WITHSCORES
ZREVRANK rank:daily:20260427 user-1

동점 처리

Sorted Set은 score가 같으면 member 문자열 기준 정렬이 섞일 수 있습니다. 동점 정책이 중요하면 score에 시간이나 보조 점수를 반영하거나, DB에서 최종 순위를 보정합니다.

방식 설명 주의
score만 사용 단순 동점 순서가 업무 기준과 다를 수 있음
score + timestamp 조합 먼저 달성한 사람 우선 등 표현 score 설계가 복잡
Redis는 후보만, DB에서 최종 정렬 정확함 조회 비용 증가

기간별 key 설계

rank:daily:20260427
rank:weekly:2026-W18
rank:monthly:2026-04

기간별 key를 나누면 삭제, 집계, hot key 완화가 쉬워집니다.

Bitmap

Bitmap은 String을 bit 배열처럼 쓰는 방식입니다. 출석, 방문 여부처럼 true/false 상태를 대량 저장할 때 좋습니다.

주요 명령

명령 의미
SETBIT key offset value 특정 bit 설정
GETBIT key offset 특정 bit 조회
BITCOUNT key 1인 bit 개수
BITOP AND/OR/XOR 연산

예시

SETBIT attendance:20260427 1001 1
GETBIT attendance:20260427 1001
BITCOUNT attendance:20260427

주의할 점

offset이 매우 크면 중간 공간까지 메모리를 사용할 수 있습니다. userId를 그대로 offset으로 쓰기 전에 ID 범위와 밀도를 확인합니다.

HyperLogLog

HyperLogLog는 고유 개수를 근사치로 계산합니다.

주요 명령

명령 의미
PFADD 원소 추가
PFCOUNT 고유 수 추정
PFMERGE 여러 HyperLogLog 병합

예시

PFADD uv:20260427 user-1 user-2 user-3
PFCOUNT uv:20260427

PFMERGE uv:202604 uv:20260401 uv:20260402 uv:20260403

정확한 사용자 목록이 필요하면 Set을 써야 합니다. HyperLogLog는 고유 수만 필요하고 약간의 오차를 허용할 때 적합합니다.

Stream

Stream은 append-only log 자료구조입니다. Redis 안에서 이벤트를 쌓고 consumer group으로 처리할 수 있습니다.

주요 명령

명령 의미
XADD 메시지 추가
XRANGE 범위 조회
XREAD 메시지 읽기
XGROUP CREATE consumer group 생성
XREADGROUP group으로 메시지 읽기
XACK 처리 완료
XPENDING pending 메시지 확인
XAUTOCLAIM 오래 pending 된 메시지 인계
XTRIM stream 길이 제한

Consumer Group 예시

XADD order-events * type ORDER_CREATED orderId 1001
XGROUP CREATE order-events order-service $ MKSTREAM
XREADGROUP GROUP order-service consumer-1 COUNT 10 STREAMS order-events >
XACK order-events order-service 1740000000000-0
XPENDING order-events order-service

재처리와 DLQ

1. consumer가 XREADGROUP으로 메시지 읽음
2. 처리 성공 시 XACK
3. 처리 실패 시 재시도
4. 계속 실패하면 DLQ stream에 원본과 에러 저장
5. 원본 메시지는 ACK해서 전체 소비가 멈추지 않게 함

Stream은 Redis 내부의 가벼운 이벤트 처리에 좋습니다. 서비스 간 핵심 이벤트, 긴 보관, 대규모 재처리가 필요하면 Kafka를 우선 비교합니다.

Geospatial Index

Geospatial은 좌표 기반 검색을 지원합니다.

주요 명령

명령 의미
GEOADD 좌표 추가
GEODIST member 거리
GEOSEARCH 좌표/멤버 기준 반경 검색
GEOHASH geohash 조회
ZREM 위치 member 삭제

주변 매장 검색 예시

GEOADD store:geo 127.0276 37.4979 store-1
GEOADD store:geo 127.0290 37.5001 store-2

GEOSEARCH store:geo FROMLONLAT 127.03 37.50 BYRADIUS 2 km WITHDIST COUNT 20 ASC

반경이 너무 크거나 결과 수 제한이 없으면 응답이 커질 수 있습니다. 복잡한 조건 검색, 정교한 공간 검색이 필요하면 검색 엔진을 검토합니다.

Java/Spring 사용 예시

StringRedisTemplate

stringRedisTemplate.opsForValue()
    .set("auth:email:user-1", "123456", Duration.ofMinutes(3));

String code = stringRedisTemplate.opsForValue()
    .get("auth:email:user-1");

Hash

HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
hash.put("user:1", "grade", "gold");
Object grade = hash.get("user:1", "grade");

Sorted Set

redisTemplate.opsForZSet()
    .incrementScore("rank:daily:20260427", "user-1", 120);

Set<ZSetOperations.TypedTuple<String>> top10 = redisTemplate.opsForZSet()
    .reverseRangeWithScores("rank:daily:20260427", 0, 9);

Set

redisTemplate.opsForSet().add("post:1:likes", "user-1");
Boolean liked = redisTemplate.opsForSet().isMember("post:1:likes", "user-1");

장애/성능 체크리스트

체크 질문
Cardinality 한 key에 최대 몇 개까지 들어가는가
전체 조회 HGETALL, SMEMBERS, LRANGE 0 -1을 운영 API에서 쓰는가
TTL key가 자동으로 정리되는가
Hot Key 인기 key 하나에 요청이 몰리는가
Big Key value 크기나 원소 수 알림이 있는가
분할 기준 날짜, 사용자, 도메인별 key 분리가 필요한가

베스트 프랙티스

권장 방식 이유
자료구조 선택 전에 조회 패턴을 먼저 적음 잘못된 모델링 방지
큰 컬렉션은 기간별·샤딩 key로 분리 big key 완화
운영 API에서 전체 조회 명령 제한 지연 방지
Stream/List는 길이 제한 무한 증가 방지
랭킹은 기간별 key 사용 삭제와 집계가 쉬움
정확도 요구를 먼저 확인 Set vs HyperLogLog 선택 기준

실무에서는?

요구 추천 이유
상품 상세 캐시 String JSON 통째 조회
사용자 포인트 일부 갱신 Hash 필드 단위 증가
게시글 좋아요 여부 Set 중복 없는 포함 여부
일간 랭킹 Sorted Set score 정렬
최근 본 상품 50개 List 순서 유지 + LTRIM
출석 체크 Bitmap boolean 대량 저장
일간 UV HyperLogLog 고유 수 추정
비동기 작업 처리 Stream consumer group과 ACK
주변 매장 검색 Geospatial 좌표 반경 검색

관련 파일: - 기본 사용과 자료구조 - 성능 최적화 - Pub/Sub과 Stream