종목 검색 api

[종목 검색 API] 5. Redis Sentinel과 CircuitBreaker 장애 대응 실험

kyoulho 2026. 5. 26. 20:09

Redis Sentinel

Redis Sentinel은 Redis master 장애를 감지하고 replica를 새 master로 승격시키는 HA 구조다. Redis Cluster처럼 데이터를 여러 노드에 분산 저장하는 구조가 아니다.

구분 Redis Sentinel Redis Cluster
목적 master 장애 대응 sharding + HA
데이터 분산 없음 있음
주요 기능 failover hash slot 분산
적합한 상황 단일 Redis의 HA Redis 용량·처리량 분산


Redis cache-aside 구조에서는 Redis가 정상일 때 반복 검색어를 캐시로 흡수한다. 하지만 Redis가 죽으면 cache layer가 사라지고 DB fallback이 증가한다. 따라서 이번 실험의 대상은 Redis Cluster가 아니라 Redis Sentinel이다.


실험 조건

한국투자증권 Open API에서 제공하는 실제 종목 데이터를 사용했다. 해당 데이터를 Batch로 적재해 stock_master 테이블에 저장했고, 총 9,092건의 종목 데이터를 기준으로 실험했다.


부하는 k6로 주었다.

  • 시나리오: 30s@10 -> 1m@30 -> 30s@0
  • 장애 주입: 시작 후 75초에 Redis master stop
  • Redis timeout: 100ms
  • Sentinel 구성: master 1 / replica 1 / sentinel 2

확인한 지표는 다음이다.

  • p95 / p99
  • DB query count
  • Redis get/set error
  • CircuitBreaker bypassed get


장애 흐름

Sentinel 환경의 Redis master 장애 흐름은 다음과 같다.

Redis master 장애
→ Sentinel failover 진행
→ Redis get/set error 발생
→ CircuitBreaker OPEN
→ Redis 호출 bypass
→ DB fallback 증가


Sentinel은 Redis master를 복구한다. 하지만 CircuitBreaker가 OPEN 상태라면 애플리케이션은 복구된 Redis를 호출하지 않는다.

Sentinel은 HA 계층이고, CircuitBreaker는 장애 격리 계층이다. 둘은 대체 관계가 아니다.

Sentinel의 효과를 보려면 CircuitBreaker가 복구된 Redis로 돌아갈 수 있어야 한다.


기존 CircuitBreaker 설정

설정 의미
sliding window type COUNT_BASED 실패율을 시간 기준이 아니라 최근 호출 개수 기준으로 계산한다.
sliding window size 20 최근 Redis 호출 20개를 기준으로 실패율을 계산한다.
minimum number of calls 10 최소 10번 이상 호출이 쌓여야 실패율을 계산한다.
호출 수가 너무 적을 때 성급하게 OPEN 되는 것을 막는다.
failure rate threshold 50 최근 호출 중 실패율이 50% 이상이면 CircuitBreaker를 OPEN 한다.
wait duration in open state 10s OPEN 상태가 되면 10초 동안 Redis 호출을 차단한다.
permitted calls in half-open 3 10초가 지난 뒤 HALF_OPEN 상태에서 Redis에 시험 호출 3개를 허용한다.
automatic transition true OPEN 상태에서 10초가 지나면 자동으로 HALF_OPEN으로 전환한다.
Redis timeout 100ms Redis 명령이 100ms 안에 끝나지 않으면 실패로 본다.


Sentinel 환경에서는 OPEN에서 HALF_OPEN으로 전환되는 시점이 중요하다. 너무 빨리 Redis를 다시 호출하면 failover가 끝나기 전에 error가 증가한다. 너무 늦게 Redis를 다시 호출하면 Redis가 복구됐는데도 DB fallback을 계속 탄다.


후보 선정

이전 실험에서는 주로 Redis 복귀 시점을 앞당기는 방향을 확인했다. 하지만 CircuitBreaker 튜닝에는 다른 축도 있다. Redis 장애를 얼마나 빠르게 감지하고 차단할 것인가다. slidingWindowSize와 minimumNumberOfCalls를 줄이면 더 적은 샘플로 실패율을 판단한다.
장애 감지는 빨라질 수 있지만, 일시적인 오류에도 민감해질 수 있다. 이번에는 복귀 시점과 장애 감지 민감도를 함께 비교했다.

후보 설계 의도
A baseline-current 현재 설정 기준선
B faster-wait waitOpen만 줄였을 때 Redis 복귀가 빨라지는지 확인
C stable-window waitOpen을 줄이되 window, minCalls를 키워 일시 오류에 덜 민감해지는지 확인
D balanced-probe C에서 halfOpen probe만 늘렸을 때 회복 확인이 나아지는지 확인
E sensitive-window window, minCalls를 줄였을 때 장애 감지가 빨라지는지 확인
F sensitive-recovery 민감한 감지와 빠른 복귀를 같이 적용했을 때의 변화 확인
G medium-sensitive A와 E 사이의 중간 민감도 확인


후보별 설정은 다음과 같다.

후보 waitOpen halfOpen slidingWindow SizeminCalls failRate
A baseline-current 10s 3 20 10 50
B faster-wait 5s 3 20 10 50
C stable-window 6s 3 30 12 50
D balanced-probe 6s 4 30 12 50
E sensitive-window 10s 3 10 5 50
F sensitive-recovery 6s 3 10 5 50
G medium-sensitive 8s 3 15 8 50


Redis timeout은 모든 후보에서 100ms로 고정했다. 이번 실험에서는 Redis timeout이 아니라 CircuitBreaker 회복 정책을 비교한다.


