티스토리 뷰
포스팅 내용은 강대명님의 우아한 테크 세미나 내용을 정리 하였습니다
Redis
를 소개하기 전에 Cache
란?
Cache
의 정의
Cache
는 나중에 요청올 결과를 미리 저장해두었다가 빠르게 서비스 해주는 것을 의미
Cache
구조 #1 - Look aside cache
Web Server
는 데이터가 존재하는지Cache
를 연계 확인Cache
에 데이터가 있으면Cache
에서 가져온다Cache
에 데이터가 없다면 DB에서 얻어온다DB
에서 얻어온 데이터를Cache
에 다시 저장한다
Cache
구조 #2 - Write Back
Web Server
는 모든 데이터를Cache
에 저장Cache
에 특정 시간동안의 데이터가 저장Cache
에 있는 데이터를 DB에 저장한다.- DB에 저장된 데이터를 삭제한다.
Write Back
의 단점은 메모리에 저장이 되기 때문에 장애가 생기면 데이터가 사라질 가능성이 있다Log
를 DB에 관리 할 때 특정 시간동안의 로그를 모아 Batch 작업으로Insert Query
를 날려주는데Write Back
구조를 많이 사용한다
Redis
소개
Remote dictionary server
- In-Memory Data Structure Store
- Open Source
- Support data structures
- Strings, set, sorted-set, hashes, list
- Hyperloglog, bitmap, geospatial index
- Stream
왜 Collection
이 중요한가?
- 외부의
Collection
을 잘 이용하는 것으로, 여러가지 개발 시간을 단축시키고, 문제를 줄여줄 수 있기 때문에Collection
이 중요
개발의 편의성
랭킹 서버를 직접 구현한다면?
가장 간단한 방법
- DB에 유저
Score
를 저장하고Score
로order by
로 정렬 후 읽어오기 - 개수가 많아지면 속도에 문제가 발생할 수 있음
- 결국은 디스크를 사용하므로
- DB에 유저
Redis
의Sorted Set
을 이용하면, 랭킹을 구현 할 수 있음- 덤으로,
Replication
도 가능하다 - 다만 가져다 쓰면 거기에 한계에 종속덕이 됨
- 랭킹에 저장해야할 id가 1개당 100byte라고 했을 때
- 10명 1K
- 10000명 1M
- 10000000명 1G
- 10000000000 명 1TB
- 랭킹에 저장해야할 id가 1개당 100byte라고 했을 때
- 덤으로,
개발의 난이도
친구 리스트를 직접 구현한다면?
- 친구 리스트를 KEY/VALUE 형태로 저장해야 한다면?
- 현재 유저 123의 친구 Key는 friends:123, 현재 친구 A가 있는 중
- Transaction 1(친구 B를 추가)
- 친구 리스트 friends:123을 읽는다
- friends123의 끝에 친구 B를 저장한다
- 해당 값을 friend:123에 저장한다
- Transaction 1(친구 C를 추가)
- 친구 리스트 friends:123을 읽는다
- friends123의 끝에 친구 C를 추가한다
- 해당 값을 friend:123에 저장한다
Race Condition 해결
Race Condition이란 여러 개의 Thraed가 경헙하는 것, Context Switching에 따라 원하지 않는 결과가 발생
Redis
는 기본적으로Single Threaded
Redis
의 경우 자료구조가Atomic
하기 때문에, 해당Race Condition
을 피할 수 있습니다.- 그래도 잘못짜면 발생한다
- 서로 다른
Transaction Read/Write
를 동기화
Redis
는 실행했는데 어디에 써야 하죠?
Redis
사용처
- Remote Data Store
- A서버, B서버, C서버에 데이터를 공유하고 싶을 때
- 한대에서만 필요하다면, 전역 변수를 쓰면 되지 않을까?
Redis
자체가Atomic
을 보장해준다(Single Thread)
- 주료 많이 쓰는 곳들
- 인증 토큰 등을 저장(String or hash)
- Ranking Board(Sorted Set)
- User API Limit
- Job Queue(List)
Redis Collections
Strings
단일 Key
- 기본 사용법
Set <key> <value>
Get <key>
멀티 Key
- 기본 사용법
mset <key1> <value1> <key2> <value2> ..... <keyN> <valueN>
mget <key1> <key2> ..... <keyN>
Strings - 간단한 SQL을 대체한다면?
insert into users(name,email) values('minsoo','minsoo1604@naver.com')
- Using Set
Set name:minsoo minsoo
Set email:minsoo minsoo1604@naver.com
- Using mset
Mset name:minsoo minsoo email:minsoo minsoo1604@naver.com
List
insert
- 기본 사용법
Lpush <key> <A>
Key:(A)
Rpush <key> <B>
Key:(A,B)
Lpush <key> <C>
Key:(C,A,B)
pop
- 기본 사용법
Key:(C,A,B,D,A)
LPOP <key>
Pop C, Key:(C,B,D,A)
RPOP <key>
Pop A, Key:(A,B,D)
RPOP <key>
Pop D, Key:(A,B)
lpop,blpop,rpop,brpop
- 기본 사용법
Key:()
LPOP <Key>
No Data
BLPOP <key>
누가 데이터를 Push 하기 전까지 대기
Set
데이터가 있는지 없는지만 체크하는 용도
- 기본 사용법
SADD <key> <value>
value가 이미 key에 있으면 추가되지 않는다
SMEMBERS <key>
모든 value를 돌려줌
SISMEMBER <key> <value>
value가 존재하면 1, 없으면 0
Sorted Set
- 기본 사용법
ZADD <key> <Score> <value>
value가 이미 Key에 있으면 Score로 변경된다
ZRANGE <key> <StartIndex> <endIndex>
해당 Index 범위 값을 모두 돌려줌
Zrange testkey 0 -1
모든 범위를 가져옴
Hash
Key 밑에 sub key가 존재
- 기본 사용법
Hmset <key> <subkey1> <value1> <subkey2> <value2>
Hgetall <key>
해당 key의 모든 subkey와 value를 가져옴
Hget <key> <subkey>
Hmget <key> <subkey1> <subkey2> ..... <subkeyN>
간단한 SQL을 대체 한다면?
insert into users(name,email) values('minsoo','minsoo1604@naver.com')
hmset minsoo name minsoo email minsoo1604@naver.com
Collection
주의 사항
- 하나의 컬렉션에 너무 많은 아이템을 담으면 좋지 않음
- 10000개 이하 몇천개 수준으로 유지하는게 좋음
Expire
는Collection
의 item 개별로 걸리지 않고 전체Collection
에 대해서만 걸림- 즉 해당 10000개 아이템을 가진
Collection
에expire
가 걸려있다면 그 시간 후에 10000개의 아이템이 모두 삭제
- 즉 해당 10000개 아이템을 가진
Redis
운영
메모리 관리를 잘하자
Redis
는 In-Memory Data Store- Physical Memory 이상을 사용하면 문제가 발생
Swap
이 있다면Swap
사용으로 해당 메모리Page
접근시 마다 늦어짐Swap
이 없다면?
Maxmemory
를 설정하더라도 이보다 더 사용할 가능성이 큼- RSS 값을 모니터링 해야함
메모리 관리
- 큰 메모리를 사용하는
instance
하나보다는 적은 메모리를 사용하는instance
여러개가 안전함 Redis
는 메모리 파편화가 발생할 수 있음. 4.x 대 부터 메모리 파편화를 줄이도록jemlloc
에 힌트를 주는 기능이 들어갔으나,jemalloc
버전에 따라서 다르게 동작할 수 있음- 3.x 대 버전의 경우
- 실제
userd memory
는 2GB로 보고가 되지만 11GB의RSS
를 사용하는 경우가 자주 발생
- 실제
메모리가 부족할 때는?
- Cache is Cash
- 좀 더 메모리 많은 장비로
Migration
- 메모리가 빡빡하면
Migration
중에 문제가 발생할수도 있다
- 좀 더 메모리 많은 장비로
- 있는 데이터 줄이기
- 데이터를 일정 수준에서만 사용하도록 특정 데이터를 줄임
- 다만 이미
Swap
을 사용중이라면, 프로세스를 재시작 해야함
메모리를 줄이기 위한 설정
- 기본적으로 Collection 들은 다음과 같은 자료구조를 사용
Hash
->HashTable
을 하나 더 사용Sorted Set
->Skiplist
와HashTable
을 이용Set
->HashTable
사용- 해당 자료구조들은 메모리를 많이 사용함
Ziplist
를 이용하자
O(N) 관련 명령어는 주의하자
Redis
는 Single Threaded- 그러면
Redis
가 동시에 여러 개의 명령을 처리할 수 있을까?- 한번에 하나의 명령만 수행 가능
- 참고로 단순한
get/set
의 경우, 초당 10만 TPS 이상 가능(CPU 속도에 영향을 받습니다)
- 그러면
대표적인 O(N) 명령어
- KEYS
- FLUSHALL, FLUSHDB
- Delete Collections
- Get All Collections
대표적인 실수 사례
Key
가 백만개 이상인데 확인을 위해 KEYS 명령을 사용하는 경우- Spring security oauch RedisTokenStore
- 최신 버전에서는 사용 가능
KEYS 는 어떻게 대체할 것인가?
scan
명령을 사용하는 것으로 하나의 긴 명령을 짧은 여러번의 명령으로 바꿀 수 있다
Spring security oauch RedisTokenStore 이슈
Access Token
의 저장을 List(O(N)) 자료구조를 통해서 이루어짐- 검색,삭제시에 모든
item
을 매번 찾아봐야 함 - 100만개 쯤 되면 전체 성능에 영향을 줌
- 검색,삭제시에 모든
- 현재는 Set(O(1))을 이용해서 검색, 삭제를 하도록 수정되어있음
Replication
Async Replication
Replication Lag
가 발생할 수 있다.- Replicationof(>=5.0.0) or slaveof 명령으로 설정 가능
- Replicationof hostname port
DBMS
로 보면statement replication
가 유사
Replication 설정 과정
Secondary
에replicaof or slaveof
명령을 전달Secondary
는Primary
에 sync 명령 전달Primary
는 현재 메모리를 저장하기 위해- Fork
- Fork 한 프로세서는 현재 메모리 정보를
disk
에 dump - 해당 정보를
secondary
에 전달 - Fork 이후의 데이터를
secondary
에 계속 전달
Redis Replication 시 주의할 점
Replication
과정에서fork
가 발생하므로 메모리 부족이 발생할 수 있다Redis-cli--rdb
명령은 현재 상태의 메모리 스냅샷을 가져오므로 같은 문제를 발생시킴AWS
나 클라우드의Redis
는 좀 다르게 구현되어서 좀더 해당 부분이 안정적- 많은 대수의 Redis 서버가
Replica
를 두고 있다면- 네트워크 이슈나, 사람의 작업으로 동시에
replication
이 재시도 되도록 하면 문제가 발생할 수 있음
- 네트워크 이슈나, 사람의 작업으로 동시에
권장 설정 Tip
Maxclient
설정 50000RDB/AOF
설정 off- 특정 commands disable
- Keys
AWS
의ElasticCache
는 이미 하고 있음
- 전체 장애의 90% 이상이
EKYS
와SAVE
설정을 사용해서 발생 - 적절한
ziplist
설정
Redis
데이터 분산
- 데이터의 특성에 따라서 선택할 수 있는 방법이 달라진다
Cache
일때는우아한 Redis
Persistent
해야하면 안 우아한 Redis
데이터 분산 방법
- Application
- Consistent Hashing
twemproxy
를 사용하는 방법으로 쉽게 사용 가능
- Sharding
- Consistent Hashing
- Redis Cluster
결론
- 기본적으로
Redis
는 매우 좋은 툴 - 그러나 메모리를 빡빡하게 쓸 경우, 관리하기 어려움
- 32GB 장비라면 24GB 이상 사용하면 장비 증설을 고려하는 것이 좋음
Write
가 Heavy 할 때는Migration
도 매우 주의해야함
- Client-output-buffer-limit 설정이 필요
Redis as Cache
- Cache 일 경우는 문제가 적게 발생
Redis
가 문제가 있을 때 DB둥의 부하가 어느정도 증가하는지 확인 필요Consistent Hashing
도 실제 부하를 아주 균등하게 나누지는 않음.Adaptive Consistent Hashing
을 이용해 볼 수도 있음
Redis as Persistent Store
Persistent Store
의 경우- 무조건
Primary/Secondary
구조로 구성이 필요함 - 메모리를 절대로 빡빡하게 사용하면 안됨
- 정기적인
Migration
이 필요 - 가능하면 자동화 툴을 만들어서 이용
RDB/AOF
가 필요하다면Secondary
에서만 구동
- 정기적인
- 무조건
주의해야 할 점
Single Thread
서버 이므로 시간 복잡도를 고려해야 한다O(N)
명령 주의
In-memory
특성상 메모리 파편화, 가상 메모리등 이해가 필요하다
'Redis' 카테고리의 다른 글
[Spring Boot] Redis Cache TTL 이름별로 설정하는 방법 (0) | 2021.12.30 |
---|---|
[Spring Boot] Redis 연동 및 Cache 적용 예제 (0) | 2021.12.28 |
댓글