DB

7가지 핵심 질문으로 알아보는 개발자를 위한 Redis 활용법

Joonfluence 2025. 3. 14.

목차

 

서론

위 글은 최근 읽었던 개발자를 위한 Redis란 책을 요약 겸 복습하기 위해 정리한 글입니다. 

7가지 핵심 질문으로 알아보는 개발자를 위한 Redis 활용법

핵심요약

1. Redis란 무엇이고 어떤 원리로 동작할까?

  • Redis란 무엇이며, 왜 사용하는가?
  • Redis는 어떤 데이터 구조를 지원하는가?
  • Redis의 주요 사용 사례는 무엇인가?

2. Redis는 NoSQL인가? 그렇다면 어떤 데이터 구조를 제공하는가?

  • String, List, Set, Sorted Set, Hash 등의 데이터 타입별 특징과 사용 예시는?
  • Bitmaps, HyperLogLog, Streams 등 고급 데이터 타입은 언제 사용하면 좋은가?
  • TTL(Time to Live) 및 Expire 기능을 어떻게 활용할 수 있는가?

3. Redis를 캐시, 세션스토어, 메세지 브로커로 쓸 때 알아야 할 것들

  • 캐시(Cache)로 사용할 때의 Best Practice는?
  • 캐시 전략에는 어떤 것들이 있는가?
  • 세션 스토어로서의 레디스
  • 메시지 브로커로서의 레디스
  • Redis와 Kafka의 차이점 및 적절한 활용 방안은? (이거)

4. Redis 백업, 모니터링 및 운영 방법은 무엇이 있을까?

  • 레디스에서 데이터를 영구적으로 저장하는 방법은 무엇일까? 
  • 무엇을, 어떻게 모니터링 해야 할 까?

5. Redis의 성능 및 최적화 방법은 무엇인가? 

  • Redis의 메모리 관리는 어떻게 동작하는가? 
  • LRU(Least Recently Used)와 LFU(Least Frequently Used) 정책은 무엇인가?
  • Redis에서 메모리가 부족할 때 어떻게 동작하는가?
  • 파이프라이닝(Pipelining)이란 무엇이며, 언제 사용해야 하는가?
  • 대용량 데이터를 저장할 때 주의할 점은 무엇인가?

6. Redis는 어떻게 복제, 분산 및 확장 가능할까? 

  • Redis에서의 복제 구조와 복제 메커니즘은 어떠한가? 
  • Redis Sentinel이란 무엇이며, 장애 조치는 어떻게 이루어지는가?
  • Redis Cluster는 어떻게 작동하며, Sharding(샤딩)은 어떻게 이루어지는가?
  • 멀티 노드 환경에서 데이터 정합성을 유지하는 방법은?

7. Redis에서는 데이터 일관성과 트랜잭션을 어떻게 보장할까? 

  • Redis의 트랜잭션(MULTI/EXEC)은 어떻게 동작하는가?
  • Redis에서 원자적(Atomic) 연산을 보장하는 방법은?
  • 분산 환경에서 RedLock(분산 락)을 어떻게 구현하는가?
  • Redis는 데이터 영속성(Persistence)은 어떻게 보장되는가?

Redis란 무엇이고 어떤 원리로 동작할까?

Redis는 오픈소스 인메모리 데이터 구조 저장소로, 주로 NoSQL 데이터베이스, 캐시 또는 메시지 브로커로 사용된다. Redis는 데이터를 메모리(RAM)에 저장하여 매우 빠른 속도로 데이터에 접근할 수 있게 해준다. 주로 키-값(Key-Value) 형태로 데이터를 저장하지만, 여러 종류의 데이터 구조(String, List, Set, Hash, Sorted Set 등)도 지원한다. 

Redis는 왜 사용하는가?

Redis는 캐시, 세션 저장소, 실시간 데이터 처리, 메시지 큐, 분산락, 지도 데이터 구조 등을 지원하여, 매우 다양한 시스템 아키텍처에서 핵심적인 역할을 할 수 있다.

  1. 빠른 성능 : 디스크에 비해 빠른 데이터 접근 속도
    • Redis는 모든 데이터를 메모리(RAM)에서 관리하므로 디스크 기반 데이터베이스보다 빠른 응답속도를 제공한다. 이 덕분에 고속 캐싱 시스템 실시간 애플리케이션에서 주로 사용된다. 
    • Disk에 저장된 데이터를 조회하는데 오래 걸리는 이유는 Disk에 저장된 데이터는 바로 찾을 수 없으며, 디스크의 데이터를 페이지 단위로 메모리에 올린 뒤 메모리에서 데이터를 찾고, 없는 경우 다시 다른 페이지를 디스크에서 가져와 메모리에 올린 뒤 찾는 과정을 반복해야 하기 때문이다. 
  2. 단순성 : 이벤트 루프를 이용한 싱글스레드 동작 방식
    • 레디스는 메인 스레드 1개와 별도의 스레드 3개로 동작하지만 클라이언트의 커맨드를 처리하는 부분은 이벤트 루프를 이용한 싱글 스레드로 동작한다. 이로 인해 멀티 스레드 어플리케이션에서 요구되는 동기화나 잠금 메커니즘 없이도 안정적이고 빠르게 사용자의 요청을 처리할 수 있다. 
    • 다만 이로 인해 반환이 느린 커맨드 사용에 주의해야 한다. 다른 사용자는 해당 쿼리가 완료될 때까지 대기할 수밖에 없기 때문이다. 이러한 명령어만 피해도 장애 발생 가능성을 줄일 수 있다. 
    • 또한 Redis는 비동기 방식으로 작업을 처리하며, 여러 클라이언트가 요청을 동시에 보내더라도 I/O 멀티플렉싱 기법을 사용하여 단일 스레드로 모든 요청을 효율적으로 처리한다. 이를 통해 여러 요청을 동시에 처리하는 것처럼 보이게 할 수 있다. 
  3. 다양한 자료형 제공 
    • Redis는 Key-Value 형태의 단순한 키-값 저장소 외에도 여러 가지 고급 데이터 구조를 지원한다. 예를 들어, 리스트, , 정렬된 셋, 해시맵 등 다양한 구조를 활용할 수 있어 복잡한 데이터 모델링에도 유리하다. 
  4. 사용성
    • Redis는 기본적으로 매우 간단한 명령어들을 처리합니다. 키-값 구조에서 데이터를 처리하는 대부분의 작업은 한 번의 메모리 접근으로 끝납니다. 
    • Redis는 비교적 단순한 API를 제공하고, 다양한 프로그래밍 언어를 지원한다. 이로 인해 개발자가 Redis를 빠르게 배워서 실제 프로젝트에 적용할 수 있다. 
  5. 고가용성
    • Redis는 자체적으로 HA(High Availability) 기능을 제공한다. 복제를 통해 데이터를 여러 서버에 분산시킬 수 있으며, 센티널은 장애 상황을 탐지해 자동으로 페일오버를 시켜준다. 애플리케이션이 센티널을 이용해 레디스에 연결하는 구조에서는 마스터에 장애가 발생하더라도 레디스로의 엔드포인트를 변경할 필요 없이 페일오버가 완료돼 정상화된 마스터 노드를 사용할 수 있다. 
  6. 확장성
    • 클러스터 모드를 사용한다면 아래와 같이, 손쉬운 수평적 확장이 가능하다. 데이터는 레디스 클러스터 내에서 자동으로 샤딩된 후 저장되며, 여러 개의 복제본이 생성될 수 있다. 이 데이터의 분리는 데이터베이스 레이어에서 처리되며 애플리케이션에서는 대상 데이터가 어떤 샤드에 있는지 신경 쓰지 않아도 되므로, 레디스를 사용할 때와 동일하게 데이터를 가져오고 저장할 수 있다. 
    • 클러스터 구조에서 모든 레디스 인스턴스는 클러스터 버스라는 프로토콜을 이용해 서로 감시하고 있으며, 이를 이용해 클러스터의 마스터 노드에 문제가 발생하면 자동으로 페일오버를 시켜 고가용성을 유지할 수 있다. 

7가지 핵심 질문으로 알아보는 개발자를 위한 Redis 활용법 - Redis란 무엇이고 어떤 원리로 동작할까? - Redis는 왜 사용하는가?

 

Redis의 주요 사용사례는 무엇인가?