측정 결과

후보별 3회 측정 결과의 평균은 다음과 같다.

후보 p95 평균 p99 평균 DB query 평균 get_error 평균 set_error 평균
A 22.90ms 239.41ms 960.33 18.67 9.00
B 27.54ms 175.77ms 946.67 28.67 13.67
C 30.96ms 245.57ms 943.33 30.67 14.33
D 30.52ms 275.08ms 945.33 36.00 19.00
E 27.40ms 190.24ms 953.67 15.67 7.00
F 28.69ms 229.31ms 940.67 24.33 9.33
G 31.56ms 282.83ms 926.33 25.00 8.33


HTTP 실패율은 모든 후보에서 0이었다.


결과 해석

A baseline-current

A는 기존 설정이다.

p95 평균은 22.90ms로 가장 낮다.

다만 get_error 평균은 18.67이고, set_error 평균은 9.00이다.

기준선으로 나쁘지 않지만, cache error 관점에서는 개선 여지가 있었다.

B faster-wait

B는 waitOpen만 10초에서 5초로 줄인 후보다.

p99 평균은 낮아졌지만, get_error와 set_error가 모두 증가했다.

Redis 복귀를 앞당긴 효과보다, failover 직후 Redis를 다시 호출하면서 생기는 error 증가가 더 컸다.

C stable-window

C는 waitOpen을 6초로 줄이고, slidingWindowSizeminimumNumberOfCalls를 키웠다.

DB query 평균은 A보다 낮아졌지만, p95와 cache error가 증가했다.

실패율 판단을 둔감하게 만든다고 장애 구간 안정성이 좋아지지는 않았다.

D balanced-probe

D는 C에서 halfOpen probe를 3에서 4로 늘린 후보다.

get_error와 set_error가 후보 중 가장 높다.

half-open probe를 늘리면 Redis 회복 확인은 더 적극적으로 할 수 있지만, failover 직후 불안정한 Redis에 더 많은 요청을 보낼 수 있다. 이번 조건에서는 불리했다.

E sensitive-window

E는 slidingWindowSize를 20에서 10으로, minimumNumberOfCalls를 10에서 5로 줄인 후보다.

waitOpen과 halfOpen은 기존 설정과 동일하게 유지했다. p95는 A보다 높지만, get_error와 set_error가 후보 중 가장 낮다. E는 장애 감지를 더 민감하게 하면서도 Redis 복귀 시점은 기존처럼 보수적으로 유지한 설정이다. 이번 실험에서는 이 조합이 가장 안정적이었다.

지표 A E
p95 평균 22.90ms 27.40ms
p99 평균 239.41ms 190.24ms
DB query 평균 960.33 953.67
get_error 평균 18.67 15.67
set_error 평균 9.00 7.00

 

F sensitive-recovery

F는 E에서 waitOpen을 10초에서 6초로 줄인 후보다.

DB query 평균은 가장 낮은 편이지만, get_error가 E보다 크게 증가했다.

민감한 장애 감지와 빠른 복귀를 같이 적용하면 Redis error가 다시 늘어난다.

G medium-sensitive

G는 A와 E 사이의 중간 민감도 후보다.

DB query 평균은 가장 낮지만, p95와 p99가 모두 느린 편이다.

set_error는 낮지만 get_error가 높아 선택 후보로 보기는 어렵다.


선택 후보

이번 실험에서는 E sensitive-window를 선택한다.

설정
wait duration in open state 10s
permitted calls in half-open 3
sliding window size 10
minimum number of calls 5
failure rate threshold 50
Redis timeout 100ms


E는 Redis 복귀 시점을 앞당기지 않았다. 대신 장애 감지 window를 줄였다. 즉 “더 빨리 Redis로 돌아가는 설정”이 아니라, “장애 Redis를 더 빨리 차단하는 설정”이다. 이번 측정에서는 이 방향이 더 안정적이었다.


B, F처럼 waitOpen을 줄인 후보는 Redis 복귀를 앞당겼지만 error가 증가했다.

C, D처럼 window를 키운 후보도 p95와 error 측면에서 불리했다.

E는 get/set error가 가장 낮고, p99 평균도 A보다 낮다.


현재 실험 조건에서는 E가 가장 안정적인 선택이다.


운영 반영 기준

이번 결과는 현재 조건에서의 선택이다. 운영 반영 전에는 같은 지표를 다시 본다.

  • p95 / p99
  • DB query count
  • get_error / set_error
  • bypassed_get
  • HTTP 실패율

운영에서는 Redis 배포 방식, 네트워크 지연, 실제 검색 트래픽이 달라질 수 있다.

따라서 운영값은 운영 관측치를 기준으로 확정한다.


정리

Redis 장애 대응은 한 계층으로 끝나지 않는다.

  • Redis timeout은 응답 대기 시간을 제한한다.
  • CircuitBreaker는 장애 Redis 호출을 막는다.
  • Sentinel은 Redis master를 복구한다.
  • DB fallback은 최종 방어선이다.
  • Sentinel을 붙였다고 CircuitBreaker가 필요 없어지는 것이 아니다.
  • CircuitBreaker가 있다고 Sentinel이 필요 없어지는 것도 아니다.
  • Sentinel의 효과를 보려면 CircuitBreaker가 복구된 Redis로 돌아갈 수 있어야 한다.

이번 실험에서는 Redis로 더 빨리 돌아가는 후보보다, 장애 감지를 더 민감하게 하는 후보가 더 안정적이었다.

현재 실험 조건에서는 E 설정을 다음 운영 검증 후보로 둔다.