- Published on
Redis 핫키로 QPS 폭주? LFU로 5분 진단
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버는 멀쩡한데 Redis만 QPS가 튀고, CPU는 100%에 가깝고, 네트워크 대역폭도 급증한다면 가장 먼저 의심해야 하는 게 핫키(hot key) 입니다. 특정 키 하나(또는 소수의 키)에 트래픽이 몰리면 Redis는 싱글 스레드 특성상 해당 요청을 처리하느라 다른 요청까지 밀리며 지연이 전파됩니다.
이 글은 “원인 파악까지 오래 걸리는 상황”을 줄이기 위해, 5분 안에 핫키 가능성을 확인하고 다음 액션(완화, 재발 방지)까지 연결하는 실전 절차를 정리합니다. 특히 LFU(Least Frequently Used) 를 활용해 “어떤 키가 자주 접근되는지”를 짧은 시간에 가시화하는 방법에 집중합니다.
장애 대응 흐름은 프론트엔드 INP나 Long Task 디버깅처럼, “지표 급락
->병목 후보 좁히기->증거 수집”이 핵심입니다. 웹 성능 이슈를 추적하는 방식이 궁금하면 Chrome INP 점수 급락 원인 - Long Task 추적법도 같이 참고하면 좋습니다.
1) 5분 진단 로드맵
핫키 의심 시 가장 빠른 진단 순서는 아래와 같습니다.
- 증상 확인: QPS, latency,
instantaneous_ops_per_sec,blocked_clients등 - 명령 분포 확인: GET/SET/MGET/HGETALL 등 특정 커맨드가 폭증했는지
- 핫키 탐지:
--hotkeys또는 LFU 기반으로 “자주 접근되는 키” 후보 확인 - 키 특성 확인: 타입, 크기, TTL, 해당 키를 만드는 코드 경로
- 즉시 완화: 캐시 키 샤딩, 로컬 캐시, TTL 지터, 읽기 복제/프록시 등
이 글에서는 2~4번에 LFU를 접목해 시간을 단축합니다.
2) 먼저 지표로 “핫키일 가능성”을 빠르게 판별
2.1 INFO로 현재 상태 스냅샷
아래는 운영에서 가장 빨리 보는 조합입니다.
redis-cli -h $REDIS_HOST -p $REDIS_PORT INFO stats
redis-cli -h $REDIS_HOST -p $REDIS_PORT INFO clients
redis-cli -h $REDIS_HOST -p $REDIS_PORT INFO commandstats
핵심 필드 해석:
instantaneous_ops_per_sec: 현재 QPS에 가까운 값total_commands_processed: 누적 커맨드 수(증가 속도로 추세 확인)blocked_clients: 블로킹 연산 또는 리소스 병목 신호commandstats: 어떤 커맨드가 얼마나 호출되는지(예:cmdstat_get,cmdstat_mget)
핫키 패턴은 보통 다음 중 하나로 나타납니다.
GET또는MGET가 비정상적으로 높고, 앱 레이턴시가 동반 상승- 특정 해시/정렬셋을 통째로 읽는
HGETALL,ZRANGE류가 폭증 - 키스페이스 미스가 많아지며 백엔드(DB)까지 연쇄로 느려짐
2.2 SLOWLOG로 “느린 명령”과 키 후보 확인
핫키는 반드시 느린 명령으로 잡히진 않지만, 큰 값(large value)이나 큰 컬렉션을 읽는 경우 SLOWLOG에 단서가 남습니다.
redis-cli SLOWLOG GET 20
여기서 키가 직접 노출되거나(명령 인자), 특정 패턴의 키(prefix)가 반복되면 강력한 힌트입니다.
3) LFU로 핫키를 “증거 기반”으로 좁히기
LFU는 Redis의 maxmemory-policy 중 하나로도 알려져 있지만, 핫키 진단 관점에서는 “키별 접근 빈도 카운터”를 활용한다는 점이 핵심입니다.
다만 주의할 점이 있습니다.
- Redis는 키 메타데이터에 LFU 카운터를 유지하지만, 이를 사람이 보기 좋게 전체 키에 대해 바로 덤프하는 API는 없습니다.
- 그래서 실전에서는 다음 2가지 중 하나를 씁니다.
redis-cli --hotkeys(가장 간단)- LFU 정책을 켠 상태에서
SCAN으로 샘플링 후OBJECT FREQ로 후보를 좁힘
3.1 1분 컷: redis-cli --hotkeys
redis-cli --hotkeys
이 커맨드는 내부적으로 샘플링을 수행해 “핫한 키”를 추정합니다. 결과에 상위 몇 개 키가 반복적으로 등장하면, 그 키가 QPS 폭주의 주범일 확률이 큽니다.
운영 팁:
- 트래픽이 높은 시간대에 2~3회 반복 실행해 상위 키가 안정적으로 재현되는지 봅니다.
- 클러스터 환경이면 해당 키가 위치한 노드에서 실행해야 합니다.
3.2 LFU 정책을 활용한 샘플링 진단(5분 플로우)
--hotkeys가 여의치 않거나, 좀 더 “수치 기반”으로 확인하고 싶다면 다음 절차를 권합니다.
(1) 현재 메모리 정책 확인
redis-cli CONFIG GET maxmemory-policy
LFU 기반을 쓰려면 일반적으로 allkeys-lfu 또는 volatile-lfu가 필요합니다.
allkeys-lfu: 모든 키를 대상으로 LFU 메타데이터가 의미 있게 쌓임volatile-lfu: TTL이 있는 키만 대상(캐시 키에 TTL을 강제하는 환경이면 유용)
(2) 정책 변경은 “진단 목적”으로 신중히
운영에서 정책 변경은 eviction 동작을 바꿔 장애를 키울 수 있습니다. 가능하면 복제본(replica) 또는 스테이징에서 재현하거나, 최소한 아래처럼 원복 계획을 포함하세요.
# (예시) 현재 값 백업
redis-cli CONFIG GET maxmemory-policy
# (예시) 진단을 위해 변경
redis-cli CONFIG SET maxmemory-policy allkeys-lfu
# (예시) 재시작 후에도 유지하려면 redis.conf 또는 CONFIG REWRITE 고려
(3) 3~5분 정도 관찰 후, 키 샘플링과 LFU 빈도 확인
LFU 카운터는 접근이 누적되어야 의미가 생깁니다. 트래픽이 실제로 흐르는 상태에서 몇 분만 지나도 상위 키는 눈에 띄게 튑니다.
아래는 SCAN으로 키를 샘플링하고 OBJECT FREQ로 빈도를 확인하는 예시입니다.
# 패턴이 있다면 MATCH로 범위를 좁히는 게 훨씬 빠릅니다.
redis-cli --scan --pattern 'cache:*' | head -n 200 | while read -r k; do
freq=$(redis-cli OBJECT FREQ "$k" 2>/dev/null)
echo "$freq\t$k"
done | sort -nr | head -n 20
해석:
- 상위에 반복적으로 등장하는 키가 있으면 핫키 후보
freq가 비정상적으로 높고, 그 키의 타입이string이든hash든 상관없이 “접근 집중”이 문제
추가로 키의 타입과 크기까지 같이 보면 “왜 느려지는지”도 함께 보입니다.
k='cache:product:123'
redis-cli TYPE "$k"
redis-cli MEMORY USAGE "$k"
redis-cli TTL "$k"
MEMORY USAGE가 크면 네트워크 전송량과 직렬화 비용까지 함께 커질 수 있습니다.TTL이-1(만료 없음)인 캐시 키는 핫키가 되었을 때 방어 수단이 적습니다.
4) 핫키가 생기는 대표 원인 6가지
4.1 단일 인기 리소스(베스트 상품, 공지, 메인 배너)
서비스 특성상 특정 ID가 모든 사용자에게 노출되면, 키도 한두 개로 수렴합니다.
4.2 캐시 키 설계가 “전역 1개 키”로 되어 있음
예: cache:homepage 같은 키에 모든 데이터를 몰아 넣고 자주 갱신/조회.
4.3 TTL 동시 만료(캐시 스탬피드)
다수 키가 같은 TTL로 생성되어 특정 시점에 동시에 만료되면, DB로 몰리고 다시 Redis로 쓰기 폭주가 생깁니다.
4.4 클라이언트 측 재시도 폭주
타임아웃이 짧고 재시도 정책이 공격적이면, Redis가 느려진 순간 “더 많은 요청”으로 악화됩니다. 백오프/리트라이는 Redis뿐 아니라 외부 API에서도 동일하게 중요합니다. 재시도 운영 패턴은 OpenAI 429/RateLimitError 실전 백오프·리트라이에서 설명한 원칙을 그대로 적용할 수 있습니다.
4.5 큰 컬렉션을 통째로 읽는 커맨드
HGETALL, LRANGE 0 -1, ZRANGE 0 -1 WITHSCORES 같은 패턴은 핫키와 결합되면 치명적입니다.
4.6 애플리케이션 버그로 특정 키를 폴링
배치/워커가 GET을 초당 수천 번씩 반복하거나, 실패 시 즉시 루프를 도는 경우입니다. 쿠버네티스 환경에서는 이런 버그가 Pod 재시작과 맞물려 증폭되기도 합니다. 장애 패턴이 비슷하면 K8s Pod CrashLoopBackOff 원인 7가지와 해결도 함께 점검하세요.
5) 발견 즉시 적용 가능한 완화책(우선순위 순)
5.1 캐시 키 샤딩: 핫키를 여러 키로 분산
가장 전형적인 해법입니다. 예를 들어 cache:homepage를 cache:homepage:0..N-1로 쪼개고, 읽을 때는 해시로 선택합니다.
// Node.js 예시
import crypto from 'crypto'
function shardKey(base, userId, shards = 32) {
const h = crypto.createHash('md5').update(String(userId)).digest('hex')
const n = parseInt(h.slice(0, 8), 16) % shards
return `${base}:${n}`
}
const key = shardKey('cache:homepage', userId)
// GET key
주의:
- 샤딩은 “동일 데이터를 모두가 읽어야 하는 경우”엔 정합성/갱신이 번거롭습니다.
- 그럴 땐 “읽기 분산” 대신 아래 5.2, 5.3이 더 효과적일 수 있습니다.
5.2 로컬 캐시(프로세스 메모리)로 1차 방어막
모든 요청이 Redis로 가지 않게, 애플리케이션에 짧은 TTL의 로컬 캐시를 둡니다.
// 간단한 in-memory TTL 캐시 예시
const localCache = new Map()
function getLocal(key) {
const v = localCache.get(key)
if (!v) return null
if (v.expireAt < Date.now()) {
localCache.delete(key)
return null
}
return v.value
}
function setLocal(key, value, ttlMs) {
localCache.set(key, { value, expireAt: Date.now() + ttlMs })
}
5.3 TTL 지터(jitter)로 동시 만료 방지
function ttlWithJitter(baseSec, jitterSec = 30) {
const j = Math.floor(Math.random() * jitterSec)
return baseSec + j
}
캐시 스탬피드를 줄이는 데 즉효가 있습니다.
5.4 단일 큰 값은 쪼개서 부분 조회 가능하게
예: 큰 JSON을 통째로 GET하지 말고, 필드 단위로 키를 분리하거나 hash로 관리해 필요한 필드만 읽습니다.
5.5 읽기 복제본 활용 또는 프록시 계층
읽기 트래픽이 압도적이라면 replica로 분산하거나, Redis Cluster에서 키 분산이 잘 되는지(해시태그 사용 여부 포함) 점검합니다.
5.6 클라이언트 타임아웃/재시도 정책 조정
- 타임아웃을 너무 짧게 두면 재시도가 폭증해 QPS가 더 튑니다.
- 지수 백오프와 최대 재시도 횟수 제한을 강제하세요.
6) 재발 방지: 관측과 가드레일
6.1 키별 QPS를 직접 계측하기
Redis 자체만으로 “키별 QPS”를 완벽히 뽑기는 어렵습니다. 대신 애플리케이션에서 다음을 로그/메트릭으로 남기면 재발 시 탐지가 빨라집니다.
- 상위 N개 키(prefix 포함)별 호출 수
- cache hit/miss
- 특정 키의 응답 크기(직렬화 후 바이트)
6.2 위험한 커맨드 사용 가이드
KEYS는 운영에서 금지(대체:SCAN)- 큰 컬렉션을 통째로 읽는 커맨드는 페이지네이션/부분 조회로 제한
6.3 배포 직후 QPS/latency 자동 알람
핫키는 종종 “배포로 인해 특정 키를 더 자주 조회하게 된 경우” 발생합니다. 배포 후 지표가 튀면 즉시 롤백/차단할 수 있게 알람을 촘촘히 두는 게 비용 대비 효과가 큽니다.
7) 체크리스트: 5분 안에 결론 내리기
아래 순서대로 실행하면, 핫키 여부를 빠르게 결론 낼 수 있습니다.
INFO commandstats에서 폭증 커맨드 확인SLOWLOG GET으로 반복되는 키/패턴 확인redis-cli --hotkeys로 상위 키 후보 확보- 필요 시
SCAN샘플링+OBJECT FREQ로 LFU 상위 키 정렬 TYPE,MEMORY USAGE,TTL로 키 특성 확인- 즉시 완화: 로컬 캐시, TTL 지터, 키 샤딩 중 1개 이상 적용
핫키 대응은 “원인을 찾는 것”보다 “폭주를 멈추는 것”이 먼저인 경우가 많습니다. LFU 기반 진단은 완벽한 포렌식 도구는 아니지만, 짧은 시간에 의심 키를 좁히고 완화책을 선택하는 데 매우 실용적입니다.