면접들을 보고 Redis에 대해 추가적으로 학습하면서 이론적으로는 클러스터가 뭔지, 장애 복구는 어떻게 이루어지는 구조인지를 알 수 있었다. 하지만 직접 해보거나 유의미한 차이를 직접 보지는 못했기 때문에 이번 기회에 로컬에 간단하게나마 환경을 구축해보고, 기존에 Redis로 성능 향상을 이루어냈던 선착순 응모 프로젝트를 이용하여 테스트를 해보려고 한다.
서비스 요구사항이 점점 고도화되고 Redis의 사용은 많아지면서 개발만 잘하기보다는 운영 관점의 지식도 알아두면 좋을 것 같다. 성능 최적화 측면에서도 이러한 인프라를 이해해둬야 여러가지 해결책들을 생각해낼 수 있을 것 같다.
우선 현재 테스트 환경의 상태는 Docker를 통해 Redis 7.2 버전을 로컬에 띄워둔 상태이고, Prometheus에 Redis Exporter 스크래핑을 설정해 Redis 관련 메트릭을 수집하여 그라파나에 모니터링을 구축해놓은 상태이다. 이 환경으로 성능테스트를 해보고 장애 복구 등의 시나리오를 확인해보려고 한다.
RDB vs AOF
이 둘은 Redis의 주요 영속화 방식을 의미한다.
- RDB: 일정 주기나 조건에 따라 메모리 전체 상태를 스냅샷으로 떠 디스크에 저장한다.
- 장점
- 이러한 스냅샷 파일은 압축되어 있어 파일 크기가 작고, 백업이나 이동이 용이하다.
- 디스크 I/O가 스냅샷 시점에만 집중되어 쓰기 성능에 미치는 영향이 비교적 적다.
- 단점
- 마지막 스냅샷 이후 발생한 변경사항들이 유실 가능하다.
- 스냅샷 생성 시점에 대량 I/O가 발생 해 매우 큰 데이터셋일 경우 지연이 발생할 수 있다.
- 장점
- AOF: 모든 쓰기 명령을 로그 형식으로 디스크에 순차적으로 기록한다. Redis 실행 시 AOF를 재생하는 방식으로 메모리 상태를 복원한다.
- 장점
- 스냅샷 이후 모든 변경이 로그에 누적되어 장애 시 데이터 유실이 최소화된다.
- 단점
- 로그 파일 크기가 커질 수 있고, 재시작 시 복구에 시간이 오래걸린다.
- 디스크 I/O가 빈번해져 쓰기 성능에 영향을 끼친다.
- 장점
결론적으로 간단하게는 쓰기 처리량이 우선이라면 RDB를, 내구성이 중요하다면 AOF를 선택하면 된다. 각각 트레이드오프가 있고, 두 방식을 모두 채택해 균형있게 운영도 가능하다.
AOF 디스크 동기화 정책
- appendfsync everysec
- 매 초 한번씩 디스크에 동기화
- 권장되는 방식
- appendfsync always
- 명령마다 동기화
- 가장 명확하지만 성능 저하가 일어날 수 있다.
- appendfsync no
- 운영체제에 위임하는 방식
성능 테스트
우선 기본적으로 AOF를 따로 설정해주지 않았다면 RDB 방식으로 되어 있을 것이다.
1
CONFIG GET appendonly
위 명령어의 결과가 no가 나온다면 RDB 방식이다.
성능 테스트를 해보자.
Redis Benchmark (RDB)
Redis Benchmark는 Redis 서버의 처리량과 지연을 측정해주는 간단한 클라이언트 프로그램이다.
Redis를 설치했다면 기본적으로 함께 설치된다.
우선 나는 Docker로 Redis 환경을 띄워놨기 때문에 cmd 창에서 Benchmark를 실행하기 위해 아래 명령어로 접근했다.
1
docker exec -it observability-redis-1 sh
Docker 컨테이너의 이름이고 observability-redis-1 부분에 알맞은 컨테이너 이름을 입력하면 된다. 정상적으로 접근했다면 # 이 나타나고 벤치마크를 실행해볼 수 있다.
1
redis-benchmark -h 127.0.0.1 -p 6379 -c 200 -n 200000 -t lpop,sadd -P 16
- h : Redis 서버 호스트
- p : Redis 서버 포트
- c : 동시 연결 수 (Connections)
- n : 총 요청 수
- t : 벤치마크할 명령
- P : 파이프라인 깊이
기존 프로젝트의 선착순 응모 로직과 비슷한 환경을 Benchmark로 테스트해보자.
- 기존 프로젝트와의 차이
- 중복 처리 로직은 제외된 상태이다.
- 루아 스크립트를 활용한 구조여서 명령어를 한 번에 실행하기 때문에 중간 개입 없이 원자적으로 처리해 네트워크 왕복 비용이 적은 상태이다. 하지만 벤치마크 테스트에서는 lpop, sadd는 별도 요청으로 날아가 네트워크 왕복이 두 번 일어난다.
- 티켓 수가 한정되어 실제 명령이 동작하는 횟수가 훨씬 적은 반면 벤치마크에서는 그렇지 않다.
정확한 로직의 테스트 결과는 제이미터를 활용해서 해볼 것이고 러프하게 비교하기 위해 이 정도 오버헤드가 있겠구나를 알아보는 용도이다.
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
====== LPOP ======
200000 requests completed in 0.14 seconds
200 parallel clients
3 bytes payload
keep alive: 1
host configuration "save": 3600 1 300 100 60 10000
host configuration "appendonly": no
multi-thread: no
Latency by percentile distribution:
0.000% <= 0.655 milliseconds (cumulative count 16)
50.000% <= 1.791 milliseconds (cumulative count 100096)
//...
99.994% <= 7.311 milliseconds (cumulative count 200000)
100.000% <= 7.311 milliseconds (cumulative count 200000)
Cumulative distribution of latencies:
0.000% <= 0.103 milliseconds (cumulative count 0)
0.056% <= 0.703 milliseconds (cumulative count 112)
//...
99.920% <= 7.103 milliseconds (cumulative count 199840)
100.000% <= 8.103 milliseconds (cumulative count 200000)
Summary:
throughput summary: 1449275.38 requests per second
latency summary (msec):
avg min p50 p95 p99 max
1.921 0.648 1.791 2.759 5.839 7.311
====== SADD ======
200000 requests completed in 0.16 seconds
200 parallel clients
3 bytes payload
keep alive: 1
host configuration "save": 3600 1 300 100 60 10000
host configuration "appendonly": no
multi-thread: no
Latency by percentile distribution:
0.000% <= 0.887 milliseconds (cumulative count 16)
50.000% <= 2.167 milliseconds (cumulative count 100800)
//...
99.994% <= 5.543 milliseconds (cumulative count 200000)
100.000% <= 5.543 milliseconds (cumulative count 200000)
Cumulative distribution of latencies:
0.000% <= 0.103 milliseconds (cumulative count 0)
0.016% <= 0.903 milliseconds (cumulative count 32)
//...
99.984% <= 5.103 milliseconds (cumulative count 199968)
100.000% <= 6.103 milliseconds (cumulative count 200000)
Summary:
throughput summary: 1242236.00 requests per second
latency summary (msec):
avg min p50 p95 p99 max
2.238 0.880 2.167 3.183 4.135 5.543
- Throughput (처리량)
- LPOP: 1,449,275 req/sec
- SADD: 1,242,236 req/sec
- LPOP이 약간 더 빠른 모습을 볼 수 있다.
- latency (지연)
- LPOP
- avg: 1.921
- min: 0.648
- P50: 1.791
- P95: 2.759
- P99: 5.839
- max: 7.311
- SADD
- avg: 2.238
- min: 0.880
- P50: 2.167
- P95: 3.183
- P99: 4.135
- max: 5.543
- 전부 ms 기준으로 대부분 5ms 이내에 응답하는 것을 볼 수 있다.
- LPOP
- SADD가 평균적으로 더 느린 모습을 볼 수 있는데, SET 구조 특성상 내부에서 처리 비용이 추가로 발생하기 때문이다.
- LPOP은 O(1)의 간단한 연산이지만 한 번에 몰린 I/O 등이 간헐적 스파이크를 만들어 max나 P99의 소규모 집단에서 SADD보다 더 긴 지연을 확인할 수 있었다.
- 순수 Redis 서버의 요청 처리 능력을 확인한 결과로는 초당 140만건도 아무런 문제 없이 받아내는 것을 볼 수 있다. Redis의 성능이 얼마나 좋은지 확인할 수 있는 대목이다.
- 하지만 실제로는 HTTP/TCP 같은 애플리케이션 계층, 비즈니스 로직, 네트워크 RTT와 같은 요소가 포함되어야 하기 때문에 훨씬 낮은 처리량이 나올 것이다.
- 그리고 순수 LPOP, SADD 명령은 매우 가벼운 명령이기 때문에 이러한 처리량이 나온다. 실제 서비스 로직이 들어가면 그만큼 오버헤드가 늘어나게 도니다.
- 또, 로컬 단일 노드 기준의 성능일 뿐이고 실제 운영 환경에서는 클러스터 모드로 샤딩하거나 레플리카를 추가해 분산하는 방식을 사용한다. 따라서 각 환경을 고려한 비교 정도의 용도로 확인해보면 된다.
Redis Benchmark (AOF)
이제 AOF 모드에서의 성능을 확인해보자. RDB 성능보다 낮게 나올 것으로 예상한다.
1
2
3
CONFIG SET appendonly yes
BGREWRITEAOF
- SET appendonly yes: Redis 서버 설정을 런타임 중 바꿔 RDB 스냅샷 방식 대신 AOF 기반 모드로 전환한다.
- Redis는 이후 들어오는 모든 쓰기 명령을 appendonly.aof 파일에 순차적으로 기록하게 된다.
- 디스크에 AOF 파일을 남겨 갑작스럽게 서버가 종료되어도 데이터 유실을 최소화한다.
- SET, LPUSH, LPOP 등등의 명령이 기록된다.
- BGREWRITEAOF
- 누적된 AOF 로그를 압축 리라이트해 파일 크기를 최적화한다.
- 기존의 appendonly.aof 파일을 최적화(rewrite)해 현재 메모리 상태를 기반으로 최소한의 로그만 담은 새 AOF 파일을 생성한다.
- 내부적으로 Fork를 수행해 자식 프로세스를 만들고, 자식 프로세스가 메모리의 스냅샷을 읽어 현재 상태를 되살릴 수 있는 최소한의 명령 시퀀스를 기록한다. 기록이 완료되면 부모 프로세스가 기존의 appendonly.aof 파일을 새 파일로 교체한다.
1
INFO persistence
명령어를 통해 AOF 모드가 활성화되어있는지 확인이 가능하다.
1
CONFIG GET appendfsync
명령어를 통해 현재 정책을 확인할 수 있다. 현재 everysec으로 기본 설정이다.
1
redis-benchmark -h 127.0.0.1 -p 6379 -c 200 -n 200000 -t lpop,sadd -P 16
이제 같은 명령으로 실행해보자.
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
====== LPOP ======
200000 requests completed in 0.16 seconds
200 parallel clients
3 bytes payload
keep alive: 1
host configuration "save": 3600 1 300 100 60 10000
host configuration "appendonly": yes
multi-thread: no
Latency by percentile distribution:
0.000% <= 0.647 milliseconds (cumulative count 16)
50.000% <= 1.943 milliseconds (cumulative count 100560)
//...
99.994% <= 9.135 milliseconds (cumulative count 200000)
100.000% <= 9.135 milliseconds (cumulative count 200000)
Cumulative distribution of latencies:
0.000% <= 0.103 milliseconds (cumulative count 0)
0.080% <= 0.703 milliseconds (cumulative count 160)
//...
99.984% <= 9.103 milliseconds (cumulative count 199968)
100.000% <= 10.103 milliseconds (cumulative count 200000)
Summary:
throughput summary: 1273885.25 requests per second
latency summary (msec):
avg min p50 p95 p99 max
2.179 0.640 1.943 3.959 5.743 9.135
====== SADD ======
200000 requests completed in 0.17 seconds
200 parallel clients
3 bytes payload
keep alive: 1
host configuration "save": 3600 1 300 100 60 10000
host configuration "appendonly": yes
multi-thread: no
Latency by percentile distribution:
0.000% <= 1.055 milliseconds (cumulative count 16)
50.000% <= 2.231 milliseconds (cumulative count 100080)
//...
99.994% <= 6.359 milliseconds (cumulative count 200000)
100.000% <= 6.359 milliseconds (cumulative count 200000)
Cumulative distribution of latencies:
0.000% <= 0.103 milliseconds (cumulative count 0)
0.048% <= 1.103 milliseconds (cumulative count 96)
//...
99.952% <= 6.103 milliseconds (cumulative count 199904)
100.000% <= 7.103 milliseconds (cumulative count 200000)
Summary:
throughput summary: 1204819.38 requests per second
latency summary (msec):
avg min p50 p95 p99 max
2.363 1.048 2.231 3.719 4.559 6.359
- Throughput (처리량)
- LPOP: 1,273,885req/sec (1,449,275 req/sec)
- SADD: 1,204,819 req/sec (1,242,236 req/sec)
- latency (지연)
- LPOP
- avg: 2.179 (1.921)
- min: 0.640 (0.648)
- P50: 1.943 (1.791)
- P95: 3.959 (2.759)
- P99: 5.743 (5.839)
- max: 9.135 (7.311)
- SADD
- avg: 2.363 (2.238)
- min: 1.048 (0.880)
- P50: 2.231 (2.167)
- P95: 3.719 (3.183)
- P99: 4.559 (4.135)
- max: 6.359 (5.543)
- LPOP
- 예상대로 AOF 모드일 때 처리량은 감소하고, 지연은 증가함을 볼 수 있다.
- 기본 appendfsync everysec 정책인 경우 매 1초 1번 디스크 동기화가 발생한다.
- LPOP이 더 큰 폭으로 처리량이 줄었다.
- 부하가 적을 때 BGREWRITEAOF를 예약하는 방식으로 관리할 수 있다.
AOF always, no 테스트
- always
- LPOP: 1,470,588 req/sec
- avg: 1.836
- min: 0.664
- max: 4.767
- SADD: 1,351,351 req/sec
- avg: 2.048
- min: 0.808
- max: 4.599
- LPOP: 1,470,588 req/sec
- no
- LPOP: 1,587,301 req/sec
- avg: 1.747
- min: 0.680
- max: 3.847
- SADD: 1,265,822 req/sec
- avg: 2.258
- min: 1.272
- max: 4.271
- LPOP: 1,587,301 req/sec
각 모드별 결과를 알아보자.
- no 모드가 최고 처리량, 최저 지연을 가진다.
- 디스크 동기화가 없어 순수 Redis 연산 비용만 반영한다.
- 단, 당연히 데이터 유실 가능성이 높다.
- always 모드는 LPOP 처리량이 everysec 모드보다 높고, 평균 지연도 더 낮다.
왜 always 모드가 everysec 보다 성능이 더 좋았을까??
- 작게 나누어 동기화하는 효과가 있어 I/O 스파이크 방지가 되어 성능이 더 낫게 나왔을 수 있다.
- 벤치마크 테스트 특성상 노이즈가 발생할 수 있다.
정리하자면, 단순하게 안정성, 성능을 고려하기보다는 실제 운영환경에서는 각 연산별, 로직별로 상황에 따라 다를 수 있기 때문에 각각 어떻게 동작하는지 인지해두면 된다.
실제 서비스에서는 실제 서비스에서 수행하는 연산 패턴마다 AOF 정책이 어떻게 작용하는지 실제로 테스트해보고 최적의 방안을 생각하는게 최적의 방법일 것 같다.
BGREWRITEAOF 테스트
벤치마크 테스트 도중 BGREWRITEAOF를 실행해 성능에 어떤 영향을 끼치는지 확인해보자.
중간에 QPS가 0에 가깝게 떨어진 구간이 보인다.
리라이트 중 fork -> AOF 파일 쓰기 작업이 진행되면서 부모 프로세스가 잠시 블락되거나 우선순위가 뺏길 수 있다. 클라이언트 요청 처리량이 순간적으로 거의 0이 되는 모습을 볼 수 있다.
메모리 부족 테스트
이번엔 메모리가 부족할 때 Redis가 키를 어떤식으로 제거하고 그 과정이 성능에 어떤 영향을 끼치는지 확인해보자.
우선 maxmemory를 낮추고 정책을 설정한다.
1
2
3
4
5
CONFIG SET maxmemory 4mb
CONFIG SET maxmemory-policy $POLICY
$POLICY 부분엔 알맞은 POLICY 입력 -> allkeys-lru 기본
이후 벤치마크를 실행해 eviction을 확인해보자.
1
2
3
redis-benchmark -h 127.0.0.1 -p 6379 -c 50 -n 200000 -t set -d 1024 -r 200000
-d : 1024B 짜리 랜덤한 페이로드를 생성해 메모리 소비를 늘리기 위한 옵션
allkeys-lru
- 메모리 사용량 최대 4MB중 2.92MB 사용
- evictedKeys = 699
- 총 200,000개의 SET key value 요청을 했다.
- 메모리가 곧 차게 되면 한 번에 한 키씩 LRU 기준으로 제거하며 새로운 키를 받는다.
- 그 과정에서 699번의 키 제거가 일어났다.
volatile-lru, volatile-ttl
TTL이 설정된 키 기준으로 eviction을 적용한다.
설정만 바꾸고 같은 벤치마크를 실행했더니
1
Error from server: OOM command not allowed when used memory > 'maxmemory'.
이 에러를 보였다. 기존 벤치마크대로 수행하면 각 키에 TTL이 설정되지 않는다. 당연히 키 제거가 일어나지 않고 메모리를 초과한다는 에러를 보인 것이다.
allkeys-lfu
키의 사용 빈도 기준으로 제거 대상을 고른다.
결과는 당연히 키의 사용 빈도나 만료 시간이나 다 전체 키를 대상으로 하기 때문에 제거되는 키의 수는 같다.
단 지연율에서 큰 차이를 보였다.
LFU 알고리즘이 각 키의 접근 빈도를 카운팅하는 로직을 갖고, 그것을 바탕으로 선별해야하기 때문에 그 오버헤드에서 차이가 발생한 것 같다.
- avg: 0.468 -> 0.477
- min: 0.112 -> 0.104
- max: 12.895 -> 45.855
최대 지연율에서 큰 차이를 보였다.
클러스터 모드 구축하기
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
## Redis Cluster Node (Master 3, Replica 3)
redis-7000:
image: redis:7.2
hostname: redis-7000
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
volumes:
- ./cluster/redis-7000/redis.conf:/usr/local/etc/redis/redis.conf
- ./cluster/redis-7000/data:/data
ports:
- "7000:6379"
redis-7001:
image: redis:7.2
hostname: redis-7001
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
volumes:
- ./cluster/redis-7001/redis.conf:/usr/local/etc/redis/redis.conf
- ./cluster/redis-7001/data:/data
ports:
- "7001:6379"
redis-7002:
image: redis:7.2
hostname: redis-7002
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
volumes:
- ./cluster/redis-7002/redis.conf:/usr/local/etc/redis/redis.conf
- ./cluster/redis-7002/data:/data
ports:
- "7002:6379"
redis-7003:
image: redis:7.2
hostname: redis-7003
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
volumes:
- ./cluster/redis-7003/redis.conf:/usr/local/etc/redis/redis.conf
- ./cluster/redis-7003/data:/data
ports:
- "7003:6379"
redis-7004:
image: redis:7.2
hostname: redis-7004
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
volumes:
- ./cluster/redis-7004/redis.conf:/usr/local/etc/redis/redis.conf
- ./cluster/redis-7004/data:/data
ports:
- "7004:6379"
redis-7005:
image: redis:7.2
hostname: redis-7005
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
volumes:
- ./cluster/redis-7005/redis.conf:/usr/local/etc/redis/redis.conf
- ./cluster/redis-7005/data:/data
ports:
- "7005:6379"
## Sentinel (auto failover)
sentinel-26379:
image: redis:7.2
command: ["redis-server", "/usr/local/etc/redis/sentinel.conf", "--sentinel"]
volumes:
- ./sentinel/sentinel-26379/sentinel.conf:/usr/local/etc/redis/sentinel.conf
ports:
- "26379:26379"
sentinel-26380:
image: redis:7.2
command: ["redis-server", "/usr/local/etc/redis/sentinel.conf", "--sentinel"]
volumes:
- ./sentinel/sentinel-26380/sentinel.conf:/usr/local/etc/redis/sentinel.conf
ports:
- "26380:26379"
sentinel-26381:
image: redis:7.2
command: ["redis-server", "/usr/local/etc/redis/sentinel.conf", "--sentinel"]
volumes:
- ./sentinel/sentinel-26381/sentinel.conf:/usr/local/etc/redis/sentinel.conf
ports:
- "26381:26379"
기존에 사용했던 docker-compose 파일에 위와 같이 추가해주었다.
- command를 통해 Redis 서버를 시작할 때 내부 설정파일을 읽도록 지정한다.
- volumes를 통해 호스트의 설정 파일을 컨테이너 내부 경로로 마운트.
따라서 읽을 설정 파일이 필요하다.
docker-compose 파일이 존재하는 디렉토리에 각각 cluster, sentinel 폴더를 만든다.
- cluster
- redis-7000
- redis-7001
- …
- sentinel
- sentinel-26379
- sentinel-26380
- …
그리고 각 폴더에 설정파일을 넣으면 된다.
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
31
32
33
34
## redis.conf
# 기본 포트 (컨테이너 내부에서는 6379, 호스트와는 700X:6379 매핑)
port 6379
# 클러스터 모드 활성화
cluster-enabled yes
# 클러스터 노드 정보 저장 파일
cluster-config-file nodes.conf
# 노드 응답 타임아웃(ms)
cluster-node-timeout 5000
# AOF 영속화 활성화 (운영과 유사한 환경을 위해)
appendonly yes
# 스냅샷(RDB) 간격 설정 예시 (기본값 유지해도 OK)
# save 900 1
# save 300 10
# save 60 10000
# 보안 모드 해제 (로컬 테스트 전용)
protected-mode no
# 로그 출력 레벨 (optional)
# loglevel notice
# 동시 연결 제한 (optional)
# maxclients 10000
# 메모리 최대치 (optional)
# maxmemory 512mb
# maxmemory-policy allkeys-lru
- cluster-node-timeout 5000
- 마스터가 응답 없다고 판단하기까지 5초 대기한다.
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
## sentinel.conf
# Sentinel 기본 포트 (컨테이너 내부에서는 26379, 호스트와는 2638X:26379 매핑)
port 26379
# 모니터할 마스터 이름, 호스트, 포트, quorum(투표수)
sentinel monitor mymaster redis-7000 6379 2
# 마스터가 응답 없다고 간주할 시간(ms)
sentinel down-after-milliseconds mymaster 5000
# 페일오버 타임아웃(ms)
sentinel failover-timeout mymaster 10000
# 페일오버 시 동시 동기화할 replica 수
sentinel parallel-syncs mymaster 1
# 외부 접속 허용
protected-mode no
# 로그 파일(선택)
# logfile "/var/log/redis/sentinel-26379.log"
# Sentinel 리소스 제한(선택)
# maxmemory 128mb
- sentinel monitor mymaster redis-7000 6379 2
- mymaster라는 이름으로 redis-7000:6379 마스터를 모니터링하고 3대 Sentinel 중 2대가 다운 투표를 해야 페일오버가 시작된다.
- parallel-syncs
- 페일오버 후 레플리카들이 새 마스터에 동기화될 때 동시에 몇 개까지 동기화할지 설정한다.
- 한 번에 하나씩 순차 동기화해 마스터 부하를 최소화하고 동기화 실패 시 원인 파악이 쉽기 때문에 1로 설정한다.
- 클러스터 규모가 작기 때문에 동기화 대상 노드가 많지 않아 병렬 동기화의 이득이 크지 않다.
- 초기에 1로 두고 동기화 속도, 부하를 모니터링하며 필요할 때 늘리는 것이 일반적이다.
클러스터와 센티넬에 대해 다시 간단히 정리하자면
- Cluster
- 데이터 샤딩 및 고가용성 목적으로 슬롯 기반으로 키를 분산한다.
- 16,384개의 슬롯을 노드에 분산 배치해 같은 키는 항상 같은 슬롯에 저장한다.
- 노드를 추가/제거하면서 자동으로 슬롯을 재분배 가능하며 대량의 데이터를 여러 노드에 나누어 저장해 처리량을 확대한다.
- Sentinel
- 단일 마스터 환경에서 자동으로 장애를 감지하고 페일오버를 하는 목적
우선 클러스터, 센티넬 환경을 테스트하기 전에 이전에 했던 Jmeter 성능 테스트를 다시 해봤다.
분당 20만 트래픽을 10분간 받아본 결과이다. 이전 테스트 결과로는 분당 35만정도에서 확실한 실패라고 볼 수 있었다.
현재 에러가 조금 잡히는 이유는 다른 프로그램을 많이 켜뒀을 때 시스템 CPU 사용량이 치솟아 잠깐 잡혔다.