본문 바로가기
Back-end

Redis

by 신재권 2024. 1. 29.

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  (1) 2024.01.14