ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 인메모리 데이터 저장소 Redis, 왜 사용할까? #Redis 파헤치기
    database/redis 2021. 2. 11. 21:42

    인메모리 데이터 저장소 Redis


     

    개발을 하다보면 트랜잭션이 많이 발생하는 화면에서 느껴지는 느림(?) 같은 것을 느끼곤 했다. 자주사용되는 하지만 세션에 담고 있기에는 뭔가 모호한 것들을 조회하기 위해 DB를 몇번이나 갔다와야 하니.. 어떻게 하면 속도를 줄일 수 있을지 고민했다. 처음에는 한방에 union all로 가져와? 같은 .. 별로인 생각도 해봤는데 검색하다 보니 redis라는 캐시? 데이터베이스?의 존재를 알게 되었다. 쭉 찾아보니 꽤나 유용한 기술이라고 생각되고 실제로 유명한 곳에서 많이 사용되고 있음을 알고 한번 정리해 보려고 한다. 

     

     

    소개


    redis의 어원

     redis는 단어의 의미에서 보면 외부에 key-value를 저장하는 서버를 말한다.

    redis.io 에는 다음과 같이 redis를 소개한다. Redis는 인메모리 데이터 구조 저장소로, 데이터베이스, 캐시, 메시지 브로커로 사용한다고 말한다.

     

    인메모리(In-memory)?


    RAM은 속도가 빨라!

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

    HDD가 용량이 더 크고 저렴해!

     redis는 빠른 속도를 자랑하는 대신 치명적인 단점이 있다. 바로 용량이다. 내 노트북만 봐도 RAM 용량은 8GB다. 그래서 메인 데이터베이스로 사용하기에는 무리가 있다. 그렇다고 RAM을 10TB씩 구매하자니 비용이라는 큰 걸림돌이 있다. 또 다른 특징으로는 Key-Value 형태의 NoSQL이라는 점이다. redis가 다양한 형태의 데이터 구조를 지원하기는 하지만 복잡한 데이터를 저장하는 데이터베이스로 사용하기에는 어려움이 있다.

     처음에는 오라클, MySQL과 같은 데이터베이스라고 생각하고 접근했으나 찾아보니 redis는 메인으로 사용하는 데이터베이스보다는 보조적인 수단으로 사용되는 경우가 많음을 알게 되었다. 정리하자면  redis의 장단점을 고려했을 때 가장 적합한 역할은 캐시 데이터베이스 서버인 것이다.

     예를 들어, 게임의 랭킹 상위 100위를 보여주는 기능이 있다고 해보자. 랭킹 정보를 사용자에게 제공하기 위해 오라클같은 관계형 데이터베이스에 랭킹 정보를 저장을 하고 order by 로 불러올 수 있다. 그런데 사용자 수가 폭발적으로 증가해 수백만명으로 늘어나게 되면 어떻게 될까? 보여주는건 100명의 데이터뿐이지만 시간이 점점 오래 걸리게 될 것이다. 이러한 문제를 해결하기 위해 캐시에 상위 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) 편리하게 랭킹데이터를 관리 할 수 있다. 

    찾아보니 redis와 비교되는 memcached라는 key-value 형태의 저장소가 있다. memcached는 우리가 알고 있는 데이터 형인 string, int, char[].. 등을 지원한다. memcached에 정렬된 랭킹정보를 저장하려면 개발자의 손길(?)이 필요해진다. 이처럼 다양한 데이터 구조를 지원하여 개발의 편의성과 개발의 난이도를 낮추는데 도움이 된다.

     

     

    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 명령 (메모리덤프방식의 저장방법)을 사용시에도 메모리 스냅샷으로 메모리 사용량이 급증한다. 레디스는 인메모리 데이터 저장소다. 항상 메모리 관리에 주의해야한다.

     

    Post


    References


     


    잘못된 코드나 내용이 있다면 댓글을 남겨주세요. 즉시 수정하도록 하겠습니다! :)

     

    댓글