기타

Redis (Remote Dictionary Server)

ziwookim 2022. 10. 25. 01:57

Redis(Remote Dictionary Server)

Key-Value 형태의 NoSQL 인메모리(In-memory)데이터 구조 저장소로, 데이터베이스, 캐시, 메시지 브로커로 사용한다.

 

인메모리(In-memory)란?

컴퓨터의 메인 메모리 RAM에 데이터를 올려서 사용하는 방법을 말한다. 왜 메모리에 데이터를 올릴까? 이유는 명확하게도 속도 때문이다.  SSD,HDD 같은 저장공간에서 데이터를 가져오는 것보다 RAM에 올려진 데이터를 가져오는데 걸리는 속도가 수백배(HDD 기준) 이상 빠르다. 때문에 Redis는 빠른 속도가 큰 장점이다.

 

예를 들어, 게임의 랭킹 상위 100위를 보여주는 기능이 있다고 해보자. 랭킹 정보를 사용자에게 제공하기 위해 오라클같은 관계형 데이터베이스에 랭킹 정보를 저장을 하고 order by 로 불러올 수 있다. 그런데 사용자 수가 폭발적으로 증가해 수백만명으로 늘어나게 되면 어떻게 될까? 보여주는건 100명의 데이터뿐이지만 시간이 점점 오래 걸리게 될 것이다. 이렇게 수시로 값의 연산이 발생하고, 조회가 빈번한 '랭킹 정보', '유튜브 영상 조회수'와 같은 데이터를 처리하기에 캐시에 데이터를 담아두는 것은 매우 유리한 방법이다.

 

캐시(cache)

cache란, 자주 사용하는 데이터나 값을 미리 복사해 놓는 임시 장소를 말한다.

캐시에 있는 데이터는 시간과 자원 면에서 최소한의 비용으로 반복적으로 접근 할 수 있다.

 

Local Cache vs Global Cache

  • Local Cache
    • Local 장비 내에서만 사용 되는 캐시
    • Local 장비의 Resource를 이용한다 (Memory, Disk)
    • Local에서 작동 되기 때문에 속도가 빠르다.
    • Local에서만 작동되기 때문에 다른 서버와 데이터 공유가 어렵다

 

  • Global Cache
    • 여러 서버에서 Cache Server에 접근하여 사용하는 캐시
    • 데이터를 분산하여 저장 할 수 있다.
      • Replication - 데이터를 복제
      • Sharding - 데이터를 분산하여 저장
    • Local Cache에 비해 상대적으로 느리다 (네트워크 트래픽)
    • 별도의 Cache Server를 이용하기 때문에 서버 간 데이터 공유가 쉽다.

 

>> redis는 Global Cache에 적합함

 

redis 데이터 구조

 redis 는 key-value 형태의 데이터를 저장소이다. 즉, key 하나당 value를 저장하는 형태이다. redis는 단순히 string뿐만 아니라, 다양한 종류의 데이터 구조를 지원한다. 

  • 문자열(string)
  • 해시(hashes)
  • 리스트(list)
  • 집합(sets)
  • 정렬된 집합(sorted sets)
  • 비트맵(bitmap)
  • 하이퍼로그 로그(hyperloglog)
  • 지리공간 인덱스(geospatial indexes)
  • 스트림(streams)

 

 다양한 형태의 데이터 구조를 사용할 수 있다는 것은 큰 이점이다. 위에서 말한 상위 랭킹 100위의 정보를 저장해보자. redis가 제공하는 sorted set을 이용하면 (랭킹점수 순으로 sort) 편리하게 랭킹데이터를 관리 할 수 있다.

 

Java HashMap을 사용한다면?

private final Map<String, Object> map = new HashMap<>();

서버가 여러대인 경우, Consistency의 문제 발생. (ex) 세션 데이터)

멀티 쓰레드 환경에서 Race Condition(여러 개의 쓰레드가 경쟁하는 중에 Context Switching에 따라, 원치 않는 결과가 발생.) 문제 발생.

 

Redis를 사용한다면?

Redis는 기본적으로 싱글 쓰레드

Redis 자료구조는 Atomic -> Critical Section에 대한 동기화를 제공.

서로 다른 트랜잭션 Read/Write를 동기화.

그렇기 때문에 Redis를 사용한다면, Java HashMap을 사용했을 때의 'Consistency', 'Race Condition' 두 문제를 해결할 수 있다. 

 

Redis, 메모리 관리를 잘하자

 Redis는 메모리에 데이터를 저장하여 사용한다. 만약 물리적인 메모리보다 더 많은 용량을 사용하려고 한다면 문제가 발생하기 때문에 메모리 관리가 매우 중요하다.

 