Redis는 여러 가지 활용 방식이 있으며, 가장 대표적인 사용 사례는 다음과 같다. 

  1. 캐시 시스템
    • Redis는 빠른 읽기 성능 덕분에 주로 캐시 시스템으로 사용된다. 데이터베이스나 외부 API 호출 결과를 Redis에 캐시하여, 반복적인 요청에 대한 응답 시간을 획기적으로 줄일 수 있다. 캐시 무효화 전략이나 TTL(유효 기간)을 사용해 유연하게 관리할 수 있다. 
    • (예시) 웹 애플리케이션에서 사용자 세션 데이터를 Redis에 저장하여 빠르게 접근한다. 
  2. 세션 저장소
    • Redis는 세션 관리에 매우 유용하다. 사용자 로그인 세션 정보를 Redis에 저장하면, 빠르게 세션을 관리하고, 서버가 장애를 일으켜도 세션이 유지된다. 
    • (예시) 전자상거래 사이트에서 장바구니 정보를 Redis에 저장하여 다른 서버로 이동해도 지속적인 세션 상태를 유지된다. 혹은 유저 로그인 세션 정보를 저장해두기도 한다. 
  3. 메시지 큐 및 Pub/Sub 시스템
    • Redis는 메시지 큐로 사용할 수 있으며, Pub/Sub 모델을 통해 실시간 메시징을 처리할 수 있다. 여러 클라이언트가 서로 메시지를 발행하고 구독할 수 있어 실시간 알림 시스템이나 이벤트 기반 시스템에 유용하다. 
    • (예시) 실시간 알림, 채팅 애플리케이션에서 메시지 발송 
  4. 실시간 분석
    • Redis는 집합 연산정렬된 집합을 통해 실시간 데이터 분석랭킹 시스템을 구축하는 데 사용된다. 예를 들어, 게임의 실시간 리더보드나 사용자 활동 데이터를 Redis로 처리할 수 있다. 
    • (예시) 스포츠 웹사이트에서 실시간 점수판 업데이트 
  5. 순위 및 카운팅
    • Redis는 정렬된 집합(Sorted Set)을 활용하여 순위 기반 시스템을 구축할 수 있다. 주로 랭킹 시스템(예: 게임의 최고 점수, 주식 순위 등)에 사용된다. 
    • (예시) 소셜 미디어 플랫폼에서 인기 게시물 순위 관리 
  6. 분산 락(Distributed Lock)
    • Redis는 SpinLock, Pub-Sub 기반 락, RedLock을 사용하여 분산 시스템에서의 락 관리를 지원한다. 여러 인스턴스에서 동시에 자원에 접근하지 않도록 하는 데 사용된다.
    • (예시) 주문 처리 시스템에서 동시에 여러 서버가 주문을 처리하지 않도록 하기 위한 락

각각의 예시 코드를 Kotlin 기준으로 살펴보자. 먼저 SpinLock

while (redis.setnx("myLock", "1") == 0L) {
    Thread.sleep(100)  // 짧은 시간 대기 후 재시도
}
try {
    // Critical Section
} finally {
    redis.del("myLock")
}

Pub-Sub

val lockKey = "resource_lock"
val pubSubChannel = "lock_channel"

// 락 획득 시도
if (redis.setnx(lockKey, "locked") == 1L) {
    try {
        // Critical Section
    } finally {
        redis.del(lockKey)
        redis.publish(pubSubChannel, "released")
    }
} else {
    redis.subscribe(pubSubChannel) { message ->
        if (message == "released") {
            // 다시 락 시도 가능
        }
    }
}

RedLock

    // 3개의 개별 락 객체 생성 (각 노드에서 같은 이름의 락을 가져옴)
    val lock1: RLock = redisson.getLock("distributed_lock")
    val lock2: RLock = redisson.getLock("distributed_lock")
    val lock3: RLock = redisson.getLock("distributed_lock")

    // RedLock 생성 (다중 Redis 노드 기반)
    val redLock = redisson.getRedLock(lock1, lock2, lock3)

    try {
        println("🔒 Trying to acquire lock...")

        // 락 획득 시도 (최대 10초 대기, 30초 후 자동 해제)
        if (redLock.tryLock(10, 30, java.util.concurrent.TimeUnit.SECONDS)) {
            println("✅ Lock acquired! Performing critical operation...")

            // 크리티컬 섹션
            Thread.sleep(5000) // 예제용 작업

            println("✅ Operation completed.")
        } else {
            println("❌ Failed to acquire lock.")
        }
    } catch (e: Exception) {
        e.printStackTrace()
    } finally {
        redLock.unlock() // 락 해제
        println("🔓 Lock released.")
        redisson.shutdown()
    }

각 락의 장점과 단점, 추천 사용 사례는 다음과 같다. 

7가지 핵심 질문으로 알아보는 개발자를 위한 Redis 활용법 - Redis란 무엇이고 어떤 원리로 동작할까? - Redis의 주요 사용사례는 무엇인가?

Redis는 NoSQL인가? 그렇다면 어떤 데이터 구조를 제공하는가?

Redis는 어떤 데이터 구조를 지원할까?

Redis는 다양한 데이터 구조를 지원한다. 이들 데이터 구조는 여러 가지 상황에 맞는 효율적인 처리를 가능하게 한다.

  1. String
    • 가장 기본적인 데이터 구조로, 단일 값을 저장한다. 예를 들어, 키-값 형태로 문자열, 숫자 등을 저장할 수 있다.
    • (예시) SET name "John", GET name
  2. List
    • 순차적인 리스트 형태로 데이터를 저장한다. 양방향 큐(queue)로 사용되며, 데이터를 앞이나 뒤에서 삽입하거나 삭제할 수 있다.
    • (예시) LPUSH list 1 2 3, LRANGE list 0 -1 
  3. Set
    • 중복 없는 집합을 저장하는 데이터 구조로, 요소가 유일함을 보장한다. 교집합, 합집합, 차집합 등의 집합 연산을 효율적으로 처리할 수 있다. 
    • (예시) SADD set 1 2 3, SMEMBERS set 
  4. Sorted Set
    • 각 요소가 점수(score)와 함께 저장되는 정렬된 집합이다. 주로 랭킹 시스템이나 우선순위 큐로 활용됩니다. 점수를 기준으로 자동으로 정렬된다. 
    • (예시) ZADD zset 1 "apple" 2 "banana", ZRANGE zset 0 -1 WITHSCORES 
  5. Hash
    • 필드와 값의 쌍으로 이루어진 맵 구조이다. 키 하나에 여러 필드를 저장할 수 있으며, 영속적인 객체 저장에 유용하다. 
    • (예시) HSET user:1 name "John" age 30, HGETALL user:1
  6. Bitmaps
    • 비트 단위로 데이터를 처리하는 구조로, 주로 비트 플래그를 사용하여 데이터를 관리할 때 사용된다. 대규모 데이터에 대한 효율적인 저장과 처리가 가능하다. 
    • 주로 사용자 활동(로그인, 출석 등) 기록을 관리하거나 비트 연산(AND, OR 등)을 활용한 빠른 집계가 필요할 때 사용한다. 
  7. HyperLogLog
    • 근사값 계산을 위한 데이터 구조로, 대규모 데이터에서 고유한 값의 개수를 효율적으로 추정할 수 있다. 메모리 사용을 최소화하면서 대량의 데이터를 처리할 수 있다. 
    • 주로 실시간 이벤트 로그에서 중복되지 않은 이벤트 개수 계산하거나 광고 시스템에서 고유 사용자 노출(Reach) 추정 할 때 사용한다. 
    • (예시) PFADD hll "a" "b" "c", PFCOUNT hll
  8. Stream
    • 이벤트 스트리밍을 처리할 수 있는 데이터 구조로, 데이터의 흐름을 관리하고, 소비자와 생산자 간에 메시지를 처리할 수 있다. Kafka와 유사한 기능을 제공하며, 로그 처리 및 이벤트 기반 시스템에 유용하다. 
    • 주로 실시간 로그/이벤트 저장 (예: 주문 이벤트, 결제 이벤트, 채팅 메시지 저장) 할 때 사용한다. 
    • (예시) XADD mystream * name "John" age 30

Kotlin에서의 데이터 타입별 사용 예시는?

  • Set
// 값 저장
redis.set("user:123:session", "active")
redis.expire("user:123:session", 3600)  // 1시간 후 만료

// 값 가져오기
val session = redis.get("user:123:session")
println("Session status: $session") // active

