일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- API 활용 신청
- url 랜더링
- expo
- 꿀팁 환영
- 창업 300
- 개발
- Dreamhack
- 오블완
- apk 빌드
- 블로그 뉴비
- 공공데이터 포털
- level3
- python
- 프로그래머스
- web-view
- Redux
- 새로고침
- 사업계획서
- 고고학 최고의 발견
- 훈수 가능
- redux state값 유지
- php-1
- 코딩테스트
- 보안
- 티스토리챌린지
- react-router-dom
- 부산 맛집 OPEN API
- React
- 드림핵
- Today
- Total
1223v
Redis 데이터 분산 [redis 죽으면 어카노…?] 본문
고민의 시작
- 우리 프로젝트는 MYSQL RT 저장 방식 ⇒ Redis RT 저장 방식으로 변경했다.
- 그렇다면 고려해야하는 것이 redis가 다운되었을 경우의 복구 전략이다..
왜 이 고민이 필요해?
- 현재 redis를 도입하는 대부분의 프로젝트들이 로그인에서 RT를 저장하는 부분일 것이다.
사용자 ⇒ 로그인 시도 ⇒ at/rt 발급 ⇒ rt redis에 저장 ⇒ at/rt 반환 ⇒ 로그인 성공
보통 토큰을 사용한 로그인은 위와 같은 과정을 따르는데,
사용자 ⇒ 로그인 시도 ⇒ at/rt 발급 ⇒
rt redis에 저장
⇒
at/rt 반환 ⇒ 로그인 성공
redis가 다운된다면 다음 과정마저, 에러가 나서 모든 로그인 과정이 불통이 된다.
이는 로그인이라는 도메인이 redis에 강한 연결(의존)이 되있음을 의미한다.
때문에 우리는 이 강한 의존도를 분리해줄 필요가 있다.
기존 방식
- redis가 도입되면서, 따로 redis 관련 에러 처리를 해주지 않았다.
변경 방식
이후, redis에 어떠한 복구 전략이 없다는 부분에서 찾아보고 도입하게 되었다.
우선 생각한 해결 방식은 아래와 같다.
- Fallback 전략⇒ Redis가 다운된 경우: 예외 처리 후 MySQL
member
테이블에 RT를 저장하여 사용. - ⇒ RT 갱신 시: Redis가 복구되면 다시 Redis를 우선적으로 사용하도록 설정.
- ⇒ Redis가 정상 작동할 경우: Redis에 RT를 저장하고, AT 검증 시 사용.
- Redis Master/Slave (Replication)
- Redis Cluster
우리는 오늘 Redis에서 제공하는 Redis Master/Slave (Replication)와 Redis Cluster를 알아보도록 하겠다
Redis Master/Slave (Replication)
Master 노드가 데이터를 저장하고 Slave 노드가 Master의 데이터를 복제하여 백업 역할을 한다.
- 주로 고가용성을 위해 사용한다.
- Redis Sentinel을 추가하면 자동 장애 조치를 지원할 수 있다.
- 하나의 Master에 다수의 Slave가 붙을 수 있다.
- Master는 Read-Write Mode로 동작하고, Slave는 Read-Only Mode로 동작한다.
- Master와 Slave 사잉의 Replication은 Async 방식을 이용
Data 변경시 변경 내용을 backlog에 기록 ⇒ Slave가 Master에 접속하여 backlog의 내용을 바탕으로 Replication 수행
이 과정에서 비동기로 동작하므로서 Master의 저장된 Data가 Slave에 잠깐동안 저장되지 않을 수 있음.
예). npm 버전 업데이트시 어쩌다 한번 검색창에서의 버전과 라이브러리 페이지 내에서의 버전 차이가 보여짐
중요.
Master가 죽을 경우 Slave는 Master에게 주기적으로 Connection을 요청하며 Master가 되살아 날때까지 대기 ⇒ Master가 복구되면 Slave는 Replication을 수행하여 master와 동기화
만약 Master 복구가 힘든 경우 Redis 관리자는 Slave 중에 하나를 수동으로 Master로 승격 → 나머지 Slave들을 새로운 Master로부터 Replication하도록 설정 → Master가 바뀐뒤에는 죽었던 Master는 새로운 master의 Slave로 설정
Sentinel
- Master의 동작이 멈출경우, Redis 클라이언트는 Slave를 통해서 읽기 수행은 가능하지만, 쓰기 동작은 불가능하다.Master의 DownTime은 Redis Cluster의 가용성을 떨어뜨린다.
- result.
⇒ 이 문제를 해결하는 것이 Sentinel이다.
- Sentinel은 Master가 죽는지 감지하고 Master가 죽었을 경우 Slave 중 하나를 Master로 승격시키고 기존의 Master는 Slave로 강등시킨다.
- Redis 관리자 없이 자동으로 동작하기 때문에 Master의 Downtime을 최소화하여 HA를 가능하게 만들 수 있다.
1) Redis Sentinel 설정 (sentinel.conf
)
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1
2) Spring Boot에서 Redis Sentinel 설정 (application.yml
)
spring:
redis:
sentinel:
master: mymaster
nodes: 192.168.1.10:26379,192.168.1.11:26379,192.168.1.12:26379
Sentinel의 Split-brain 방지 방법
✅ 1) Sentinel 홀수 개 배치 (Quorum 투표) = 일반적인 방법
- Sentinel을 3개 5개 처럼 홀수 개로 배치하여 과반수를 확보
- 투표로 가장 많은 Sentinel이 인식한 노드를 마스터로 지정
- 설정 값 적용 가능 ⇒ 특정 Redis에 장애 발생 시 몇개의 Sentinel이 특정 Redis의 장애 발생을 감지시 장애라 판단
✅ 2) STONITH
- 문제가 있는 노드를 강제로 종료하여 중복 마스터 방지
- 고가용성 클러스터 환경에서 주로 사용
✅ 3) Network Partition Detection (네트워크 분리 감지)
- 클러스터의 네트워크 상태를 주기적으로 체크하여 마스터 승격 전 네트워크 상태를 검증
HAProxy
- Redis Master-Slave 구성 시 Client는 각각의 IP/Port를 알고 적절하게 붙어서 동작 수행
- BUT, Master가 교체될 때 마다 Redis를 이용하는 모든 Client의 설정을 바꾸는 일은 쉬운게 아님.
- HAProxy가 client에게 redis의 Master, Slave에 일정하게 접근할 수 있는 End-point를 제공
- tcp-check를 이용해서 주기적으로 각각 어떤식으로 동작하는 파악하고 그에 따라 동적으로 Routing Rule을 설정
Redis Cluster
여러개의 redis 노드를 사용하여 데이터를 자동으로 Replication 및 샤딩(분산 저장) 기법
- 수평 확장 지원 ⇒ 하나의 마스터가 모든 데이터를 저장하는 기존 방식과 달리 데이터를 여러 마스터 노드에 분산하여 저장
- 고가용성 ⇒ 특정 노드가 장애가 나도 자동으로 복구(Failover)
- 자동 페일오버 ⇒ 마스터가 죽으면 슬레이브가 자동으로 승격되어 서비스 중단 방지
- Split-brain 방지 ⇒ 홀수 개로 구성하여 잘못된 마스터 승격 방지
- 각 redis 모두 직접 연결하여 gossip Protocol을 통해 통신(상태 정보 교환)
- [master의 죽음을 파악한 뒤 스스로 master로 승격하여 master를 대신함]
- gossip Protocol은 Redis 클라이언트가 이용하는 port 번호보다 높은 번호 사용 (기본 : 6379)
- client도 redis와 직접 연결
- Redis Cluster는 Muti-master, Muti-Slave 구조
중요!
- Slave Redis에게 쓰기 요청을 보내면 Slave Redis는 해당 요청을 처리할 수 있는 Master Redis의 정보를 Client에게 넘겨준다
- 요청 Redirection으로 인해 Client는 Redis Cluster 이용시 redis Cluster를 지원하는 라이브러리가 필수 (spring data redis와 같은 라이브러리)
- Master와 Slave사이의 Replication은 비동기방식 ⇒ Master 다운은 Master-Slave 간의 Data 정합성을 깰 수 있음. (데이터 충돌 발생시 무조건 나중에 Master가 된 Data를 기준으로 정합성 맞춤)
평가
고려 요소 | Redis Sentinel | Redis Cluster |
---|---|---|
데이터 정합성 | ✅ 보장됨 (마스터-슬레이브 동기화) | ❌ 보장되지 않음 (샤딩 중 일부 데이터 유실 가능) |
수평 확장(Sharding) | ❌ 지원 안함 (단일 마스터) | ✅ 지원 (데이터 분산) |
자동 페일오버 | ✅ 지원 | ✅ 지원 |
멀티 키 연산 | ✅ 지원 (MGET , MSET 가능) |
❌ 제한됨 (같은 슬롯에서만 가능) |
설정 복잡도 | 🔻 단순함 | 🔺 복잡함 |
사용 사례 | Refresh Token, 세션 저장, 인증 데이터 | 대규모 데이터 처리, 캐싱, 로그 저장 |
📢 Refresh Token 저장에서는 "데이터 유실 방지"와 "정합성 유지"가 중요하므로, Redis Sentinel이 더 적합함.
고찰
- 우리는 결국 redis에 사용자의 정보를 저장하는 셈이다. 즉, redis가 다운되더라도 혹은 복구되더라도 충돌과 같은 장애는 막아야한다.
- 또한, refreshtoken을 마이그레이션 하면서 관리포인트가 증가 했기때문에 더이상의 관리포인트를 늘리는 클러스터는 지양하고자 했다.
- 때문에 현재 서비스에서 가장 적합한 복구 솔루션은 redis sentinel을 통한 복구 전략을 진행하는 것이 현재로써는 맞다고 생각한다.
- 다른 이견 있으면 알려주세영
'개발 > 개발 고찰' 카테고리의 다른 글
낙관적 락(Optimistic Lock) 비관적 락(Pessimistic Lock) [feat. 레디베리 Race Condition 문제] (0) | 2025.02.13 |
---|---|
명확한 근거를 통한 불필요한 polling 점진적으로 제거하기(Long polling, SSE, Websocket)[feat.레디베리] (0) | 2024.10.31 |
[SpringBoot] Spring Security 로그인 시, 세션 유지 안되는 현상 (1) | 2024.09.30 |
[React] 음원, 녹음 동시 작업 실행 시, 녹음 품질 저하 문제 해결 및 고찰 (0) | 2024.09.23 |
단위테스트 적응기 (1) | 2024.07.16 |