티스토리 뷰

우아한 레디스

2019.11.21 우아한 형제들 테크 세미나

강대명 - 유데미 데이터 엔지니어

  1. Redis 소개

  2. 왜 Collection이 중요한가

  3. Redis Collection

  4. Redis 운영

  5. Redis 데이터 분산

  6. Redis Failover

  7. Monitoring Factor

  8. 결론

다루지 않는 것들

  1. Redis Persistence

  2. Redis Pub/Sub

  3. Redis Stream

  4. 확률적 자료구조

  5. Redis Modules

한줄 후기

Redis Intro와 사용법 내용은 압축과 생략으로 과감하게 진행하고, 경험에 의해 쌓인 redis 운영과 관리에 관한 이야기를 중점적으로 들을 수 있어서 매우 흥미로웠습니다.

모든 내용을 담진 못했으므로, 미흡한 부분은 테크 세미나 유튜브 채널에 공개될 영상을 참고하시면 좋을 것 같습니다.


P.S. 세미나 킬링파트 - 몸으로 이해하는 레디스(PT 30-40장 스킵 스킬, 발표시간 맞추기 스킬)

1. Redis 소개

  • In-Memory Data Structure Store

  • 오픈소스

  • Support data structures

    • strings, set, sorted-set, hashes, list

  • Only 1 Committer

  • 파레토 법칙 2:8

    • 전체 요청의 80%는 20%의 사용자가 만들어낸다.

  • 캐시구조

    • Look aside Cache

      • 캐시에 있으면 리턴, 없으면 캐시에 저장하고 리턴

    • Write Back

      • 모든 데이터를 캐시에 저장, 특정 시점에만 캐시 데이터 DB에 저장

      • ex) 로그를 캐시에 쌓아두고, 특정 시점마다 DB로

2. 왜 Collection이 중요한가

  • 개발의 편의성

  • 개발의 난이도

  • 랭킹 서버를 구현한다면?

    • 간단한 방법은 DB에 넣고 score로.

    • 유저가 많다면?

    • Redis Sorted Set을 이용하면 랭킹을 구현할 수 있음.

    • 덤으로 Replication도 가능

  • 친구리스트를 key-value 형태로 저장해야 한다면?

    • 레디스의 경우 자료구조가 Atomic하기 때문에 Race Condition을 피할 수 있음.

  • 외부의 Collections을 잘 이용하는 것으로 우리가 비즈니스 로직에 집중할 수 있음. 시간 단축, 문제를 줄여줌.

  • Redis 사용처

    • Remote Data Store

      • A, B, C 에서 데이터를 공유하고 싶을때

    • 한대에서만 필요하다면, 전역 변수를 쓰면 되지 않을까?

      • Redis 자체가 Atomic을 보장해준다.(싱글 스레드라서)

    • 주로 많이 쓰이는 곳들

      • 인증 토큰 등을 저장

      • 랭킹

      • 유저 API limit

      • 좌표

3. Redis Collections

  • 데이터 컬렉션을 잘못 선택해서 속도 차이가 날 수 있음.

  • Strings

    • Key-value

    • 사용법은 생략

    • Key를 어떻게 잡을 것인가가 중요하다.

      • 키를 어떻게 잡느냐에 따라서 분산이 바뀔 수 있다.

  • List

    • Lpush, Rpush : 시작과 끝이 데이터 추가 가능

    • LPOP, RPOP : 마찬가지로 시작과 끝 꺼내기 가능.

  • Set

    • 중복 제거, 순서 없음. find 빠름

    • SADD, SMEMBERS, SISMEMBERS

    • 특정 유저를 Follow 하는 목록을 저장해 둔다면

  • Sorted Set

    • score를 포함한 순서가 있는 Set

    • ZADD <Key> <Score> <Value>

    • 범위를 대상으로 데이터 가져올 수 있음

      • zrange

    • 유저 랭킹 보드로 사용할 수 있음

    • (주의) Sorted set의 score는 double 타입이기 때문에 값이 정확하지 않을 수 있다.

      • score는 실수형이기 때문에 실수하기 쉽다.(?ㅎ)

      • 어떤 특정 정수는 실수에서 표현이 안된다.

      • 컴퓨터에서는 실수가 표현할 수 없는 정수값들이 존재한다.

      • 자바스크립트에서 유저 ID를 보낸다면 Long 정수형으로 보내면 안된다. String으로 보내야 정상적으로 동작한다.

  • Hash

    • Key Value 안에 sub key value가 존재하는 형태

  • Collection 주의 사항

    • 하나의 컬렉션에 너무 많은 아이템을 담으면 좋지 않다.

      • 1만개 이하로 유지하는게 좋음

    • Expire는 Collection의 아이템 개별로 걸리지 않고 전체 Collection에 대해서만 걸린다.

4. Redis 운영