// 카운터 증가 (방문자 수)
redis.incr("page:views")
println("Page views: ${redis.get("page:views")}")
  • List
// 채팅 메시지 추가 (FIFO 방식)
redis.lpush("chat:room1", "Hello!", "How are you?", "Good morning!")

// 최근 3개의 메시지 조회
val messages = redis.lrange("chat:room1", 0, 2)
println("Recent messages: $messages") // [Good morning!, How are you?, Hello!]

// 메시지 삭제
redis.del("chat:room1")
  • Set 
// 유저 팔로워 추가
redis.sadd("user:1001:followers", "user2001", "user2002", "user2003")

// 특정 유저가 팔로우 중인지 확인
val isFollowing = redis.sismember("user:1001:followers", "user2001")
println("Is following: $isFollowing") // true

// 모든 팔로워 가져오기
val followers = redis.smembers("user:1001:followers")
println("Followers: $followers")

// 특정 유저 제거
redis.srem("user:1001:followers", "user2002")
  • Sorted Set
// 리더보드 추가
redis.zadd("game:leaderboard", 1500.0, "player1")
redis.zadd("game:leaderboard", 2000.0, "player2")
redis.zadd("game:leaderboard", 1800.0, "player3")

// 상위 3명 조회 (점수 높은 순)
val topPlayers = redis.zrevrangeWithScores("game:leaderboard", 0, 2)
println("Leaderboard:")
topPlayers.forEach { println(it) }

// 특정 유저 점수 확인
val score = redis.zscore("game:leaderboard", "player1")
println("Player1 score: $score") // 1500.0
  • Hash
// 사용자 정보 저장
redis.hset("user:101", mapOf(
    "name" to "Alice",
    "age" to "30",
    "email" to "alice@example.com"
))

// 특정 필드 가져오기
val name = redis.hget("user:101", "name")
println("User name: $name") // Alice

// 모든 필드 가져오기
val userInfo = redis.hgetAll("user:101")
println("User Info: $userInfo")

// 특정 필드 업데이트
redis.hset("user:101", "age", "31")
  • Bitmaps
redis.setbit("user:1001:login", 5, true) // 5일째 로그인 체크
val isLoggedIn = redis.getbit("user:1001:login", 5) // 5일째 로그인 여부 확인
  • HyperLogLog
redis.pfadd("unique_visitors", "user_123", "user_456", "user_789") // 방문 기록 추가
val count = redis.pfcount("unique_visitors") // 고유 방문자 수 추정
  • Streams
// 메시지 추가
redis.xadd("order_stream", mapOf("order_id" to "12345", "status" to "pending"))

// 메시지 소비 (가장 오래된 1개 읽기)
val messages = redis.xrange("order_stream", "-", "+", 1)

TTL(Time to Live) 및 Expire 기능을 어떻게 활용할 수 있는가?

Redis의 EXPIRE 기능은 데이터의 자동 만료를 지원하여, 메모리 효율성을 높이고 불필요한 데이터 관리를 줄일 수 있다. 또한 TTL(Time to Live) 명령어로 남은 만료 시간을 확인할 수 있다. 

// 세션 키 설정 + TTL 적용
redis.set("session:user123", "session_data")
redis.expire("session:user123", 60) // 60초 후 자동 삭제

// TTL 확인
val ttl = redis.ttl("session:user123")
println("Session TTL: $ttl seconds") // 남은 시간 출력

// 60초 후 자동 삭제됨

활용 사례 

  • 세션 관리 : 로그인 세션, API 토큰 등은 일정 시간이 지나면 만료되어야 한다. 
SET user:token:abc123 "user_data"
EXPIRE user:token:abc123 3600  # 1시간 후 만료
  • 캐싱 시스템 : 특정 API 응답을 Redis에 캐싱하고 일정 시간 후 삭제하여 스케일링 최적화한다.
SET cache:product:123 "product_data"
EXPIRE cache:product:123 300  # 5분 후 자동 삭제
  • 속도 제한 (Rate Limiting) : 특정 사용자 요청을 제한하는 Rate Limiting 구현 가능하다. 
INCR user:rate:192.168.1.1
EXPIRE user:rate:192.168.1.1 60  # 1분간 유지
  • 작업 큐 (Delayed Queue) : 특정 시간 후 실행해야 하는 작업을 처리할 때 사용한다. 
ZADD queue 1700000000 "job_123"
EXPIRE queue 86400  # 24시간 후 자동 삭제
 

Redis를 캐시, 세션스토어, 메세지 브로커로 쓸 때 알아야 할 것들

Redis Cache Best Practice

Redis를 Cache로 사용할 때, 효과적인 경우는 어떤 경우일까? 아래와 같은 경우라면 Cache를 적극 고려해보자. 

  • 원본 데이터 저장소에서 원하는 데이터를 찾기 위해 검색하는 시간이 오래 걸리거나 매번 계산을 통해 데이터를 가져와야 하는 경우
  • 캐시에서 데이터를 가져오는 것이 원본 데이터 저장소 데이터를 요청하는 것보다 빨라야 하는 경우
  • 캐시에 저장된 데이터가 잘 변하지 않는 경우
  • 캐시에 저장된 데이터가 자주 검색되는 경우

Cache 도입을 결정했다면, 아래와 같은 5가지를 고려해야 한다. 

  1. 캐시 키 (Key) 설계
    • cache:{리소스}:{식별자} 형태 사용 (cache:user:123:profile)
    • Namespace 활용 (product:details:123)
    • TTL(만료시간) 설정하여 데이터 최신 상태 유지
  2. 캐시 만료 (TTL) 설정 : 데이터의 성격에 따라 다르다. 
    • 동적 데이터: 짧은 TTL (1~10분) 설정 (setex key 600 value)
    • 정적 데이터: 길게 유지 (set key value)
    • 특정 시간에 만료되도록 설정 (pexpire key 5000)
  3. 캐시 무효화 (Cache Invalidation) 전략 
    • TTL 기반 자동 삭제 (주기적 갱신) 
    • Write-Through (DB 업데이트 시 캐시 동기화) 
    • Read-Through (캐시 미스 발생 시 DB 조회 후 저장) 
    • Look Aside (DB 먼저 갱신 후 캐시 삭제) 
  4. 캐시 일관성 유지
    • DB 업데이트 후 캐시 삭제(Cache Aside) 
    • Pub/Sub을 활용한 캐시 동기화 
  5. 캐시 용량 관리 (maxmemory-policy 설정) : Redis 메모리 관리 방법과도 연관됨
    • Least Recently Used (LRU): 자주 사용하지 않는 항목 삭제 
      • volatile-lru : 만료 시간이 설정돼 있는 키에 한해서 LRU 방식으로 키를 삭제한다. 이미 만료 시간이 설정돼 있는 키는 언젠가 삭제될 키라는 것을 의미하기 때문에, 이런 키 중 가장 오래 사용되지 않은 키를 삭제하는 방식이다. 
      • allkeys-lfu : (만료 시간이 설정되어 있지 않아도) 모든 키에 대해 LRU 알고리즘을 이용해 데이터를 삭제하기 때문에 메모리가 꽉 찼을 때 장애가 발생하는 상황을 방지할 수 있다. 
    • Least Frequently Used (LFU): 자주 사용되지 않은 데이터부터 삭제하는 정책, 키가 오랫동안 사용되지 않았더라도 과거에 자주 액세스했던 키라면 나중에도 자주 사용될 수 있다는 가정하에 우선순위가 높아진다. 

이 중에서도 캐시 무효화 (Cache Invalidation) 전략에 대해서 조금 더 자세하게 알아보자.  

Redis 캐싱 전략

캐싱 전략에서 고려해야 할 중요한 요소들은 읽기 전략, 쓰기 전략, 그리고 캐시 무효화 전략이다. 이들은 캐시의 효율성, 데이터 일관성, 시스템 성능 등을 최적화하는 데 중요한 역할을 한다.

읽기 전략 : Look Aside (Cache Aside, Lazy Loading)

  1. 원리 
    • 애플리케이션이 먼저 캐시에서 데이터를 조회
    • 캐시 미스(Cache Miss) 발생 시, DB에서 데이터를 가져와 캐시에 저장
    • 데이터 갱신 시, DB를 먼저 업데이트 후 캐시를 삭제
  2. 장점
    • 자주 조회되는 데이터만 캐싱되어 메모리 절약
    • DB 변경 시 즉시 캐시 삭제 가능
  3. 단점
    • 캐시 미스 시 DB 조회로 인해 성능 저하 발생 가능
    • 데이터가 변경된 후 다음 조회 시까지 캐시가 업데이트되지 않음
 

