MMeshOps
Redis 실전: 서비스 장애를 막는 안전한 캐싱과 상태 관리
3예상 10중급

Redis가 죽어도 서비스는 돌아가야 한다

Redis 서버가 다운되었을 때 전체 서비스 장애로 번지지 않도록 아키텍처를 견고하게 설계하는 방법을 배웁니다.

우리는 앞선 에피소드들에서 Redis를 성능 향상을 위한 보조 수단으로 써야 한다는 점을 배웠습니다. 하지만 실무에서는 Redis를 도입한 뒤 오히려 전체 서비스가 멈추는 황당한 장애를 겪는 경우가 많습니다.

Redis가 죽었을 뿐인데 웹 서버 전체가 응답하지 않는 상황, 어떻게 된 걸까요? 이번 에피소드에서는 특정 인프라의 장애가 서비스 전체의 장애로 번지는 단일 장애점(SPOF, Single Point Of Failure)의 개념과 이를 예방하는 아키텍처를 알아봅니다.

단일 장애점(SPOF)이란 무엇인가?

단일 장애점(SPOF)이란, 시스템 구성 요소 중 단 하나라도 동작하지 않으면 전체 시스템이 멈춰버리는 취약점을 의미합니다.

여러분이 운영하는 서비스가 Redis에서 최신 인기 게시물을 가져와 메인 화면에 띄운다고 가정해 봅시다. 만약 Redis 서버가 잠시 네트워크 통신이 끊어지거나 다운된다면, 메인 화면 접속자들은 에러 페이지를 보게 될 것입니다.

나쁜 아키텍처에서의 장애 연쇄 반응

Redis 서버 과부하로 다운

애플리케이션에서 Redis 접속 타임아웃 발생

애플리케이션 접속 지연

사용자 요청이 적체되어 웹 서버 스레드 고갈

스레드 고갈로 인한 행(Hang)

Redis뿐만 아니라 전체 웹 서비스 완전 마비

Redis는 "보조 도구"인데, 보조 도구가 고장났다고 튼튼한 주 데이터베이스까지 가는 길을 막아버리는 상황입니다. 이는 아키텍처 설계가 잘못되었음을 의미합니다.

폴백(Fallback) 전략: 실패를 우회하기

견고한 시스템은 Redis가 응답하지 않을 때 빠르게 포기하고 다음 대안으로 넘어가는 설계를 갖추고 있습니다. 이를 폴백(Fallback)이라고 부릅니다.

Redis가 죽었거나 연결이 지연될 때 무한정 기다리지 않고, 지체 없이 원본인 PostgreSQL(메인 DB)로 연결을 우회하여 서비스를 유지해야 합니다. 속도는 조금 느려지겠지만, 서비스가 멈추는 최악의 사태는 막을 수 있죠.

안전한 폴백 처리 패턴 (try-catch 활용)

async function getPopularPosts() {
  try {
    // 1. 빠른 Redis에서 데이터를 요청 (timeout을 짧게 설정)
    const cached = await redis.get('popular_posts', { timeout: 100 });
    if (cached) return JSON.parse(cached);
  } catch (error) {
    // 2. Redis 연결 에러나 타임아웃 발생 시, 콘솔에 경고만 남기고 진행
    console.warn("Redis가 응답하지 않습니다. 메인 DB로 우회합니다.", error.message);
  }
  
  // 3. Redis 장애와 무관하게 메인 DB에서 원본 데이터를 조회하여 반환
  // 사용자는 약간 느리게 응답을 받겠지만, 접속 장애를 겪지는 않음
  const posts = await db.query('SELECT * FROM posts ORDER BY view_count DESC LIMIT 10');
  return posts;
}

타임아웃(Timeout) 설정의 중요성

폴백을 구성할 때 핵심은 짧은 타임아웃입니다. Redis의 기본 응답 속도는 1ms 이하입니다. 만약 100ms 이상 지연된다면 이미 뭔가 문제가 발생한 것입니다. 타임아웃을 무한정으로 두면 Redis가 멈췄을 때 웹 서버도 함께 멈춰버리므로, 반드시 0.1~0.5초 이내의 짧은 타임아웃을 설정해야 합니다.

부드러운 기능 저하 (Graceful Degradation)

때로는 폴백을 위해 메인 DB로 요청을 돌리는 것 자체가 메인 DB를 터지게 만들 수도 있습니다. 평소에 Redis가 90%의 트래픽을 방어해주고 있었는데, 방어막이 사라지자마자 엄청난 트래픽이 메인 DB로 폭격처럼 쏟아지기 때문이죠. (이를 '캐시 스탬피드'라고 부르며 뒤에서 더 자세히 배웁니다.)

이럴 때는 DB로 우회하는 것조차 포기하고, 사용자에게 핵심 기능만 살려서 제공하는 부드러운 기능 저하(Graceful Degradation) 전략을 택해야 합니다.

장애 시 기능 저하 선택하기

상황선택이유
조회수 캐시 서버 다운조회수 표시를 임시로 숨김글을 읽는 핵심 기능은 살려야 하므로
실시간 랭킹 Redis 다운랭킹 영역을 빈칸으로 두거나 안내 문구 표시랭킹 생성 쿼리로 DB가 죽는 것을 방지
세션 관리 Redis 다운모든 사용자를 로그아웃 처리하고 재로그인 유도보안과 정합성을 지키기 위해

Redis를 도입할 때는 항상 "이 Redis 서버의 전원 코드를 지금 당장 뽑는다면 서비스가 어떻게 동작해야 하는가?"를 스스로에게 질문해야 합니다.

개념 퀴즈

퀴즈 답을 맞춰야 학습 완료가 됩니다.

시스템이 단일 장애점(SPOF)을 가지고 있지 않도록 잘 설계된 방식은 무엇인가요?

3줄 요약

  1. 1Redis 장애가 서비스 전체 마비로 이어지는 단일 장애점(SPOF) 구조를 경계해야 합니다.
  2. 2Redis 접근 시 에러나 타임아웃이 발생하면 신속하게 메인 DB로 우회하는 폴백(Fallback)을 구현하세요.
  3. 3메인 DB 부하가 걱정된다면 덜 중요한 부가 기능(예: 실시간 랭킹)은 일시적으로 숨기는 부드러운 기능 저하 전략이 안전합니다.