메모리 관리를 잘하자

  • Physical 메모리 이상을 사용하면 문제가 발생한다.

    • Swap이 있다면 Swap 사용으로 해당 메모리 Page 접근시 마다 늦어진다.

    • Swap 이 한번 발생하면 계속 발생한다. 디스크 사용으로 인해 느려진다.

    • Swap이 없다면? OOM으로 죽을 수 있다.

  • Maxmemory를 설정하더라도 이보다 더 사용할 가능성이 크다.

    • 레디스는 메모리 allocator에 의존하는데. jemaloc

    • 1바이트만 달라고해도 jemaloc는 페이지 단위에 의해서 메모리를 주므로 4096을 할당한다.

    • 메모리 파편화가 일어나면, 레디스 사용량과 jemaloc 할당량이 달라진다.

  • RSS

  • 많은 업체가 현재 메모리를 사용해서 Swap을 쓰고 있다는 것을 모를때가 많다.

  • 큰 메모리를 사용하는 instance 하나보다는 적은 메모리를 사용하는 instance 여러개가 안전하다.

    • 레디스는 Fork를 하게 되는데, read만 하면 상관없지만, write를 하게되면 최대 2배 사용한다.

    • 예를 들면 4코어 24기가 한대가 2배 사용하면 48기가 사용하지만,

    • 8기가 3대에서 1대가 2배를 사용하게 되면, 32기가만 사용하게 된다.

  • 레디스는 메모리 파편화가 발생할 수 있음. 4.x대 부터...

  • 메모리가 부족할 때는?

    • Cache is Cach!!!

      • 좀 더 메모리 많은 장비로 마이그레이션. 시점은 75% 정도

      • 메모리가 빡빡하면 마이그레이션 중에 문제가 발생할 수도

    • 아니면, 데이터 줄이기..

  • 메모리를 줄이기 위한 설정

    • 기본적으로 Collection 들은 아래와 같은 자료구조 사용

      • Hash -> HashTable 하나 더 사용

      • Sorted Set -> Skiplist 와 HashTable 사용

      • Set -> HashTable 사용

      • 해당 자료구조들은 메모리를 많이 사용함

    • ziplist를 사용하자

      • 선형으로 저장한다.

      • 인메모리 특성상 적은 개수라면 선형 탐색을 하더라도 빠르다.

      • List, Hash, Sorted Set을 ziplist로 대체해서 처리하는 설정이 존재한다.

        • 30% 정도까지 적게 사용 가능하다.

O(N) 관련 명령어는 주의하자

  • 레디스는 Sigle Threaded

    • 동시에 여러개의 명령을 처리할 수 있을까?

    • 참고로 단순한 get/set의 경우 초당 10만 TPS 이상 가능하다(CPU영향 가능)

    • 작업 하나가 1초 걸리면? 300ms 타임아웃 설정되어있으면 터진다.

    • Pocket으로 하나의 Command가 완성되면 processCommand로 처리함

    • 한번에 하나의 명령을 처리하므로 오래걸리는 명령을 수행하면 안된다.

    • 대표적인 O(N) 명령들

      • KEYS

      • FLUSHALL, FLUSHDB

      • Delete Collections

      • Get All Collections

  • 대표적인 실수 사례

    • Key가 백만개 이상인데 확인을 위해 KEYS 명령을 사용하는 경우

      • 모니터링 스크립트가 1초에 한번씩 호출

    • 아이템이 몇만개든 hash, sorted set, set에서 데이터를 가져오는 경우

    • Spring security oath RedisTokenStore 이슈

  • KEYS는 어떻게 대체할 것인가?

    • Scan 명령을 사용하는 것으로 하나의 긴 명령을 짧은 여러번의 명령으로 바꿀 수 있다.(cursor 방식)

    • 짧은 명령들 사이에 read 몇만개가 처리될 수 있다.

  • Collection의 모든 아이템을 가져와야 할 때?

    • Collection의 일부만 가져오거나

    • 큰 Collections을 여러개의 Collection으로 나눠서 저장

  • Spring security oath RedisTokenStore 이슈

    • Access Token의 저장을 List 자료구조를 통해서 이루어졌었음

    • 검색, 삭제시에 모든 item을 매번 찾아야함

    • 최신 버전에서는 Set(O(1))로 해결 함.

Replication

  • Async Replication

    • Replication lag이 발생할 수 있다.

    • 리플리케이션 하는 틈 사이에 데이터가 바뀔 수 있다.

  • 'Replicaof'(5.0.0 버전 이상) 명령으로 설정 가능

    • Replicaof hostname port

  • DBMS로 보면 statement replication과 유사

    • DB에서 now는 1, 2가 다를 수 있음.

  • Replication 설정 과정

    • Replicaof 명령 전달

    • Secondary는 Primary에 sync 명령 전달

    • Primary는 현재 메모리 상태 저장하기 위해 Fork

    • ... 생략

  • 주의할 점

    • Replication 과정에서 Fork가 발생하므로 메모리 부족이 발생할 수 있다.

    • Redis-cli --rdb 명령은 현재 상태의 메모리 스냅샷을 가져오므로 문제를 발생시킴. 치명적

    • AWS나 클라우드의 Redis는 좀 다르게 구현되어서 해당 부분이 안정적

    • 많은 대수의 Redis 서버가 많은 Replica를 두고 있다면?

    • 네트웍 이슈나, 사람의 작업으로 동시에 replication이 재시도 되도록 하면 문제가 발생할 수 있다.

  • 권장 설정 Tip

    • Maxclient 설정 50000

    • RDB/AOF 설정 off

      • 마스터에서는 무조건 off

    • 특정 commands disable

      • Keys

      • AWS의 ElasticCache는 이미 하고 있음

    • 전체 장애의 99%는 KEYS나 SAVE설정을 사용해서 발생

      • SAVE 주의.

    • 적절한 ziplist 설정

