대기열 시스템을 구현하기 위해 Redis의 ZSet 자료구조를 활용하였으며, 이에 대한 Redis 설정과 아키텍처 구조를 정리해보고자 합니다.
Redis의 구조는 크게 Master/Slave 구조와 Cluster 구조로 나눌 수 있습니다.
[ Master/Slave 구조 ]
Master/Slave 구조는 리더–팔로워 모델을 기반으로 한 레플리케이션 아키텍처로, 하나의 Master 노드와 n개의 Slave 노드로 구성됩니다.
모든 쓰기 작업은 Master 노드에서 처리되며, 읽기 작업은 Master와 Slave 노드 모두에서 처리될 수 있습니다.
Slave 노드는 Master 노드의 데이터를 실시간으로 복제하므로, Master 노드에 장애 발생 시 Slave 노드를 Master로 승격하여 ( failover ) 서비스 유지할 수 있습니다.
[ Cluster 구조 ]
여러 개의 Master 노드가 데이터를 분산하여 저장하는 구조이며, 각 Master 노드는 하나 이상의 Replica( Slave ) 노드를 통해 데이터를 복제합니다.
데이터는 키의 해시 값을 기반으로 16384개의 해시 슬롯 중 하나에 매핑되며, 해당 슬롯을 담당하는 Master 노드에 저장됩니다.
장애 감지를 위해 클러스터 내 Master 노드들 간에 과반수 합의가 필요하므로, 정상적인 장애 조치( failover )를 위해서는 최소 3개의 Master 노드 구성이 권장됩니다.
Sentinel과 같은 외부 감시 노드를 두지 않으며, 클러스터 내부 노드들끼리 직접 통신하고 합의를 통해 장애를 감지하고 자동으로 failover를 수행합니다.
대기열 시스템에서는 각 대기열에 대해 전역적인 순서 보장이 중요합니다.
Redis Cluster 구조는 여러 Master 노드에 데이터가 분산 저장되므로 전역적인 순서 보장이 어렵기 때문에 데이터 일관성과 순서 보장을 위해 Cluster 구조 대신 단일 Master 기반의 Master/Slave( Replica ) 구조를 사용하였습니다.
하나의 Master, 두 개의 Slave, 세 개의 Sentinel 노드로 이루어진 Redis 구조를 Docker 환경에서 구성하였습니다.
[ Sentinel 노드는 왜 사용해야 할까 ]
각 노드의 상태를 지속적으로 모니터링하고, 장애를 감지하며, 자동 Failover를 수행함으로써 시스템의 고가용성과 안정성을 보장하는 역할을 합니다.
‼️ Docker-Compose 설정 및 Redis 설정은 Github을 참조해주세요
Master 노드에 장애 발생하는 경우
down-after-milliseconds ( 기본 값 5초 ) 경과 후 각 Sentinel이 SDOWN( 주관적 장애 ) 판정합니다.+switch-master 이벤트 발행합니다.+switch-master를 수신하면 내부 master 주소 갱신합니다.autoReconnect(true)에 의해 새 master 주소로 재연결됩니다.[ Master 노드가 다운되는 경우 ]
새 Master에 연결을 시도하여 이전 Master가 Slave로써 새 Master와 동기화 시도
queue-redis-master 07:28:27.337 * Before turning into a replica, using my own master parameters to synthesize a cached master: I may be able to synchronize with the new master with just a partial transfer.
queue-redis-master 07:28:27.338 * Connecting to MASTER 172.18.0.7:6379
queue-redis-master 07:28:27.338 * MASTER <-> REPLICA sync started
queue-redis-master 07:28:27.349 * Master replied to PING, replication can continue...
Redis는 이전 상태를 바탕으로 부분 동기화를 시도하지만, replication ID가 달라 부분 재동기화 불가하여 전체 데이터 복제( Full resync )를 수행
queue-redis-master 07:28:27.350 * Trying a partial resynchronization ...
queue-redis-slave01 07:28:27.350 * Partial resynchronization not accepted ...
queue-redis-slave01 07:28:27.351 * Full resync will continue with dedicated rdb channel
새 Master에서 RDB 스냅샷을 받아 이전 Master가 완전히 동기화되고, 이전 Master는 이제 slave로서 완전히 새 Master를 따라감 ( 기존 데이터는 덮어씌워지고, Master 상태에서 slave로 전환 )
queue-redis-slave01 07:28:32.288 * Starting BGSAVE for SYNC with target: replicas sockets (rdb-channel)
queue-redis-master 2026 07:28:32.288 * Starting to receive RDB and replication stream in parallel
queue-redis-master 07:28:32.307 * Done loading RDB, keys loaded: 2, keys expired: 0
queue-redis-slave01 07:28:32.308 * Synchronization with replica 172.18.0.4:6379 succeeded
Sentinel이 새로운 구성을 기록하고, 이전 Master가 slave로 등록되어 Sentinel 관점에서도 새 Master → 기존/재시작된 Master → 다른 Slaves 구조 완성합니다.
queue-redis-sentinel01 | * +slave slave 172.18.0.4:6379 @ toMaster 172.18.0.7 6379
Redis 장애로 인해 Kafka 컨슈머가 메시지를 처리하는 과정에서 예외가 발생할 수 있습니다. 이 경우 Kafka Consume에 설정된 재시도 로직에 따라 메시지는 설정한 횟수까지 재처리됩니다.
하지만, 만약 Redis가 failover를 완료하기 전에 재시도 횟수를 모두 소진한다면, 해당 메시지는 최종적으로 DLT로 전송되어 후처리하게 됩니다.
만약 master의 데이터가 slave에 복제되지 않은 상태로 failover가 발생한다면 ?