<목차>
- Redis 소개
- 왜 Collection이 중요한가?
- Redis Collections
- Redis 운영
- Redis 데이터 분산
- Redis Failover
<다루지 않을 것>
- Redis Persistence(RDB, AOF) : 메모리에 저장하는것. 실시간으로 사용 불가함
- Redis Pub/Sub
- Redis Stream
- 확률적 자료구조 : Hyperloglog
- Redis Module
1. Redis 소개
- In-Memory Data Structure Store
인메모리데이터이기 때문에 버그패치 등 직접 고쳐서 사용할 수 있지만, 기업용으로 사용할 경우에는 고치면 외부에 공개해야함. - Open Source(BSD 4 License)
- Support data structures
- Strings(key-value), set, sorted-set, hashes, list
- Hyperloglog, bitmap, geospatial index
- Stream
- Onlye 1 Committer - 레디스를 고칠수 있는 사람은 전세계에 한 명
Cache란? Cache를 왜 쓸까요?
나중에 요청을 결과에 미리 저장해두었다가 빠르게 서비스 해주는 것을 의미한다.
Factorial?
10! = 10 * 9! = 10 * 9 * 8! ...
20800!를 계산해두고 어딘가에 저장해뒀다가 20811! 계산은 금방한다. -> 다이나믹 프로그래밍처럼 생각하면된다.
CPU Cache
용량은 위로갈수록 크고
속도는 아래로갈수록 빠르다.
우리가 쓰는 레디스는 Memory에 있다.
어디서 많이 사용하나요?
웹 서비스 구조로 생각했을 때,
DB 도 내부적으로 캐시가 있긴하지만, 여러가지를 계속 접근하다보면 캐시를 계속 삭제하기 때문에 다시 또 디스크에 접근하는 경우가 발생한다.
Cache 구조 #1 - Look aside Cache
일반적으로 가장 많이 쓰는 형식
Cache 구조 #2 - Write Back
write 하는 데이터가 많은 경우에 사용한다.
쓰는 데이터가 너무 많을 경우 데이터를 Cache 에 저장해놓고 특정 시간마다 DB에 적재한다.
일반적으로 인서트 1번씩 인서트 500번을 날리는것보다, 한번에 500개를 붙여서 날리는게 말도안되게 빠르다. : 배치작업
Write Back의 단점 :
메모리에 저장되어있기 때문에, 리부팅하면 데이터 날아간다.
장애 발생할 경우 데이터 모두 날아갈 수도있다.
로그를 DB에 저장하시나요?
로그를 DB 에 저장할 때, 매번 데이터를 저장하지 않고 Write Back 을 사용하면 좋다. (
Redis에서는 Collection을 제공하는데 Collection이 왜 중요한지? 개발의 편의성, 개발의 난이도
- 라이브러리를 많이 제공하면 개발하기 편함
- 랭킹서버 등을 만들 때, Redis의 Sorted Set 을 이용하면 작업하기 편함
Redis Collections
개발할 때, 자료구조를 잘 선택하는 것이 중요하다.
- Strings : 키 - 밸류로 저장
- Set <key> <value>
- Get <key>
- List : head, tail 사용 가능하지만 중간 데이터 찾기 오래걸림
- Lpush <key> <A>
- Rpush <key> <B, C>
- LPOP <key>
- RPOP <key>
- Set : 중복 데이터 저장하지 않음. 데이터가 있는지 없는지만 체크하는 용도
- SADD <key> <Value>
- SMEMBERS <key> : 모든 VALUE 돌려줌
- SISMEMBERS <key> : 존재하면 1, 존재하지 않으면 0
- Sorted Sets : Score 로 정렬 가능
- ZADD <key> <Score> <value> : value 가 이미 존재하면 해당 Score 로 변경됨
- ZRANGE <key> <StartIndex> <EndIndex>, ZREVRANGE <key> <StartIndex> <EndIndex> : 해당 Index 범위값 모두 돌려줌. 유저 랭킹보드로 사용 가능. score 가 double 타입이기 때문에 값이 정확하지 않을 수 있는데, 실수가 표현할 수 없는 정수값들이 존재하기 때문.
- Hash : 키 - 밸류 구조 안에 서브 키 - 서브 밸류가 인서트 가능
Collection 주의 사항
- 하나의 컬렉션에 너무 많은 아이템을 담지 말고, 10,000개 이하 수준으로 유지하는게 좋음.
- 매번 몇만개씩 가져오면 성능 이슈 발생
- Expire는 Collection의 item 개별로 걸리지 않고 전체 Collection에 대해서만 걸림
- 즉 10,000개의 아이템을 가진 Collection에 대해 expire가 걸려있다면 그 시간 이후에 모두 같이 삭제
- 일부만 걸리도록 사용하는 사람들도 있긴한데 이렇게 하면 메모리를 너무 많이 사용한다.
Redis 운영
- 메모리 관리를 잘하자. 진짜진짜 중요
- Redis는 In-Memory Data Store
- Physical Memory 이상을 사용하면 문제가 발생
- Swap 이 있다면, Swap 사용으로 해당 메모리 Page 접근시마다 늦어짐. -> 디스크 사용
- Maxmemory를 설정하더라도 이보다 더 사용할 가능성이 큼.
- 특정 메모리 이상으로 사용하지 말라고 선언하는 옵션
- 레디스는 메모리 풀을 사용하는게 아니고, 메모리 할당/해제를 jemalloc 사용. jemalloc보다 더 잘 짜서 사용할 수 있는 사람은 없겠지만, 레디스가 정확하게 어느정도 메모리를 사용하고 있는지 알 수 없음. 해제했다고 하지만 실제로는 붙잡고 있는 경우도있음. : 메모리 파편화
- RSS 값을 모니터링 해야함
- 실제 used memory는 2GB로 보고가 되지만 1GB의 RSS를 사용하는 경우가 자주 발생
- 많은 업체가 현재 메모리를 사용해서 Swap을 쓰고 있다는 것을 모를때가 많음. -> 그래서 어느순간 레디스가 느려졌다고 생각함
- 큰 메모리를 사용하는 instance 하나보다는 적은 메모리를 사용하는 instance 여러개가 안전함.
- CPU 4 Core, 32GB Memory
- 24GB 하나보다는, 8GB 3개가 좋다
- 비슷한 사이즈의 메모리를 사용하는게 좋다.
- 레디스는 필연적으로 마스터-슬레이브 포크를 하게 되어있음
- 이 때, Read가 많은 경우는 괜찮은데, Write 가 헤비하게 일어나면 메모리를 최대 2배까지도 사용한다.
- 처음에 포크했을 때는 메모리를 복사하지 않는데, write하면 메모리 복사가 일어남
- CPU 4 Core, 32GB Memory
- 메모리가 부족할 때는?
- 돈이 많으면 좀 더 메모리 많은 장비로 Migration
- 돈이 없으면 데이터 줄이기
- 기본적으로 Collection 들은 다음과 같은 자료구조 사용
- Hash -> Hash Table 을 하나 더 사용
- Sorted Set -> Skiplist와 HashTable 을 이용
- Set -> HashTable 사용
- 위의 자료구조들은 메모리를 많이 사용함 (메모리 파편화, 포인터 등으로 메모리 많이 사용됨)
- Ziplist를 사용하자.
- In-Memory 이다보니, 적당한 사이즈 까지는 풀 서치를 하더라도 빠르게 사용할 수 잇다.
- O(N) 관련 명령어는 주의하자.
- Redis 는 Single Threaded
- 단순한 get/set의 경우, 초당 10만 TPS 이상가능(CPU속도에 영향받음)
- 하나라도 긴 시간을 요하는 명령이 걸리면 문제가 생김
- 대표적인 O(N)명령 : KEYS(모든 키 순회), FLUSHALL, FLUSHDB, Delete Collections, Get All Collections
Flush의 경우 명령어 호출했을 때는 멈추지만 필요할 땐 써야함- Keys 대신해서 scan명령을 여러번 나눠서 처리할 수 있다.
scan <키값>
- Keys 대신해서 scan명령을 여러번 나눠서 처리할 수 있다.
- Collection의 모든 item을 가져와야할 때?
- Collection의 일부만 가져오기 : Sorted Set
- 큰 Collection을 작은 여러개의 Collection으로 나눠서 저장. 하나 당 몇천개 안 쪽으로 저장.
- Redis 는 Single Threaded
- Replication
- A 라는 서버에 있는 데이터를 B 서버에서도 모두 동일하게 가지고있음. (Master - Slave, Original - Replicaof)
- Async Replication
- Replication Lag 이 발생할 수 있다.
- A서버 데이터를 B서버에서 복제하는 그 사이에 이슈가 발생할 수 있다.
- 시간차가 길어질 경우 연결을 끊어버림. 그리고 다시 마스터-슬레이브 연결을 시도하는데 이때 부하가 늘어남.
- Replication Lag 이 발생할 수 있다.
- DBMS로 보면 statement replication가 유사하다.
- Replication 설정 과정
- Secondary에 replicaof or slaveof 명령 전달
- Secondary는 Primary에 sync 명령 전달
- Primary 는 현재 메모리 상태를 저장하기 위해 - Fork
- Fork 한 프로세서는 현재 메모리 정보를 disk에 dump
- 해당 정보를 secondary에 전달
- Fork 이후의 데이터를 secondary에 계속 전달
- Replication 주의할점
- Replication 과정에서 fork 가 발생하므로 메모리 부족이 발생할 수 있다.
- Redis-cli --rdb 명령은 현재 상태의 메모리 스냅샷을 가져오므로 같은 문제를 발생시킴
- AWS나 클라우드의 Redis는 좀 다르게 구현되어서 좀 더 안정적
- 많은 대수의 Redis 서버가 Replica를 두고있다면
- 네트워크 이슈나 사람의 작업으로 동시에 replication이 재시도 되도록 하면 문제가 발생될 수 있다.
- redis.conf 권장 설정 Tip
- Maxclient 설정 50000
- RDB/AOF 설정 off : 속도 빠름. 안정성 높음.
- 특정 commands disable
- Keys
- AWS의 ElasticCache는 이미 하고있음
- 전체 장애의 99%가 Keys와 slave 설정을 사용해서 발생
- 적절한 ziplist 설정
레디스 데이터 분산
- 데이터의 특성에 따라서 선택할 수 있는 방법이 달라짐
- Cache 일때는 우아한 Redis
- Persistent 해야하면 안 우아한 Redis
데이터 분산 방법
- Application 레벨에서 분산
- Consistent Hashing : twemproxy를 사용하는 방법으로 쉽게 사용 가능
- 서버가 추가되거나 사라지면 이슈 발생
- Sharding
- 데이터를 어떻게 나눌것인가?
- 데이터를 어떻게 찾을것인가?
- 하나의 데이터를 모든 서버에서 찾아야하면?
- Range : 특정 Range를 정의하고 해당 Range 에 속하면 거기에 저장.
- 서버 별로 일하는 양이 불균형해진다. 불균형하더라도 서버의 상황에 따라서 놀고있는 서버와 일하고 있는 서버를 바꿀수도 없다.
- modular 로 처리할 때는 서버 대수를 2배로 늘려야 데이터 변환이 적고, 균등하게 분배할 가능성이 높다.
- Indexed 방법으로 진행할수도 있는데, Key 가 어디에 저장되어야할지 관리하는 서버가 필요.
- 대신, 키 관리 서버가 죽으면 사용불가.
- Consistent Hashing : twemproxy를 사용하는 방법으로 쉽게 사용 가능
- Rediscluster
- Hash 기반으로 Slot 16384로 구분
- Hash 알고리즘은 CRC16을 사용
- Slot = crv16(key) % 16384
- Key가 Key{hashkey} 패턴이면 실제 crc16에 hashkey가 사용된다.
- 특정 Redis 서버는 이 slot range 를 가지고 있고, 데이터 migration은 이 slot 단위의 데이터를 다른 서버로 전달하게된다. (migrateCommand 이용)
- 레디스 클러스터의 HA 는 기본적으로 일반적인 Redis 구조와 동일
Primary 가 죽으면 Secondary가 Primary 로 승격 (replication 구조로 이루어져있음)
- 각 슬롯으로 데이터를 넣어주는데 범위에 잘못된 데이터를 넣어주면 에러 리턴 및 몇번 슬롯으로 보내줘야하는지 알려줌
- 그리고 클라이언트가 맞는 슬롯에 맞게 다시 데이터 전달해줘야함
- 장점
- 자체적인 Primary, Secondary Failover.
- Slot 단위의 데이터 관리
- 단점
- 메모리 사용량이 더 많음
- Migration 자체는 관리자가 시점을 결정해야함
- Library 구현이 필요함
- Hash 기반으로 Slot 16384로 구분
- Redis Failover
- Coordinator 기반 Failover
- Zookeeper, etcd, consul 등의 Coordinator 사용
위와 같은 구조에서, Redis 1번이 죽으면, Health Checker는 Redis #2를 Primary 로 승격시킨다.
Health Checker는 Coordinator 에 current Redis가 Redis #2 라고 업데이트한다.
Coordinator는 API 서버에 current Redis 가 변경되었다고 알려준다.
코드로 모두 작성해두어야함 - 장점은, Coordinator 기반으로 설정을 관리한다면 동일한 방식으로 관리 가능
- Zookeeper, etcd, consul 등의 Coordinator 사용
- VIP / DNS 기반 Failover
- 클라이언트에 추가적인 구현이 필요없다.
- VIP 기반은 외부로 서비스를 제공해야하는 서비스 업자에 유리 (클라우드 업체 등)
- DNS 기반은 DNS Cache TTL을 관리해야함. (아마존에서 DNS 기반으로 처리중)
- 사용하는 언어별 DNS 캐싱 정책을 잘 알아야함
- 툴에 따라서 한번 가져온 DNS 정보를 다시 호출하지 않는 경우도 존재
- Redis Cluster의 사용
- Coordinator 기반 Failover
Monitoring Factor
- Redis Info를 통한 정보
- RSS - 가장 먼저 확인해야함. 실제 피지컬 메모리 얼마나 사용하고 있는지
- Used Memory - 레디스가 생각하는 메모리 사용량
- Connection 수 -레디스가 커넥션을 엄청 연결했다가 끊고 한다면 문제가 생겼음을 알수있다.
- 초당 처리 요청 수
- System
- CPU
- Disk
- Network rx/tx
CPU 가 100% 를 칠 경우
- 처리량이 매우 많다면?
- 좀 더 CPU 성능이 좋은 서버로 이전
- 실제 CPU 성능에 영향을 받음
- O(N) 계열의 특정 명령이 많은 경우
- Monitor 명령을 통해 특정 패턴을파악하는것이 필요
- Monitor 잘못쓰면 부하로 해당 서버에 더 큰 문제를 일으킬수도있음. (짧게 쓰고 분석)
결론
- 기본적으로 Redis는 매우 좋은 툴
- 그러나 메모리를 빡빡하게 쓸 경우, 관리하기 어려움
- 32기가 장비라면 24기가 이상 사용하면 장비 증설 고려
- Write 가 Heavy 할 때는 migration도 매우 주의
- Client-output-buffer-limit 설정이 필요.
- Cache일 경우는 문제 적게 발생
- Redis가 문제가 있을 때는 DB 등의 부하가 어느정도 증가하는지 확인 필요
- Consistent Hashing도 실제 부하를 아주 균등하게 나누지는 않음. Adaptive Consistent Hashing을 이용해볼 수도 있음.
- Persistent Store의 경우
- 무조건 Primary/Secondary 구조로 구성이 필요
- 메모리를 절대 빡빡하게 사용하면 안됨.
- 정기적은 migration이 필요. (Secondary에서 백업)
- 가능하면 자동화 툴 만들어서 이용
- RDB/AOF 가 필요하다면 Secondary에서만 구동. (AOF가 I/O가 균등하기 때문에 좀 더 안정적)
- 돈을 계속해서 들여서 관리하는거말고 방법이 없음.
'Developer > DataBase' 카테고리의 다른 글
MySQL 백업 도구 사용법 (mysqldump, xtrabackup, innobackupex) (0) | 2022.03.22 |
---|---|
MySQL 테이블 삭제하기 (DROP TABLE) (0) | 2021.08.12 |
MySQL 5.7 doens't yet support 'LIMIT & IN/ALL/ANY/SOME subquery' (0) | 2021.07.25 |
MySQL Query 추적 (0) | 2021.01.13 |
[MySQL] 백업 도구 - mysqldump (0) | 2021.01.03 |