메모리 SWAP 

 메인 메모리(RAM)가 부족할 경우 메모리에 올린 프로세스들이 부족한 공간을 해결하기 위해 하드디스크에 SWAP 공간을 만들어 임시 저장하게 된다. 메모리 부족을 해결하기 위한 좋은 방법이 될 수 있다. 하지만 치명적인 단점이 발생한다. 바로 SWAP을 하는동안 레이턴시가 발생하게 된다.

 보통 redis가 느려졌다면 RAM 용량이 부족해서 메모리 SWAP이 발생한 것이다. 만약, 사용 중인 redis에 메모리 SWAP이 발생했다면 계속해서 SWAP이 발생한다. 해결하기 위해서 프로세스를 재실행해야 한다.

 

메모리 단편화 혹은 파편화

 RAM에서 메모리의 공간이 작은 조각으로 나뉘어져 사용가능한 메모리가 충분히 존재하지만 할당이 불가능한 상태를 메모리 단편화라고 한다.

redis는 jmalloc을 사용하여 메모리를 할당한다. 예를들어 jmalloc이 메모리 페이지 사이즈를 4096byte로 잡으면 1byte만 저장하게 되어도 4096byte를 할당한다. 때문에 사용하지 않는 메모리 공간이 발생하게 된다. 때문에 개발자는 정확한 메모리 사용량을 파악하기 어렵다.

그렇다면 메모리 단편화 문제를 해결 할 수 있을까? 다양한 크기의 데이터 사용을 줄이고 유사한 크기의 데이터를 사용하면 메모리 파편화를 줄일 수 있다.

 

 

O(N) 관련 명령어는 지양하자

 주요 특징중 하나로 redis는 싱글 스레드(single thread)로 동작한다. 즉, 동시에 처리할 수 있는 명령어의 갯수는 한번에 하나이다.

# 실제 명령어 동작은 packet단위로 전달되며 packet이 쌓여 명령 command가 완성되면 명령을 실행한다.

왜 시간복잡도 O(N)을 사용하면 문제가 될까? 예를 들어, CPU 성능에 따라 차이가 있지만 가장 간단한 get/set 과 같은 명령어 기준 1초에 약 10만건의 명령어를 처리 할 수 있다. 그런데 동시에 10만건의 명령이 전달되었다. 그 중 첫번째 명령어가 3초가 걸린다면? 나머지  99,999건의 명령이 3초를 기다리게 되는 상황이 발생한다. 

  • KEYS
    • >> SCAN명령으로 끊어서 key값 조회

 

  • FLUSHALL, FLUSHDB
  • Delete Collections
  • Get All Collections
    • >> 컬렉션의 일부만 가져오기
    • >> 컬렉션을 한번에 저장하지 말고 나눠서 저장하기

 

위 명령어들이 대표적인 O(N) 명령어들로 꼭 필요한 경우가 아니라면 사용을 하지 않는 편이 좋다.

 

Replication

redis 서버는 단독으로 사용되지 않는다. 보통 영속성과 고가용성을 높이기 위해 클러스링 방식으로 많이 사용한다. Master-Replica(구; Slave라고 부름) 형태로 구성된다. Master의 데이터를 Replica로 복제한다.

 

비동기 복제(Async Replication)

 Replicaof(Slaveof) <hostname> <port> 명령어로 설정할 수 있다.

 async 복제로 Replication Lag이 발생할 수 있다.  예를 들어, Master 인스턴스에 <A,100>이 저장되어 있다. 그리고 Replica 인스턴스로 복제를 명령했다. 찰나의 순간 Master에 누군가 <B,200>을 저장하고 누군가 Replica 인스턴스에서 Key값을 조회했다면 A만 조회 될 수도 있다. Lag(컴퓨터 게임의 렉와 동일)이 발생 할 수 있다.

 

복제시 메모리가 부족하지 않은지 확인

Relicaof 명령시 fork가 발생한다. 예를 들어, 전체 메모리 용량이 8GB인 서버에서 Master가 사용중인 메모리 용량이 5GB라고 하면 fork로 순간적인 메모리 사용량이 10GB로 메모리 부족이 일어난다.

마찬가지로 Redis-cli –rdb 명령 (메모리덤프방식의 저장방법)을 사용시에도 메모리 스냅샷으로 메모리 사용량이 급증한다. 레디스는 인메모리 데이터 저장소다. 항상 메모리 관리에 주의해야한다.

 

출처:https://zangzangs.tistory.com/72