https://www.youtube.com/watch?v=92NizoBL4uA
캐싱 이란?
데이터의 원래 소스보다 더 빠르고 효율적으로 액세스 할 수 있는 임시 데이터 저장소
- 대부분 애플리케이션에서 속도 향상을 위해 cache 사용
- 데이터의 재사용 횟수가 한 번 이상 이여야 의미가 있어짐
레디스 캐시로 사용하기
- 단순한 key-value 구조
- In-Memory 데이터 저장소(RAM)
- 빠른 성능
- 평균 작업 속도 < 1ms
- 초당 수백만 건의 작업 가능
캐싱 전략
캐싱 전략에 따라 시스템 성능에 영향을 미침
읽기 전략
- Look - Aside(Lazy Loading)
- 레디스에 찾는 키가 있다면 Cashe Hit
- DB를 거치지 않고, 레디스를 통해서만 데이터를 가져올 수 있음
- 레디스에 찾는 키가 없다면 Cashe Miss
- DB를 거쳐야 한다.
- DB에서 값을 가져와 레디스에 값을 캐싱해야 한다.
해당 구조는 레디스에 장애가 발생해도 DB에서 데이터를 가지고 올 수 있다.
- 캐시에 붙고 있던 커넥션들이 모두 데이터베이스로 붙기 때문에 DB에 많은 부하가 몰릴 수 있다.
캐시 미스가 발생하면 DB에서 레디스로 캐시를 저장한다.
- 성능 저하를 막기 위해 미리 DB에서 레디스로 데이터를 밀어 넣어주는 작업을 할 수도 있다.
- 이를 Cache Warming 이라 함
쓰기 전략
- Write-Around 방식
- DB에만 데이터를 저장하는 방식
- 모든 데이터는 DB에 저장한다.
- cache miss가 발생한 경우에만 cache에 데이터를 끌어오게 된다.
- cache와 DB의 데이터가 다를 수 있다는 단점이 존재
- Write-Through 방식
- DB에 데이터를 저장할 때 cache에도 함께 저장하는 방식
- cache는 항상 최신 정보를 가지고 있다는 장점이 존재
- 저장할 때마다 두 단계 스텝을 거쳐야 하기 때문에 상대적으로 느리다는 단점이 존재
- 저장하는 데이터가 재사용 되지 않을 수도 있음 → 리소스 낭비
- expire time 을 잘 설정해야 한다.
- 값을 어떻게 관리하느냐에 따라 장애 포인트가 될 수도 있음
레디스 데이터 타입
레디스는 자체적으로 다양한 자료구조를 제공
- Strings
- 기본적으로 저장되는 방식
- Bitmaps
- String의 변형이라고 볼 수 있음
- 비트 단위 연산이 가능
- Lists
- 데이터를 순서대로 저장
- 큐로 사용하기 적절
- Hashs
- 하나의 키 안에 여러 개의 필드와 밸류 쌍으로 데이터를 저장
- Sets
- 중복되지 않은 문자열의 집합
- Sorted Sets
- 중복되지 않은 값을 저장하지만, 모든 값은 score 라는 숫자 값으로 정렬된다.
- 데이터가 저장될 때 부터 score 순으로 정렬
- score가 같을 경우 사전 순으로 정렬
- HyperLogLogs
- 많은 데이터를 다룰 때 주로 쓴다.
- 중복되지 않은 값의 개수를 카운트 할 때 사용
- Streams
- log를 저장하기 좋은 자료구조
Counting
Strings
- 단순 증감 연산
- INCR / INCRBY / INCRBYFLOAT / HINCRBY / HINCRBYFLOAT / ZINCRBY
127.0.0.1:6379> set score:a 10
OK
127.0.0.1:6379> incr score:a
(integer) 11
127.0.0.1:6379> incrby score:a 4
(integer) 15
Bits
- 데이터 저장 공간 절약
- 정수로 된 데이터만 카운팅 가능
127.0.0.1:6379> SETBIT visitors:20210817 3 1
(integer) 0
127.0.0.1:6379> SETBIT visitors:20210817 6 1
(integer) 0
127.0.0.1:6379> BITCOUNT visitors:20210817
(integer) 2
HyperLogLogs
- 대용량 데이터를 카운팅 할 때 적절(오차 0.81%)
- set과 비슷하지만 저장되는 용량은 매우 작음(12KB 고정)
- 저장된 데이터는 다시 확인할 수 없음
127.0.0.1:6379> PFADD crawled:20211024 "<http://www.google.com/>"
(integer) 1
127.0.0.1:6379> PFADD crawled:20211124 "<http://www.redis.com/>"
(integer) 1
127.0.0.1:6379> PFCOUNT crawled:20211024
(integer) 1
127.0.0.1:6379> PFMERGE crawled:all crawled:20211024 crawled:20211023
OK
Messaging
Lists
- Blocking 기능을 이용해 Event Queue로 사용
- 레디스의 List는 메시지 큐로 사용하기 적절
- 자체적으로 blocking 기능을 제공
- 불필요한 polling을 막을 수 있음
CLIENT 1
127.0.0.1:6379> BRPOP myqueue 0
1) "myqueue"
2) "hi"
(9.66s)
CLIENT 2
127.0.0.1:6379> LPUSH myqueue "hi"
(integer) 1
- Client 1이 ‘myqueue’라는 list에서 첫번째 값을 뽑으려고 시도
- 값이 없어서 대기
- Client 2가 myqueue에 데이터 삽입
- 이때 client 1은 값이 생겼으므로 바로 값을 얻을 수 있음
- 키가 있을 때만 데이터 저장 가능 - LPUSHX / RPUSHX
- 이미 캐싱되어 있는 피드에만 신규 트윗을 저장
- 비효율적인 데이터의 이동을 막을 수 있음
- 이미 캐싱되어 있는 피드에만 신규 트윗을 저장
Streams
- 로그를 저장하기 가장 적절한 자료구조
- append-only
- 중간에 데이터가 바뀌지 않는다.
- 시간 범위로 검색 / 신규 추가 데이터 수신 / 소비자별 다른 데이터 수신(소비자 그룹)
127.0.0.1:6379> XADD mysream * sensor-id 1234 temperature 19.8
"1706453499828-0"
127.0.0.1:6379> XADD mysream * sensor-id 1234 temperature 20.4
"1706453505028-0"
* → key 값
* 로 사용할 시 레디스가 키를 저장하고 ID 값을 반환시켜 준다.
- ID는 저장된 시간을 나타낸다.
- 값 뒤로는 해시 처럼 key value 쌍으로 데이터가 저장된다.
- sensor-id : 1234
- temperature 19.8
ID 값을 이용해 시간 대역대로 저장된 값을 검색할 수도 있음
새로 들어오는 데이터만 리스닝할 수도 있음
카프카 처럼 소비자 그룹이라는 개념이 존재
- 원하는 소비자만 특정 데이터를 읽게 할 수도 있음
Redis에서 데이터를 영구 저장하려면? (RDB vs AOF)
Redis는 In-Memory 데이터 스토어
- 서버 재시작 시 모든 데이터 유실
- 복제 기능을 사용해도 사람의 실수 발생 시 데이터 복원 불가
- Redis를 캐시 이외의 용도로 사용한다면 적절한 데이터 백업이 필요
레디스에서는 데이터를 영구 저장하는 두 가지 방법을 제공
- AOF는 Append Only File의 약자
- 데이터를 변경하는 커맨드가 들어오면 커맨드를 그대로 모두 저장
- RDB는 스냅샷 방식으로 동작
- 저장 당시의 메모리에 있는 데이터 그대로를 사진 찍듯 찎어서 파일로 저장
AOF는 데이터가 추가되기만 하기 때문에 RDB 파일보다 커지게 된다.
- AOF 파일은 주기적인 압축이 필요
AOF는 레디스 프로토콜 형태로 저장
RDB는 바이너리 파일 형태로 저장
자동/수동 파일 저장 방법
RDB
- 자동 : redis.conf 파일에서 SAVE 옵션(시간 기준)
- 수동 : BGSAVE 커맨드를 이용해 cli 창에서 수동으로 RDB 파일 저장
- SAVE 커맨드는 절대 사용 x
RDB는 시간 단위로 저장 가능
AOF
- 자동 : redis.conf 파일에서 auto-aof-rewrite-percentage 옵션(크기 기준)_
- 수동 : BGREWRITEAOF 커맨드를 이용해 CLI 창에서 수동으로 AOF 파일 재작성
AOF는 크기 단위로 저장 가능
선택 기준
- 백업은 필요하지만 어느 정도의 데이터 손실이 발생해도 괜찮은 경우
- RDB 단독 사용
- redis.conf 파일에서 SVAE 옵션을 적절히 사용
- 장애 상황 직전까지의 모든 데이터가 보장되어야 할 경우
- AOF 사용(appendonly yes)
- APPENDFSYNC 옵션이 everysec 인 경우 최대 1초 사이의 데이터 유실 가능(기본 설정)
- 제일 강력한 내구성이 필요한 경우
- RDB & AOF 동시 사용
Redis 아키텍처 선택 노하우(Replication vs Sentinel vs Cluster)
Replication 구성
- 복제 구성
- 마스터와 리플리카만 존재하는 간단한 구조
- replicaof 커맨드를 이용해 간단하게 복제 연결
- 비동기식 복제
- HA 기능이 없으므로 장애 상황 시 수동 복구
- replicaof no one
- 애플리케이션에서 연결 정보 변경 후 배포
Sentinel 구성
- 마스터와 리플리카 노드 외에 센티널 노드가 필요
- 센티널은 일반 노드들을 모니터링 하는 역할
- sentinel 노드가 다른 노드 감시
- 마스터가 비정상 상태일 때 자동으로 페일 오버
- 연결 정보 변경 필요 없음
- sentinel 노드는 항상 3대 이상의 홀수로 존재해야 함
- 과반수 이상의 sentinel 이 동의해야 페일오버 진행
Cluster 구성
- 최소 세대의 마스터가 필요
- 샤딩 기능을 제공
- 스케일 아웃과 HA 구성(High Availability)
- 키를 여러 노드에 자동으로 분할해서 저장(샤딩)
- 모든 노드가 서로를 감시하여, 마스터 비정상 상태일 때 자동 페일 오버
- 최소 3대의 마스터 노드가 필요
아키텍처 선택 기준
사용하면 안되는 커맨드
Redis는 Single Thread로 동작
- keys * → scan 으로 대체
- Hash나 Sorted Set등 자료구조
- 키 내부에 데이터가 많아 질수록 성능 저하
- 키 나누기(최대 100만개)
- hgetall → hscan
- del → unlink : key를 백그라운드로 지워줌
변경하면 장애를 막을 수 있는 기본 설정 값
STOP-WRITES-ON-BGSAVE-ERROR = NO
- yes(default)
- RDB 파일 저장 실패 시 redis로의 모든 write 불가능
MAXMEMORY-POLICY = ALLKEYS-LRU
- redis를 캐시로 사용했을 때 expire time 설정 권장
- 메모리가 가득 찼을 때 MAXMEMORY-POLICY 정책에 의해 키 관리
- noeviction(default) : 삭제 안함
- valatile-lru
- 최근에 사용되지 않은 키부터 삭제
- expire 없는 키는 삭제 하지 않음
- allkeys-lru
- 모든 key에 대해 lru 방식 적용
Cache Stampede
TTL 값을 너무 작게 설정하면 cache stampede 현상이 발생할 가능성이 존재
MaxMemory 값 설정
Persistence/복제 사용 시 MaxMemory 설정 주의
- RDB 저장 & AOF rewrite 시 fork()
- Copy-on-Write로 인해 메모리를 두 배로 사용하는 경우 발생 가능
- Persistence/복제 사용 시 MaxMemory는 실제 메모리의 절반으로 설정
Memory 관리
물리적으로 사용되고 있는 메모리를 모니터링
- used_memory : 논리적으로 Redis가 사용하는 메모리
- used_memory_rss : OS가 Redis에 할당하기 위해 사용한 물리적 메모리 양
- 삭제되는 키가 많으면 fragmentation 증가
- 특정 시점에 피크를 찍고 다시 삭제되는 경우
- TTL로 인한 eviction이 많이 발생하는 경우
- CONFIG SET activedefrag yes
'Back-end' 카테고리의 다른 글
Spring HttpMessageConverter (0) | 2024.01.28 |
---|---|
Query DSL 성능 개선 (0) | 2024.01.20 |
Spring AutoConfiguration (0) | 2024.01.20 |
Spring Validation in Kotlin (0) | 2024.01.14 |
Spring @Async + ThreadPoolTaskExecutor (0) | 2024.01.14 |