Redis Stream을 이해하기 위해서는 먼저 Redis에 대한 이해가 필요하다.
Redis는 고성능 키-값 저장소로서, NoSQL로 분류될 수 있다.
Key-Value 쌍의 해시맵 형태의 서버로서 별도의 쿼리 없이 Key를 통해 빠르게 결과를 가져올 수 있다는 장점이 있다.
Redis는 인메모리(RAM에 데이터 저장)라는 특성으로 작업 속도가 상당히 빠르다.
그러나, 서버가 다운되면 모든 데이터가 사라질 수 있다는 문제가 있는데 Redis에서는 이를 보완하기 위해 디스크에 데이터를 저장할 수 있는 두가지 방법을 제공한다.
스냅샷 방식으로, 일정 조건에 따라 메모리 내용을 바이너리 파일로 디스크에 저장한다.
# save [Seconds] [Changes]
save 900 1 # 900초(15분) 동안 1번 이상 key 변경이 발생하면 저장
save 300 10 # 300초(5분) 동안 10번 이상 key 변경이 발생하면 저장
save 60 10000 # 60초(1분) 동안 10000번 이상 key 변경이 발생하면 저장
기본값으로 redis.conf
에 위와 같이 설정되어있다.
RDB 저장 방식
stop-writes-on-bgsave-error yes
BGSAVE 이벤트 시 RDB 파일을 디스크에 저장하다 실패했을 경우, Redis에서 데이터를 받아들일지 말지를 결정하는 파라미터이다.
레디스 서버에 데이터의 업데이트 명령이 수행될 때마다, 해당 명령어의 로그를 디스크에 appendonly.aof
파일로 남긴다.
AOF는 non-blocking으로 동작하며, append-only이므로 write 속도가 빠르다. RDB와 달리 text 파일이므로 편집이 가능하다.
RDB와 AOF를 동시에 사용하면 데이터의 영속성을 보장할 수 있다. 하지만, AOF는 text파일이라 무겁기 때문에, redis.conf
에 용량을 제한하거나 rewrite 기능 등을 설정해야 한다.
appendonly yes #AOF 적용
appendfsync everysec #AOF에 기록되는 시점
# AOF 파일 사이즈가 64mb 이하면 rewrite를 하지 않는다.
auto-aof-rewrite-min-size 64mb
# AOF 파일 사이즈가 특정 퍼센트 이상 커지면 rewrite 한다.
# 비교 기준은 레디스 서버가 시작할 시점의 AOF파일 사이즈이다.
# 0으로 설정하면 rewrite를 하지 않는다
auto-aof-rewrite-percentage 100
appendfsync
AOF는 버퍼 캐시에 저장하고 적절한 시점에 이 데이터를 디스크로 저장하는데, appendfsync로 디스크와 동기화를 얼마나 자주 할 것인지 설정할 수 있다.
rewrite
AOF 파일의 상태가 특정 조건일 때 AOF 파일을 현재 상태에 맞춰, 설정에 따라 덮어쓰기 하거나 새로 생성하도록 한다.
Redis는 싱글 스레드를 사용해 Race Condition이 거의 발생하지 않는다는 장점이 있다. 하지만, 싱글 스레드는 멀티 스레드에 비해 속도의 한계가 명확하다.
이 문제를 해결 하기 위해 Redis 6.0에서는 ThreadedIO가 도입되어, 기존보다 속도가 약 2.5배 빨라졌다고 한다. 메모리 내부에서 명령의 실행 자체는 싱글 스레드로 동작하나,
에 ThreadedIO가 적용되었다.
꽤 어려운 개념이라, 자세한 내용은 아래의 글을 읽어보면 좋을 듯 하다.
https://charsyam.wordpress.com/2020/05/05/입-개발-redis-6-0-threadedio를-알아보자/
Redis는 String 뿐 아니라 다양한 데이터 구조체를 지원함으로써 DB, Cache, Message Queue, Shared Memory 용도로 사용될 수 있다.
https://redis.io/docs/interact/pubsub/
Redis Pub/Sub은 Redis 2.0에 추가된 레디스 구조체 중 하나로서, 단순한 브로드캐스팅 작업을 지원한다.
RabbitMQ나 Kafka와 달리, 메시지가 전달되는 Channel은 이벤트를 저장하지 않기 때문에, 이벤트가 도착했을 때 채널의 Subscriber가 없다면 이벤트는 증발한다.
https://redis.io/docs/data-types/streams/
Redis Stream은 Redis 5.0에 추가되었으며,
등 기존 Redis Pub/Sub에서 더 나아가 이벤트 브로커로서의 기능을 갖추었다.
레디스의 인메모리 & 싱글 스레드 특징으로, 테스트 결과 RabbitMQ와 Kafka처럼 대량의 메시지 처리보다는 적은 개수의 메시지를 신속하게 보내는 환경에 더욱 적합했다.
Redis Pub/Sub을 다룬 논문은 많았으나, Redis Stream은 선행 연구가 부족하여 이번 캡스톤 디자인의 목표로 삼았다. Redis Stream에 대한 더 자세한 설명은 다음 게시글에서 다룰 예정이다.
Redis Cluster는 데이터를 자동으로 여러 개의 Redis 노드에 나누어 저장할 수 있도록 한다. 따라서, 일부 노드가 죽거나 통신이 되지 않을 때 작업을 계속할 수 있다.
Redis는 싱글 스레드로 동작하기 때문에 성능을 올리기 위해서 Redis Cluster를 구성할 수도 있다.
1. 샤딩(Sharding)
샤딩은 "조각내다"라는 뜻으로 DB 저장 기법 중 하나이다. 데이터를 파티셔닝 하는 기술을 뜻한다.
Redis Cluster는 master를 여러개 두어 분산 저장(Sharding)이 가능하며, Scale out이 가능하다.
2. 가용성
예를 들어, master 1,2,3이 있다면 데이터는 3개 중 하나에 저장된다.
Client가 데이터 읽기 요청 시 저장된 곳이 아닌 다른 마스터에 요청했다면, 저장된 마스터 정보를 알려준다. Client는 전달받은 마스터 정보에 다시 요청해서 데이터를 받아올 수 있다.
이 때, 서버 1에 문제가 발생해 더 이상 통신이 되지 않는다면, 자동으로 서버2 or 3에 있는 replica 노드 중 하나가 master가 되어 작업을 계속할 수 있다.
따라서, 모든 서버가 고장날 때 까지 클러스터는 정상적으로 작동한다.
3대의 서버에 총 6개의 노드를 띄우는 예시이다.
1. 방화벽 해제
cluster 구성 시 cluster bus가 통신하는 포트는 각 노드 포트 + 10000번이므로 7000, 7001, 17000, 17001
번 포트의 방화벽을 해제한다.
❗cluster bus는 장애 감지, 구성 업데이트 failover 승인 등에 사용된다.
2. 메모리 사용량 overcommit & Accept 소켓 개수 변경
# 메모리 사용량이 허용량을 초과할 경우, overcommit을 처리하는 방식. "항상"으로 변경
# 실제 메모리가 사용되기 전, 요청된 메모리 양을 계산하여 요청 양보다 많은 메모리가 사용될 경우에만 할당
sudo sysctl vm.overcommit_memory=1
sudo echo "vm.overcommit_memory=1" >> /etc/sysctl.conf
sudo sysctl -a | grep vm.overcommit
# 서버 소켓에 Accept를 대기하는 소켓 개수 파라미터를 변경
sudo sysctl -w net.core.somaxconn=1024
sudo echo "net.core.somaxconn=1024" >> /etc/sysctl.conf
sudo sysctl -a | grep somaxconn
2. 디렉토리 생성
/opt/redis
디렉토리를 생성하여 하위에 7000, 7001 폴더를 생성한다.
/opt/redis/7000
, /opt/redis/7001
3. master 노드 conf 파일 생성
/opt/redis/7000/7000.conf
생성
bind 0.0.0.0
daemonize yes
protected-mode no
port 7000
pidfile /opt/redis/7000/redis_7000.pid
logfile /opt/redis/7000/redis-7000.log
#dir /opt/redis/7000
dbfilename dump_7000.rdb
requirepass [redis password]
masterauth [redis password]
cluster-config-file node-7000.conf
cluster-enabled yes
cluster-node-timeout 5000
cluster-announce-ip [서버 public ip] # NAT/포트포워딩 사용시
rename-command keys ""
appendonly no
yes
를 추가해야 클러스터 모드로 동작한다.no
로 설정 시 standalone 모드로 동작한다.4. slave 노드 conf 파일 생성
/opt/redis/7001/7001.conf
생성
bind 0.0.0.0
daemonize yes
protected-mode no
port 7001
pidfile /opt/redis/7001/redis_7001.pid
logfile /opt/redis/7001/redis-7001.log
#dir /opt/redis/7001
dbfilename dump_7001.rdb
requirepass [redis password]
masterauth [redis password]
cluster-config-file node-7001.conf
cluster-enabled yes
cluster-node-timeout 5000
cluster-announce-ip [서버 public ip]
rename-command keys ""
appendonly no
docker로 각 서버에 redis master & slave 실행
docker run -v /opt/redis/7000:/etc/redis/7000 -p 7000:6379 --name redis-master -d redis redis-server /etc/redis/7000/7000.conf
docker run -v /opt/redis/7001:/etc/redis/7001 -p 7001:6379 --name redis-slave -d redis redis-server /etc/redis/7001/7001.conf
각 노드가 잘 동작하는지는 로그로 확인할 수 있다.
/opt/redis/7000/redis-7000.log
서버 한 대에서 다음 명령어로 클러스터를 실행한다.
docker exec -it redis-master bash
redis-cli -a [비밀번호] --cluster create [서버1 IP]:7000 [서버2 IP]:7000 [서버3 IP]:7000
서버 한 대에서 slave 노드를 추가한다.
서버 2에는 서버 1의 slave, 서버 3에는 서버 2의 slave, 서버 1에는 서버 3의 slave를 둔다.
redis-cli -a [비밀번호] --cluster add-node [서버2 IP]:7001 [서버1 IP]:7000 --cluster-slave
redis-cli -a [비밀번호] --cluster add-node [서버3 IP]:7001 [서버2 IP]:7000 --cluster-slave
redis-cli -a [비밀번호] --cluster add-node [서버1 IP]:7001 [서버3 IP]:7000 --cluster-slave
https://velog.io/@nwactris/Docker-compose로-Redis-Cluster구성with-Predixy
Redis Cluster 앞의 Proxy 기능을 하는 Predixy까지 포함하여 따로 정리했다!
Redis Cluster Scale in/out에 대해서도 생각해 볼 수 있는 글이다.
(다 이해하긴 아직 어렵다)
https://tech.kakao.com/2022/02/09/k8s-redis/
https://inpa.tistory.com/entry/REDIS-📚-데이터-영구-저장하는-방법-데이터의-영속성
https://zangzangs.tistory.com/88
https://velog.io/@tngusqkr1/Redis-cluster-설정-1
https://co-de.tistory.com/24