skarltjr / Memory_Write_Record

나의 모든 학습 기록
0 stars 0 forks source link

레디스... 그리고 캐시 #140

Open skarltjr opened 1 year ago

skarltjr commented 1 year ago

1. 먼저 레디스란?

레디스란
Redis(레디스)는 Remote Dictionary Server의 약자로 오픈소스(BSD licensed) DBMS입니다.
In-memory(인메모리) 데이터 저장소이며, Key-Value기반의 NoSQL DBMS입니다.
보통 DB, Cache(캐시), 메시지 브로커 등의 용도로 사용합니다.

2. 레디스의 특징

  1. 싱글스레드 기반 명령 수행
    • 이를 통해 Atomic operations을 보장하며 Concurrency(동시성)를 지원합니다.
    • 반대로 말하자면 오랜 시간이 걸리는 명령어 사용이 레디스를 망친다.
  2. Key-Value 기반으로 데이터 저장
  3. 다양한 DataType 지원
    레디스는 비슷한 NoSQL DBMS대비 다양한 타입을 제공합니다.
    (strings, hashes, lists, sets, sorted sets,
    bitmaps, hyperloglogs, geospatial indexes, streams)
    이를 이용하여 개발 편의성을 극대화 할 수 있습니다.
    Lists의 경우 일반적인 RDB와 비교하면 10배 빠른 속도를 지원합니다.
  4. Master-Slave 구성이 가능
    • Master-Slave와 같은 Redis Replication 뿐만 아니라 Redis cluster를 이용한 분산처리, Redis Sentinel을 이용한 장애복구 시스템을 제공합니다.

3. 캐시의 배경

다만 데이터베이스에 disk i/o만 존재하는것은 아니다. 라이브러리 캐시를 통한 sql 캐싱, 버퍼 캐시를 통한 데이터 캐싱등이 존재하지만 disk i/o가 주가된다.

- 그렇다면 어떻게 이러한 문제를 개선할 수 있을까?

대부분의 서비스는 조회가 압도적으로 빈번 문제는 결국 이러한 조회를 개선하여 데이터베이스 부하를 줄여나가는것


## 4. 조회를 개선하는 방법
- DB의 인덱스 활용

인덱스를 설명할때 책의 목차를 많이 이야기하는데 실제로 인덱스의 사전적 의미와 원리가 목차와 동일. 인덱스는 별도의 공간에 할당되어 추가, 수정, 삭제 시에 함께 수정되기 때문에 저장된 데이터가 많고 인덱스의 개수가 적정해야 그 효용을 누릴 수 있다.

수행이 느린 쿼리를 찾아 인덱스를 추가하거나 인덱스가 걸려있는 쪽으로 조인 순서를 변경하여 쿼리 튜닝을 해볼 수 있다.

하지만 아무리 인덱스를 활용하여 빠른 색인을 하더라도 매번 동일한 요청에 대한 디스크 접근은 해결할 수 없다.

- 캐시 활용

많은 서비스에서 동일한 api를 유사 시간내에 재호출하는 경우는 50% 이상이라고한다.

짧은 시간내에 동일한 리소스를 접근할때마다 disk i/o가 발생한다는 이 문제는 디스크보다 빠른 메모리에 그 리소스를 얹혀놓고 재접근시 보다 빠른 메모리 i/o로 대응할 수 있다. 이를통해 응답시간과 데이터베이스 부하 분산 측면에서 이점을 살릴 수 있다.

하지만 당연하게도 영속성을 위해선 디스크 i/o를 거칠필요가있다 또한 성능이 우수하면 그만큼 비용은 비싸다. 비용과 성능 그 사이 trade off를 항상 고려해야한다.


## 5. 캐시데이터
- 반복적으로 동일한 결과를 반환하는 데이터

사실 캐시를 사용한다고해서 무조건 성능이 좋아지는건 당연히아니다. 캐시가 의미있기위해선 캐시 Hit가 중요하고 결국 매번 바뀌는 데이터를 캐싱하는것은 캐시 사용의 의미가 사라진다.

- ⭐️유실될 가능성이 있는 데이터

분명하게 주의해야할 부분이다. 캐시된 데이터는 유실될 가능성이 있다.

메모리는 속도가 빠르지만 가장 큰 특징 중 하나는 바로 휘발성이다. 물론 비휘발성 메모리도 존재하지만 성능적 측면에서 캐시로 활용하기엔 적합하지 않다. 이 말은 곧 휘발될 수 있는 데이터에 대해 대비해야한다. 백업 혹은 데이터베이스에 저장

- 캐시의 고장

캐시에 데이터를 캐싱해뒀다 캐시가 고장나는 경우도 존재할것이다. 그럼 캐시를 향하던 트래픽이 순간적으로 데이터베이스에 몰리고 장애의 범위가 커진다. 이를. cache stamped라고 한다.

## 6. 레디스와 영속
- 레디스의 영속방식에는 크게 두 가지 방법이 존재한다
- RDB (redis database) 

특정시점(snapshot)의 메모리에 있는 데이터 전체를 바이너리 파일로 저장

- AOF (append only file) 

서버가 수신한 모든 쓰기 작업 로그를 기록하고 서버 시작 시 로그를 기반으로 원본 데이터 세트를 재구성. 로그가 너무 커지면 백그라운드에서 다시 쓸 수 있음.

⭐️하지만 redis의 persistence 옵션을 사용하면 장애를 분명히 걱정해야하는데..
대부분의 레디스 장애의 원인은 메모리 부족이다.
persistence 옵션이 문제되는 이유는 데이터 영속과정에서의 성능저하 + COW ( copy on write )

### Copy on write

리눅스에서 새로운 프로세스를 생성하기 위해서는 프로세스 스스로를 복제(fork) 후 그 프로세스를 덮어쓴다. 즉 자식은 부모를 복제하는데..

이때 자식 프로세스는 기본적으로 부모 프로세스의 메모리 공간을 공유 ⭐️그런데 자식 프로세스가 생성된 시점에 부모 프로세스에 변경이 생기면 자식 프로세스와 공간을 공유할 수 없게 된다. 이때 부모 프로세스는 공유할 수 없는 데이터를! 메모리의 다른 공간에 복사!하고 이를 수정

즉 같은 데이터를 복사할수밖에 없는 상황으로 메모리 공간이 소모되는것이 copy on write

- <img width="731" alt="스크린샷 2023-01-18 오전 12 41 28" src="https://user-images.githubusercontent.com/62214428/212943170-2befa629-a8fb-4f0b-8fe2-d11f181d2a3f.png">

- 예시를 들어보자면

레디스 서버의 메모리 용량이 16GB이고 실제 데이터가 8GB 차있는 상태.

최악의 경우 레디스에 적재된 모든 데이터에 대해 cow가 발생하면 기존 데이터 8GB + 복사본 8GB => 16GB => 메모리 부족 => 레디스 장애



캐시 전략 : https://github.com/skarltjr/Memory_Write_Record/issues/127
참고 : https://iiaii.tistory.com/11