쓰기 전략 : Write-Through

  1. 원리
    • 애플리케이션이 데이터를 업데이트할 때, 캐시와 DB를 동시에 갱신
    • 데이터 조회 시 항상 캐시에서 가져오므로, 캐시 미스가 발생하지 않음
  2. 장점
    • 항상 최신 데이터 유지
    • 캐시 미스가 발생하지 않아 일관된 성능 유지
  3. 단점
    • DB와 캐시를 동시에 업데이트해야 하므로 쓰기 연산의 부하 증가
    • 불필요한 데이터까지 캐시에 저장될 가능성 있음
 

쓰기 전략 : Cache Invalidation 

  1. 원리
    • 데이터베이스에 값을 업데이트할 때마다 캐시에서는 데이터를 삭제하는 전략
  2. 장점
    • Write-Through 방식에 비해 횔씬 적은 리소스 사용
  3. 단점
    • 캐시 삭제 후 재조회가 필요할 때 매번 DB에서 값을 읽어와야 하므로 DB 부하 발생 
    • 캐시 삭제 후 동시에 많은 요청이 DB에 몰리면 DB 부하가 급증하고 성능 저하를 초래할 수 있음 
    • 동시 업데이트가 발생하는 환경에서는 캐시와 데이터베이스의 일관성 문제가 발생할 수 있음 

쓰기 전략 : Write-Behind (Write-Back)

  1. 원리
    • 애플리케이션이 먼저 캐시를 업데이트
    • 특정 주기마다 배치 프로세스를 통해 DB를 업데이트
  2. 장점
    • 쓰기 성능 최적화 가능 (캐시에만 쓰고, DB 반영은 배치 처리)
    • 짧은 주기로 변경되는 데이터(예: 실시간 로그, 카운터)에 유리
  3. 단점
    • 장애 발생 시 데이터 유실 가능성 (DB에 반영되지 않은 상태에서 캐시 만료되면 데이터 손실)
    • 데이터 일관성이 중요할 경우 부적합
 

캐시 무효화 전략 : TTL(Time-to-Live)

  1. 원리
    • Redis의 EXPIRE(자동 만료) 기능을 사용하여 특정 시간이 지나면 캐시 자동 삭제
    • 자주 변경되는 데이터는 짧은 TTL, 변경이 적은 데이터는 긴 TTL 설정
  2. 장점
    • 자동으로 캐시 정리 가능 → 오래된 데이터가 남아 있지 않음
    • 캐시 일관성을 유지하기 쉬움
  3. 단점
    • TTL이 만료되기 전까지는 캐시가 최신 상태인지 알 수 없음
    • 특정 시점에 많은 캐시가 만료되면 부하 증가 가능
 

캐시 무효화 전략 : Pub/Sub을 활용한 캐시 무효화

  1. 원리
    • DB가 변경될 때 Redis Pub/Sub을 활용하여 관련 캐시를 삭제
    • 다수의 서버가 운영되는 환경에서 일관된 캐시 무효화 가능
  2. 장점
    • 멀티 노드 환경에서 동기화가 가능
    • DB 변경 이벤트를 활용하여 빠르게 캐시 업데이트 가능
  3. 단점
    • Redis의 Pub/Sub은 메시지 유실 가능성이 있어 구독 실패 시 캐시 동기화 이슈 발생 가능
    • 추가적인 Redis Channel 관리 필요
 

캐시 무효화 전략 : LRU(Least Recently Used) 기반 캐시 자동 삭제

  1. 원리
    • 메모리 부족 시, 자주 사용되지 않는 키를 자동 삭제
    • Redis 설정에서 maxmemory-policy를 allkeys-lru 또는 volatile-lru로 설정
  2. 장점
    • 캐시를 수동으로 관리하지 않아도 됨
    • 메모리 사용량을 초과하지 않도록 자동 관리 가능
  3. 단점
    • 중요한 데이터까지 삭제될 가능성 있음
    • LRU 기반으로 삭제되므로 특정 패턴의 트래픽에서는 예측하기 어려울 수 있음

세션 스토어로서의 레디스 

세션이란

세션은 서버와 클라이언트 간의 연결 상태를 유지하는 데 사용되는 상태 정보이다. HTTP는 무상태(stateless) 프로토콜이기 때문에 각 요청 간의 관계를 기억하지 않는다. 따라서, 사용자와의 지속적인 상호작용을 위해 서버는 사용자의 상태를 추적하고 관리할 필요가 있다. 이를 위해 세션 ID를 발급하고 해당 세션에 사용자 관련 데이터를 저장하여 여러 요청을 통해 상태를 유지한다. 이 때 세션의 데이터를 끊임없이 읽고 쓰게 되므로 빠른 응답 속도는 필수적이다. Redis는 키-값 형식으로 사용이 간단하며 string, set, hash 등의 자료구조를 제공하기 때문에 사용자 데이터를 저장하기에 용이하다. 

세션 스토어가 필요한 이유

세션 스토어는 세션 데이터를 저장하고 관리하는 저장소다. 기본적으로 세션 데이터는 서버 메모리에 저장된다. 그러나 서버가 여러 대로 늘어나는 상황에서 sticky session(특정 웹 서버에 유저가 몰려 트래픽이 몰리더라도 해당 유저의 세션이 저장된 서버와 통신해야 하므로, 다른 서버로 트래픽을 분산시킬 수 없음)로 인한 낮은 확장성과 낮은 가용성 문제, 혹은 all-to-all 방식(여러 웹 서버에 유저의 세션 데이터를 복사하여 저장하는 방식)을 적용함으로 인한 비효율적인 데이터 저장 문제가 존재한다. 이처럼 빠른 응답 속도 외에도 확장성고가용성 그리고 효율성을 위해 세션 데이터를 Redis와 같은 외부 저장소에 저장하는 경우가 많다. 

  • 상태 저장: 서버는 클라이언트와의 상태를 유지해야 하기 때문에, 세션 데이터를 저장할 장소가 필요하다.
  • 확장성: 여러 대의 서버가 존재하는 경우, 세션 데이터를 각 서버에 분산 저장할 필요가 있다. 이를 위해 중앙화된 저장소가 필요하다.
  • 세션 관리: 세션의 만료 처리, 세션 데이터 갱신, 세션 데이터 삭제 등을 효율적으로 처리할 수 있어야 한다.
  • 장애 복구: 서버가 재시작되거나 장애가 발생한 경우, 세션 데이터를 영속적으로 저장하여 시스템의 안정성을 높일 수 있다.

운영 관점에서 캐시(Cache)와 세션(Session)의 차이 

운영 관점에서, 캐시(Cache)는 주로 Look-Aside 전략을 택하기 때문에, DB의 서브셋으로 동작한다. 즉, 데이터베이스에서 자주 조회되는 부분을 빠르게 접근하기 위해 캐시에서 저장하고 관리하며, 캐시미스가 발생되더라도 DB에는 값이 존재하기 때문에 장애로 이어지진 않는다. 반면, 세션(Session)은 사용자의 상태 정보를 저장하는데 사용되며, 데이터베이스와는 독립적으로 사용자별로 관리됩니다. 세션은 데이터베이스의 서브셋이 아닌, 사용자의 상태 추적을 위한 유일한 저장소로 사용되므로 캐시로 사용할 때보다 더 신중한 운영이 필요하다. 

메시지 브로커로서의 레디스

현대 서비스 아키텍처는 여러 모듈이 서로 느슨하고 적절하게 연결시킨 구조를 선호하고 있다. 이런 형태의 구조는 다양한 장점을 갖고 있지만 모듈 간 서로 탄탄한 상호 작용이 필요하기 때문에 효율적인 메세징 솔루션, 즉 메세지 브로커를 필요로 한다. 모듈 간 통신에서는 되도록 비동기 통신을 사용하는 것을 권장한다. 그 이유는 동기식 통신 시, 하나의 모듈의 장애가 다른 모듈의 장애로 이어지기 쉽기 때문이다. 메세지 브로커를 사용하면 하나의 모듈에서 장애가 발생한 상황에서 당장 메시지를 처리하지 못하더라도 보낸 메세지를 어딘가에 쌓아 둔 뒤 나중에 처리할 수 있는 채널을 제공할 수 있다.