5. Redis 데이터 분산

  • 데이터의 특성에 따라서 선택할 수 있는 방법이 달라진다.

  • Consistent Hashing

    • 서버가 추가되거나 빠지더라도 해당 서버에 있는 데이터들만 리밸런싱이 일어남.

    • 1,2,3 번 서버중에 2번 죽으면 2번에 있는 데이터들만 이동.

  • Sharding

    • 데이터를 어떻게 나눌것인가? 는 데이터를 어떻게 찾을것인가? 와 같다.

    • 하나의 데이터를 모든 서버에서 찾아야 하면?

    • Range

      • 특정 Range를 정의 하고 해당 Range에 속하면 거기에 저장.

      • 불균형이 생긴다. 이벤트 처리 할때 많이 발생한다.

      • 확장은 편한데, 서버 상황에 따라서 놀고 있는 서버와 열일하는 서버가 나뉜다.

    • Modular

      • 서버 한대 추가할 때 재분배 양이 많아 진다.

    • Indexed

      • 해당 Key가 어디에 저장되어야 할 관리 서버가 따로 존재.

      • 단일 장애 포인트 문제 존재.

  • Redis Cluster

    • Hash 기반으로 slot 16384로 구분

      • Hash 알고리즘은 CRC16을 사용

      • 특정 Redis 서버는 이 slot range를 가지고 있고, 데이터 마이그레이션은 이 slot 단위의 데이터를 다른 서버로 전달하게 된다.(migrateCommand 사용)

    • 장점

      • 자체적인 Failover 기능

      • slot 단위의 데이터 관리

    • 단점

      • 메모리 사용량이 더 많다

      • 마이그레이션 자체는 관리자가 시점 결정해야 함

      • 라이브러리 구현이 필요함.

6. Redis Failover

  • Coordinator 기반

    • Zookeeper, etcd, consul 등의 Coordinator 사용

    • Health checker

    • Coordinator가 API서버에 Primary notify 해줌

    • 해당 기능을 이용하도록 개발이 필요하다.

  • VIP 기반

    • API 서버는 가상 IP 10.0.0.1로만 접속하고

    • Health checker는 10.0.0.1 할당 받은 1번 서버 레디스가 죽으면 10.0.0.1을 redis 2번 서버에 할당

  • DNS 기반

    • VIP 기반 방식과 같음.

  • VIP/DNS 특징

    • 클라이언트에 추가적인 구현이 필요없다.

    • VIP 기반은 외부로 서비스를 제공해야 하는 서비스 업자에 유리

    • DNS 기반은 DNS cache 를 관리해야함

7. Monitor Factor

  • Redis Info를 통한 정보

    • RSS

    • Used Memory

    • Connection 수

    • 초당 처리 요청 수

  • System

    • CPU

    • Disk

    • Network rx/tx

  • CPU 100%를 칠 경우

    • 처리량이 매우 많다면?

      • CPU 성능 좋은 서버로 이전

      • 실제 CPU 성능에 영향을 받음

        • 그러나 단순 get/set은 초당 10만 이상 처리 가능

    • O(N) 계열의 특정 명령이 많은 경우

      • Monitor 명령을 통해 특정 패턴을 파악하는 것이 필요

      • Monitor 잘못쓰면 부하로 해당 서버에 더 큰 문제를 일이킬 수도 있음(짧게 쓰는게 중요함)

8. 결론

  • 레디스는 기본적으로 매우 좋은 툴이다

  • 그러나 메모리를 빡빡하게 쓸 경우 관리가 어렵다.

    • 32기가 장비면 24기가 이상 사용하면 장비 증설을 고려하는 것이 좋음

    • Write가 Heavy할 경우 마이그레이션도 주의해야 함.

  • 레디스를 Cache로 쓰는 경우 - 문제가 적게 발생

    • 레디스가 문제가 있을 때 DB등의 부하가 어느정도 증가하는지 확인 필요함

    • Consistent Hashing도 부하를 아주 균등하게 나누지는 않음

  • 레디스를 Persistent Store로 쓰는 경우

    • 무조건 Primary/Secondary 구조로 구성이 필요함

    • 메모리를 절대로 빡빡하게 사용하면 안됨

      • 정기적인 마이그레이션 필요

      • 가능하면 자동화 툴 만들어 사용

    • RDB/AOF가 필요하다면 Secondary에서만 구동







'Experience > 2019' 카테고리의 다른 글

NHN FORWARD 2019 후기  (0) 2019.11.28
AWS SUMMIT SEOUL 2019 2일차(04/18) 참관기  (0) 2019.04.19
댓글