메시지 브로커는 크게 메시징 큐이벤트 스트림, 두 가지 방식이 있다. Redis 내에서도 구현 방식에 따라 성격이 달라져서 메시징 큐 방식은 List로, 이벤트 스트림 방식은 Pub/Sub과 Streams으로 구현 할 수 있다. 정리하면 아래와 같다. 

7가지 핵심 질문으로 알아보는 개발자를 위한 Redis 활용법 - Redis를 캐시, 세션스토어, 메세지 브로커로 쓸 때 알아야 할 것들 - 메시지 브로커로서의 레디스

각각의 활용 예시를 알아보자. 

7가지 핵심 질문으로 알아보는 개발자를 위한 Redis 활용법 - Redis를 캐시, 세션스토어, 메세지 브로커로 쓸 때 알아야 할 것들 - 메시지 브로커로서의 레디스

사실 Redis Streams 자료구조의 특성을 보면 알 수 있듯이, Kafka를 메세지 브로커를 쓸 때와 기본적인 역할에는 완전히 동일하다. 단, 설계 철학, 성능, 강점과 한계 등에서 차이를 보인다. 

Redis와 Kafka의 차이점 및 적절한 활용 방안은?

차이점을 알아보기에 앞서, 공통점에 대해서 먼저 알아보자. 메세지 브로커로서 Redis Streams와 Kafka는 아래와 같은 공통점이 있다. 비동기 메시징 처리, 메세지 전달 방식, 확장성, 소비자 그룹, 재처리, 지속성, 메세지 순서 보장 등 메세지 브로커로서의 본질적이고 필수적인 핵심 기능들은 모두 제공한다. 

7가지 핵심 질문으로 알아보는 개발자를 위한 Redis 활용법 - Redis를 캐시, 세션스토어, 메세지 브로커로 쓸 때 알아야 할 것들 - Redis와 Kafka의 차이점 및 적절한 활용 방안은?

그렇지만 메세지 브로커로서 Kafka와 Redis Streams는 설계 목적과 강점, 한계가 명확히 다른 시스템이다. 

7가지 핵심 질문으로 알아보는 개발자를 위한 Redis 활용법 - Redis를 캐시, 세션스토어, 메세지 브로커로 쓸 때 알아야 할 것들 - Redis와 Kafka의 차이점 및 적절한 활용 방안은?

각각의 활용 예시도 함께 살펴보자. 

7가지 핵심 질문으로 알아보는 개발자를 위한 Redis 활용법 - Redis를 캐시, 세션스토어, 메세지 브로커로 쓸 때 알아야 할 것들 - Redis와 Kafka의 차이점 및 적절한 활용 방안은?

요약하면 Kafka는 대규모, 고신뢰, 장기 이벤트 저장/재처리가 필요한 경우 적합하고 Redis Streams는 저지연, 가벼운 실시간 처리, MSA 경량 메시징에 적합하다. 

 

 

 

Redis 백업, 보안 및 운영 방법은 무엇이 있을까?

1) 레디스에서 데이터를 영구적으로 저장하는 방법은 무엇일까?

Redis에서 데이터를 영구적으로 저장(Persistence)하는 방법에는 두 가지 메커니즘이 있다. RDB(Redis Database)와 AOF(Append Only File)이다. RDB는 일정 시점에 메모리에 저장된 데이터 전체를 저장하는 것이고 AOF는 레디스 인스턴스가 처리한 모든 쓰기 작업을 차례대로 기록하고 복원 시에는 파일을 다시 읽어가며 데이터 세트 재구성한다. 

RDB (Redis Database Snapshotting)

  1. 개념
    • 일정 시점에 메모리에 저장된 데이터 전체를 디스크 파일로 저장하는 방식이다. 
    • 실제로 Redis는 주기적으로 메모리의 모든 데이터를 RDB 파일로 저장해서 복구용으로 사용한다. 
  2. 동작 방식
    • 설정된 조건(예: save 900 1 → 900초 동안 1번 이상 변경 시)에 따라 자동으로 RDB 파일 생성한다. 
    • 또는 BGSAVE 명령으로 수동 스냅샷 수행한다. 
  3. 장점
    • 디스크에 주기적으로 저장 → 빠른 재시작 가능.
    • 성능 영향 적음 (백그라운드 저장, 메인 스레드 영향 적음).
    • 복구 속도 빠름 (전체 메모리 상태 그대로 복구).
  4. 단점
    • 최근 데이터 손실 가능 (마지막 스냅샷 이후 변경된 데이터 반영 안 됨).
    • 고빈도 쓰기 환경에 부적합 (짧은 주기로 저장하면 I/O 부담).
  5. 활용 예시
    • 데이터 유실 허용 가능한 캐시, 비정기 저장. 
    • 빠른 재시작이 중요한 서비스. 

AOF (Append Only File)

  1. 개념
    • 모든 쓰기 명령로그로 기록해서 복구 시 순차적으로 재실행한다. 
    • "데이터가 변경된 모든 과정"이 남는 방식이다. 
  2. 동작 방식
    • Redis가 처리한 모든 write 명령을 AOF 파일에 순차적으로 기록.
    • 재시작 시 AOF 파일을 재생해서 상태 복구.
  3. 장점
    • 데이터 유실 최소화 (실시간에 가까운 기록, 설정에 따라 매 요청 fsync 가능).
    • 복구 시 정확한 상태 재현 가능.
  4. 단점
    • 파일 크기 커짐 (주기적 압축 필요, BGREWRITEAOF로 압축).
    • 성능 영향 가능 (매 쓰기 기록 시 I/O 부담).
  5. 활용 예시
    • 데이터 유실 불가 서비스 (결제, 트랜잭션 로그). 
    • 강력한 복구 신뢰성 필요. 

ElasticCache를 활용하는 경우는 어떨까?

만약 운영 환경에서 ElasticCache를 활용해서 Redis를 사용 중이라면 내부적으로 어떤 방식의 백업을 지원할까? 결론부터 말하면, AWS Elasticache for Redis는 내부적으로 RDB 방식의 자동 백업(Snapshot)을 지원하지만, AOF(Append Only File)는 지원하지 않는다. 따라서 완전한 실시간 데이터 복구나 세밀한 장애 복구는 제한적이지만, 스냅샷 기반의 시점 복원(Point-in-Time Recovery)은 가능하다. Elasticache for Redis는 RDB 스냅샷과 복제로 장애 복구를 지원하지만, AOF 미지원으로 완전 무손실 복구는 불가능하며, 중요한 데이터는 반드시 외부 이중화 필요하다. 

2) 무엇을, 어떻게 모니터링 해야 할 까? 

Redis에서 모니터링이 필요한 지표는 무엇인지 알아보고 AWS 에서 제공하는 기본 모니터링 기능과 별도 모니터링 시스템 구축이 필요한 경우가 무엇인지 알아보자. 

Redis 모니터링할 때, 가장 중요한 지표는 SlowLog, CPU, Memory, Network, Connection이다. 

  1. Slow Log
    • 중요한 이유 : Redis는 싱글 스레드로 동작하므로, 하나의 요청이 오래 걸리면 다른 요청들도 지연된다. 따라서 Slow Log는 Redis 성능 저하의 원인을 파악하는 핵심 지표이다.
    • 예시 : 대표적으로 KEYS, SORT, LRANGE 같은 O(n) 이상의 연산이 포함된 명령어 사용하는 경우가 대표적이다.
    • 해결 방법 : SLOWLOG GET 10 명령으로 최근 10개의 Slow Log 조회하고 오래 걸리는 쿼리를 분석한다. 그 외에도 아래와 같은 방법들이 존재한다. 
      • SCAN 사용 권장 (KEYS 대신 SCAN으로 점진적 조회)
      • ZSET, HASH 사용 (LRANGE 대신 ZSET, HGETALL 대신 HSCAN)
      • Lua Script 최적화 (EVAL을 반복적으로 실행하는 구조 개선)
  2. CPU : 원인과 해결방법이 Slow Log와 비슷하다. 
    • 원인 
      • 느린 명령어 실행: SORT, KEYS, LRANGE 같은 연산이 크면 CPU가 급증
      • Lua 스크립트 연산 부담: 복잡한 EVAL 스크립트 실행 시 CPU 상승
      • Pub/Sub 메시지 과부하: 구독자 수가 많을 때 CPU 소모 증가
      • I/O 블로킹 증가: 네트워크 병목으로 인해 CPU가 대기 상태로 유지됨
    • 해결방법 
      • CPUUtilization 확인 (CloudWatch 또는 INFO CPU)
      • 느린 명령어 최적화 (SORT 대신 ZSET, LRANGE 대신 ZSET 활용)
      • I/O 작업(클라이언트 요청 처리)에 한해, 멀티스레드 활성화 (io-threads 설정, Redis 6.0+)
      • 클라이언트에서 불필요한 계산 최소화 (HGETALL 대신 필요한 필드만 조회)
  3. Memory 
    • 원인
      • 만료되지 않는 데이터 증가 → TTL 없이 키가 계속 남아 있다
      • 대량의 데이터 저장 → LIST, SET, ZSET에 수백만 개의 키 저장
      • 객체 직렬화 문제 → JSON, Protobuf 직렬화 시 불필요한 오버헤드 발생
    • 해결방법
      • UsedMemory, FreeableMemory 확인 (INFO MEMORY)
      • maxmemory-policy 설정 (volatile-lru, allkeys-lru 등)
      • TTL 없는 키 점검 (MEMORY USAGE <key>, TTL <key>)
      • 데이터 압축 (STRING 대신 HASH 사용, HSET으로 필드 저장)
  4. Network 
    • 원인
      • 대량의 작은 요청 (MGET 대신 여러 GET 사용)
      • 불필요한 대량 데이터 조회 (HGETALL, SMEMBERS 등)
      • 클라이언트와 Redis 간 불필요한 왕복 요청
    • 해결방법
      • Pipelining 사용: 여러 요청을 한 번에 보내 응답 횟수 줄이기
      • MGET/MSET 사용: 여러 키를 한 번에 조회 (GET 여러 개 대신 MGET)
      • 압축 사용: ZSTD, LZ4 압축 적용
      • 클라이언트 캐싱 적용: Redis에서 불필요한 반복 조회 방지
  5. Connection
    • 원인
      • 과도한 연결 증가 방지: CurrConnections 증가 시 성능 저하 가능
      • 불필요한 연결 탐지: NewConnections가 급증하면 서비스 오작동 가능성 존재
      • 커넥션 풀링 필요성 확인: 클라이언트가 연결을 효율적으로 재사용하고 있는지 점검
    • 해결방안
      • CurrConnections, NewConnections 모니터링
      • 필요 시 maxclients 설정 (redis.conf에서 조정 가능)
      • 커넥션 풀링 (Jedis, Lettuce 사용) 적용하여 불필요한 연결 방지
    • MaxClient의 역할
      • Redis는 maxclients 제한을 두어 과도한 연결을 방지
      • 기본값은 10,000이며, 초과 시 ERR max number of clients reached 발생

완전 관리형 서비스인 AWS ElasticCache for Redis를 사용할 경우, 기본적으로 CloudWatch를 통해, CPUUtilization, FreeableMemory, CacheHits/Misses 다양한 성능 지표 및 경보(Alarms) 설정이 가능하다.

7가지 핵심 질문으로 알아보는 개발자를 위한 Redis 활용법 - Redis 백업, 보안 및 운영 방법은 무엇이 있을까? - 2) 무엇을, 어떻게 모니터링 해야 할 까? 
CloudWatch에서 조회 가능한 주요 ElasticCache for Redis 지표

그렇지만 AWS CloudWatch 알람은 기본적인 임계치 모니터링만 가능하며, 상세한 모니터링을 하기 위해선 별도의 모니터링 시스템 구축이 필요하다. 이는 Prometheus와 Grafana를 활용하여 Redis의 상태를 시각적으로 모니터링할 수 있다.

7가지 핵심 질문으로 알아보는 개발자를 위한 Redis 활용법 - Redis 백업, 보안 및 운영 방법은 무엇이 있을까? - 2) 무엇을, 어떻게 모니터링 해야 할 까? 

추가로 살펴보면 좋은 지표들은 아래와 같다. 

  • Keyspace 지표
    • redis_db_keys → DB별 저장된 키 개수 (Sharding 필요성 확인)
    • redis_db_expires → TTL 설정된 키 개수 (메모리 증가 가능성 점검)
    • redis_db_avg_ttl → 평균 TTL 시간 (데이터 만료 정책 분석)
  • 캐시 효율성 지표
    • redis_keyspace_hits → 캐시 적중 수 (캐시 활용도 확인)
    • redis_keyspace_misses → 캐시 미스 수 (캐시 전략 개선 필요)
    • redis_evicted_keys → 메모리 부족으로 삭제된 키 개수 (메모리 관리 필요)
  • 명령어 사용 분석
    • redis_commands_total → 실행된 명령어 총 개수 (특정 명령어 과사용 감지)
    • redis_commands_duration_seconds_total → 명령어 실행 시간 (성능 저하 원인 분석)
  • Replication 및 Clustering 지표
    • redis_connected_slaves → Replica 개수 (복제 정상 작동 여부 확인)
    • redis_master_repl_offset → Replica 동기화 지연 (네트워크 또는 부하 문제 점검)

Redis의 성능 및 최적화 방법은 무엇인가? 

Redis의 메모리 관리는 어떻게 동작하는가? 

Redis 메모리 관리의 핵심은 정해진 maxmemory 한도 내에서만 사용하는 것이다. 

  • redis.conf 또는 클라우드 구성(AWS Elasticache 등)에서 maxmemory 설정 가능하다. 대표적인 방식은 위에서 보았던 LRU(Least Recently Used) 방식과 LFU(Least Frequently Used) 방식이 있다. 
  • 메모리가 설정 한도를 초과하면 eviction(제거) 동작을 통해 데이터를 비워야 한다. 

그 외에도 Redis의 데이터 저장/관리 전략은 다음과 같다. 

데이터 저장 메모리에 상주 (디스크 사용 안 함)
영속성 (Persistence) AOF, RDB로 스냅샷 저장 가능
maxmemory 메모리 한계 설정, 초과 시 eviction 시작
압축 (기본) 작은 String은 SDS (Simple Dynamic String)으로 압축
메모리 최적화 자료구조 작은 Hash, Set, List는 내부적으로 Ziplist, Intset 사용

 

Redis에서 메모리가 부족할 때 어떻게 동작하는가?

 

maxmemory를 초과하면, 더 이상 데이터 저장 불가하다. 이 때 Redis는 설정된 eviction 정책에 따라 데이터 제거가 가능하다. 

7가지 핵심 질문으로 알아보는 개발자를 위한 Redis 활용법 - Redis의 성능 및 최적화 방법은 무엇인가?  - Redis에서 메모리가 부족할 때 어떻게 동작하는가?

일반적으로 allkeys-lru, allkeys-lfu가 캐시 서버로는 적합하다. 또 최적화 전략은 아래와 같다. 

7가지 핵심 질문으로 알아보는 개발자를 위한 Redis 활용법 - Redis의 성능 및 최적화 방법은 무엇인가?  - Redis에서 메모리가 부족할 때 어떻게 동작하는가?

또한 운영 관점에선 아래와 같이 생각해 볼 여지가 있다. 

7가지 핵심 질문으로 알아보는 개발자를 위한 Redis 활용법 - Redis의 성능 및 최적화 방법은 무엇인가?  - Redis에서 메모리가 부족할 때 어떻게 동작하는가?

이처럼 Redis는 메모리 한계 내 성능 최적화를 위해 LRU/LFU 기반 자동 제거를 지원하며, 캐시/스토리지 목적에 따라 다른 메모리 정책과 운영 전략이 필요하다. 

파이프라이닝(Pipelining)이란 무엇이며, 언제 사용해야 하는가?

  1. 정의
    • 여러 개의 명령(Command)을 한 번에 서버로 보내고, 결과도 한 번에 받아오는 통신 방식이다. 
    • 일반 Redis 명령은 요청 → 응답 → 요청 → 응답 방식으로 매번 RTT(Round Trip Time)가 발생한다. 
    • 파이프라이닝은 요청 여러 개를 묶어서 보내기 때문에 네트워크 지연 최소화, 성능 향상된다. 
  2. 언제 사용해야 하나?
    • 수천~수만 개의 대량 데이터 저장/조회 : 네트워크 왕복 횟수 줄여서 성능 향상 (특히 고지연 환경)
    • 대량 초기화 (Warm-up) 작업 : 빠른 캐시 로딩, 데이터 미리 적재
    • 배치 작업 (Bulk Insert/Update) : 대량 작업 시 응답-요청 반복 없이 처리
    • 많은 독립 작업을 한번에 처리 : 연관 없는 작업도 한번에 요청하여 효율적 처리
    • 고성능 데이터 적재 (Log 수집, 모니터링 데이터) : 빠른 대량 데이터 수집 및 적재 필요
  3. 장점
    • 성능 향상 (네트워크 최적화) : 여러 명령을 모아서 전송 → 왕복 횟수(RTT) 최소화
    • 대량 작업 효율적 수행 : 반복적인 작업을 단 한 번의 요청으로 수행 가능
    • 클라이언트 측 부하 감소 : 네트워크 통신 병렬화로 처리량 향상 
  4. 주의사항
    • 메모리 사용량 증가 (클라이언트/서버) : 명령이 모아서 전송되므로 대량 요청 시 메모리 사용량 급증
    • 명령 순서 보장 필요 : 파이프라이닝 내 명령 순서대로 실행되지만 응답 순서를 잘못 다루면 데이터 꼬일 수 있음
    • 트랜잭션 아님 (Atomic 아님) : 묶어서 보내지만, 원자성 보장 안 됨 (중간 실패 가능성 존재)
  5. 동작방식
    • 아래와 같이, 클라이언트가 여러 명령을 한 번에 보내면 Redis는 순차적으로 처리 후 응답을 모아서 반환한다. 
(기존 방식)
Client  Redis : SET key1 val1
Redis  Client : OK
Client  Redis : SET key2 val2
Redis  Client : OK

(파이프라이닝)
Client  Redis : SET key1 val1, SET key2 val2 (동시에 전송)
Redis  Client : OK, OK (한 번에 응답)

 

대용량 데이터를 저장할 때 주의할 점은 무엇인가?

7가지 핵심 질문으로 알아보는 개발자를 위한 Redis 활용법 - Redis의 성능 및 최적화 방법은 무엇인가?  - 대용량 데이터를 저장할 때 주의할 점은 무엇인가?

Redis는 어떻게 복제, 분산 및 확장 가능할까? 

Redis에서의 복제 구조와 복제 메커니즘은 어떠한가? 

  1. 복제의 정의 
    • 복제(Replication)는 "데이터를 복사해서 여러 군데 똑같이 저장" 하는 것이다. 즉, 한 Redis 서버에 저장된 데이터를 다른 Redis 서버에도 똑같이 저장하는 것이다. 
  2. 필요한 이유
    • 장애 발생 (서버 고장) : 마스터 서버가 죽으면, 복제본(슬레이브)로 서비스 계속 유지
    • 읽기 부하 분산 (확장) : 많은 사용자가 접속할 때 복제본들이 대신 읽어주기 때문에 속도 유지
    • 데이터 보호 (백업) : 마스터 서버에 문제가 생겨도 복제본에 데이터가 남아 있어 복구 가능
  3. 복제의 기본 구조
    • 마스터 : 진짜 주인. 데이터를 처음 저장하고 모든 명령을 처리함
    • 슬레이브 : 마스터를 따라하는 친구. 마스터 데이터를 똑같이 복사하고 사용자가 읽을 수도 있음 
  4. 복제 메커니즘 : 데이터 복사 과정
    • 복제 연결 : 처음 슬레이브가 마스터와 연결할 때 한 번 전체 데이터를 몽땅 복사한다. 순서는 아래와 같다. 
      1. 슬레이브가 마스터에게 "너의 데이터를 복사하고 싶어" 요청
      2. 마스터가 RDB 스냅샷을 만들어서 슬레이브에게 전송
      3. 슬레이브는 그 데이터를 받아서 자기 메모리에 저장
    • 실시간 복제 : 복제가 끝난 뒤에는 마스터가 받은 모든 쓰기 명령을 슬레이브에게 그대로 보내서 실행한다. 
    • 장애 이후 복구 : 마스터는 최근 명령 기록(Replication Backlog)에서 빠진 부분만 다시 보내준다 (부분 복제, Partial Resync)
  5. 동작 원리
    • Replication Backlog : 마스터가 슬레이브에게 보낸 최근 명령들을 모아놓는 공간 (메모리 내 기록)
    • RDB + Command Sync : 처음엔 RDB(스냅샷) 전체 복사 + 이후엔 쓰기 명령 실시간 전달 
    • PSYNC (Partial Sync) : 잠깐 끊긴 슬레이브가 돌아올 때 빠진 부분만 다시 복사
  6. 복제 구성의 확장 : 복제의 복제 (Chained Replication)
    • Slave 1이 Slave 1-1의 마스터 역할을 할 수 있다. 
    • 이렇게 하면 부하를 나눌 수 있다 (읽기 분산) 
  7. 복제 데이터 동기화 문제와 해결
    • 문제점
      • 마스터-슬레이브 데이터 불일치 : 슬레이브가 오래 끊겼다가 돌아오면 데이터 차이 발생
      • 네트워크 끊김/장애 : 슬레이브가 중간에 끊기면 복구 필요
    • 해결방법
      • Replication Backlog (최근 명령 저장) : 슬레이브가 잠깐 끊겼다 돌아오면 빠진 명령만 다시 전송 (부분 복제)
      • RDB 전체 복사 : 너무 오래 끊겼을 때는 다시 전체 복사 (Full Sync)
      • 자동 재연결 + 복구 : 슬레이브가 자동으로 다시 연결, 데이터 복구
      • 비동기 복제 : 마스터가 먼저 저장하고 슬레이브는 나중에 받기 때문에, 빠르고 부드럽게 동작
  8. 복제의 한계
    • 완벽한 동기 복제 아님 (비동기 복제) : 슬레이브가 마스터보다 조금 느릴 수 있다. 
    • 쓰기 분산 불가 : 마스터에게만 쓰기 가능 (슬레이브는 읽기만), 고성능 쓰기 분산이 어렵다.
    • 장애 시 데이터 유실 가능성 (최신 데이터) : 마스터 죽고 슬레이브 승격 시, 아주 최근 데이터는 유실될 수 있다. 
  9. 실제 사용 예시
    • 읽기 부하 분산 (읽기 Replica) : 슬레이브가 읽기 전담 → 마스터는 쓰기만. 읽기 성능 향상
    • 고가용성 (장애 복구) : 마스터 죽어도 슬레이브가 대신 마스터로 승격
    • 지리적 분산 (멀티 리전 복제) : 한국 서버가 마스터, 미국 서버가 슬레이브. 전 세계 사용자 빠른 응답 가능 

Redis Sentinel이란 무엇이며, 장애 조치는 어떻게 이루어지는가?

  1. 역할
    • Redis Sentinel(센티넬)은 쉽게 말하면 Redis를 지키는 감시자이다. Redis가 죽거나 문제가 생겼는지 계속 감시하고, 문제가 생기면 자동으로 다른 서버로 바꿔주는 역할을 한다. 즉, 장애 조치 (Auto Failover) 시스템이다. 
  2. 구성
    • 마스터 : 실제 데이터가 저장되는 서버
    • 슬레이브 : 마스터를 복사해서 데이터 저장, 백업 서버 역할
    • Sentinel 서버 : 감시하는 서버, 여러 개 설치해서 서로 감시 + 투표로 결정
  3. 장애 조치 방법
    • 마스터가 죽으면 Sentinel이 감지
    • Sentinel 여러 개가 "진짜 죽은 게 맞나?" 확인하고 투표
    • 투표로 결정되면 슬레이브 중 하나를 새로운 마스터로 승격
    • 다른 슬레이브들이 새 마스터에게 복제 시작
    • 애플리케이션에도 새 마스터 정보 전달

 

Redis Cluster는 어떻게 작동하며, Sharding(샤딩)은 어떻게 이루어지는가?

  1. 정의
    • Redis를 여러 대로 나눠서(분산) 데이터 저장하는 방법
    • 데이터가 너무 많을 때, 하나의 Redis 서버로는 감당 불가
    • 그래서 여러 서버로 자동 분산해서 저장
  2. 샤딩이란
    • 샤딩(Sharding) : 데이터를 여러 Redis 서버로 나눠서 저장하는 것 
    • 슬롯(Slot) : 데이터를 나눠 저장할 칸 (0~16383까지 16384칸)
  3. 동작원리
    • Redis Cluster는 해시 슬롯(Hash Slot) 이라는 걸 사용
    • 키를 숫자로 바꾸고 16384개의 칸 중 하나에 배정
    • 각 서버가 칸을 나눠서 맡음
  4. 노드 구성
    • 마스터 노드 : 데이터 직접 저장, 읽고 쓰는 역할
    • 슬레이브 : 마스터 백업, 마스터 죽으면 자동 승격 (Sentinel 기능 내장)
  5. 장애 복구
    • 마스터 노드 죽으면 그 노드의 슬레이브가 자동으로 마스터 승격
    • 다른 노드들은 새로운 마스터를 인식

멀티 노드 환경에서 데이터 정합성을 유지하는 방법은?

  1. 데이터 정합성이 중요한 이유
    • A 서버와 B 서버 데이터가 다르면? 다른 사용자에게 다른 정보가 보일 수 있음
    • 서버가 죽고 나서 복구했을 때 데이터가 다르면? 데이터 신뢰성 깨짐 (예: 결제 정보 다른 경우 심각)
  2. Redis의 데이터 정합성 유지 방법
    • Replication (복제) : 마스터 → 슬레이브로 항상 데이터 복사, 최신 데이터 유지
    • Failover : 마스터 죽으면 슬레이브가 마스터 승격, 최신 데이터로 유지
    • Cluster 해시 슬롯 : 같은 키의 데이터는 항상 같은 슬롯 → 항상 같은 서버에 데이터 저장
    • 동기 복제 (WAIT 명령어) : 마스터가 슬레이브에 동기 복제 완료 후 작업 응답 (강한 일관성 필요할 때)
    • 비정상 노드 자동 격리 (Fail-fast) : 데이터 문제 있는 서버는 자동 격리 → 잘못된 데이터 전파 방지
    • 클라이언트가 클러스터 인식 (Re-routing) : 클라이언트가 잘못된 서버로 접근 시 자동으로 맞는 서버로 재요청
  3. SplitBrain 상황에서 데이터 정합성 유지 방법
    • 정의
      • 원래 하나로 동작해야 할 Redis Master가 네트워크 단절 등으로 인해 여러 개의 Master로 쪼개져버리는 현상. 즉, 동시에 여러 Master가 존재해서 각기 다른 데이터를 받는 상황
    • 위험한 이유
      • 두 개의 Master가 각각 데이터를 받음 : 서로 다른 데이터로 업데이트 → 복구 시 충돌 발생 가능
      • 장애 복구 후 어느 쪽이 맞는지 모름 : 데이터 일관성 깨짐
      • 트랜잭션/결제/주문 같은 중요 데이터 처리 시 : 금전적 손실, 신뢰 문제, 법적 문제 발생
    • 발생 시나리오
      • Sentinel 장애 조치 중 네트워크 단절 : 마스터와 슬레이브 간 네트워크 장애 발생
      • Redis Cluster의 일부 노드가 다른 노드와 통신 불가 : 노드 간 통신 단절 (AZ 장애, 리전 장애, 네트워크 장애)
      • 리더 선출 알고리즘 실패로 두 개의 마스터 승격 : Sentinel 투표 중 과반 미달 or 타임아웃
    • 데이터 정합성 유지 방법
      • 사실 근본적으로는 CAP 이론에 따르면 네트워크가 분리된 상황에서는 일관성(Consistency)과 가용성(Availability)을 동시에 보장할 수 없다. 
    • 대응 전략
      • Quorum (과반수) 기반 장애 조치 (Sentinel, Cluster)
        • Sentinel 과반 투표
          • 설명 : Sentinel들이 과반수 동의해야 새 마스터 선출. 
          • 한계/주의사항 : 네트워크 분리로 과반 실패 시 서비스 중단 (가용성 희생) 
        • Cluster 과반 기반 선출
          • 설명 : Cluster 노드들도 과반 연결 유지해야 리더 승격
          • 한계/주의사항 : 과반 실패 시 Split Brain 방지하지만, 쓰기 불가 (정합성 우선)
      • 외부 분산락 시스템 연계
        • 외부 락 기반 리더 선출
          • 설명 : Redis 장애 조치 시 외부 시스템에 리더 락 요청 후 선출
          • 한계: 락 시스템 장애 시 Redis 조작 불가
        • 락 없을 시 복구 대기
          • 설명 : 락 점유 실패 시 복구 대기, 마스터 중복 방지
          • 한계: 외부 락 시스템 관리 필요, 복잡성 증가
      • 물리적/네트워크 기반 장애 방지
        • 멀티 AZ 구성 (AWS, GCP, Azure)
          • 설명 : 다중 가용영역(AZ)에 분산 배치 → 한 AZ 문제 시 전체 장애 방지
          • 한계: 멀티 리전 간 지연 (Latency)
        • 고성능 네트워크 장비
          • 설명 ; 장애 발생 시 자동 라우팅, 페일오버
          • 한계 : 인프라 비용 증가
      • 애플리케이션 계층에서 쓰기 차단/중복 방지
        • 마스터 후보 확인 후 쓰기 허용
          • 설명 : 애플리케이션이 현재 마스터인지 확인 후 쓰기 실행.
          • 한계 : 복잡한 로직, 응답 시간 지연
        • 레디스 상태 체크로 마스터 확인
          • 설명 : Sentinel/Cluster에서 상태 확인 후 쓰기
          • 한계 : 고가용성 요구 시 성능 저하 가능

Redis에서는 데이터 일관성과 트랜잭션을 어떻게 보장할까? 

Redis의 트랜잭션(MULTI/EXEC)은 어떻게 동작하는가?

MULTI
명령1 (예: SET user:1 "Matt")
명령2 (예: INCR balance:1)
EXEC (명령1, 명령2 순서대로 실행)
  1. 주요 특징
    • 명령 일괄 실행 : EXEC 때 한번에 순차 실행. 중간 실패 명령이 있어도 나머지 실행됨
    • 중간 실패 시 전체 취소 아님 : 트랜잭션 내 일부 명령 실패해도 나머지는 실행 (DB 트랜잭션의 Rollback 아님)
    • 명령 대기 (큐잉) : MULTI 후 명령어들은 대기 상태. EXEC 할 때까지 실행 안 함
    • 순차성 보장 : 명령 순서대로 실행. 단, 완전한 원자성/롤백 보장 아님
  2. RDBMS 트랜잭션과 비교
    1. 원자성  : 모두 성공 아님. 일부 실패 시 나머지는 실행
    2. 격리성 : 단일 클라이언트 대상. 동시성 경합 시 WATCH 필요
    3. 일관성 : 일부 보장 (동시성 문제 존재)

Redis에서 원자적(Atomic) 연산을 보장하는 방법은?

Redis에서 단일 명령은 항상 원자적이다. 그렇지만 Redis의 트랜잭션(MULTI/EXEC)만으로는 원자성(Atomicity)이 완전히 보장되지 않는다. 이를 보장하려면 Lua 스크립트를 사용하면 진정한 의미의 원자성이 보장할 수 있다.

7가지 핵심 질문으로 알아보는 개발자를 위한 Redis 활용법 - Redis에서는 데이터 일관성과 트랜잭션을 어떻게 보장할까?  - Redis에서 원자적(Atomic) 연산을 보장하는 방법은?

언제 어떤 걸 쓰면 좋을까? 

7가지 핵심 질문으로 알아보는 개발자를 위한 Redis 활용법 - Redis에서는 데이터 일관성과 트랜잭션을 어떻게 보장할까?  - Redis에서 원자적(Atomic) 연산을 보장하는 방법은?

추가로 Redis Pipelining과 MULTI/EXCE의 차이점에 대해서 알아보자. 둘 다 여러 명령 모아서 보낸다는 점에서 공통점이 있다. 그렇지만 아래와 같은 차이점이 존재한다. 

7가지 핵심 질문으로 알아보는 개발자를 위한 Redis 활용법 - Redis에서는 데이터 일관성과 트랜잭션을 어떻게 보장할까?  - Redis에서 원자적(Atomic) 연산을 보장하는 방법은?

분산 환경에서 RedLock(분산 락)을 어떻게 구현하는가?

SET lock resource NX PX 3000 (모든 Redis 노드에 요청)
3/5 노드 이상 성공 → 락 획득 성공
작업 수행
UNLOCK (락 해제)

 

이상으로 Redis에 대해 자세히 알아보았다. 

반응형

댓글