ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Redis] Redis를 LRU 캐시로 사용하기
    프로그래밍/DBMS 2018. 10. 1. 14:46
    Redis를 LRU 캐시로 사용하기

    Redis를 LRU 캐시로 사용하기

    Redis를 캐시로 사용할 경우 새로운 데이터를 입력할 때 오래된 데이터를 자동적으로 후위 저장장치로 전송하는데 용이하다. 이러한 동작은 memcached 시스템과 동일하다.

    LRU는 Redis에서 지원하는 캐시 리플레이스먼트 알고리즘 중 하나이다. 본 문서에서는 Redis에서 고정된 양의 메모리만 사용하도록 하는 maxmemory 명령에 대해서 알아보고, 유사 LRU인 Redis의 LRU 알고리즘에 대해서 알아본다.

    Redis 4.0부터 LFU (Least Frequently Used) 알고리즘이 등장하였고, 본 문서의 마지막에 다룬다.

    Maxmemory 설정 명령

    maxmemory 는 Redis가 사용하는 메모리 중 데이터 셋을 위한 메모리의 양을 특정 크기로 지정하는 명령이다. redis.conf 파일을 통해 지정 가능하며, config set 명령어를 통해 런타임 중 변경도 가능하다.

    예를 들어, maxmemory 100mb를 redis.conf에 선언하는 것으로 Redis의 최대 메모리를 100 메가바이트로 제한할 수 있다.

    maxmemory를 0으로 지정할 경우 메모리 사용량에 제한이 없음을 의미하며, 64비트 시스템에서 Redis의 기본 설정값이다. 32bit 시스템에서는 3GB로 제한된다.

    최대 메모리까지 모두 사용한 경우 캐시 교체 정책에 따라 동작이 달라 진다. 단순하게는 메모리를 추가로 요구하는 명령에 대해서 에러를 반환하는 것부터, 새로운 데이터가 추가 될때 마다 오래된 데이터를 교체하는 방식까지 다양한 정책이 존재한다.

    캐시 교체 정책

    maxmemory 사용량에 도달한 경우 Redis의 동작을 조절하기 위해서는 maxmemory-policy 옵션을 설정하면 된다. 사용 가능한 정책은 아래와 같다:

    • noeviction: 추가 메모리를 요하는 명령에 대해서 에러를 반환한다.
    • allkeys-lru: LRU 방식으로 키를 선정한다.
    • volatile-lru: expire set 포함된 키들 내에서만 LRU 방식으로 선정한다.
    • allkeys-random: 랜덤한 키를 선정한다.
    • volatile-random: expire set에 포함된 키들 내에서 랜덤한 키를 선정한다.
    • volatile-ttl: expire set에 포함된 키들 중에서 가장 짧은 TTL (time to live)를 가진 키를 선정한다.

    volatile-lru, volatile-randomvolatile-ttl을 사용할 때, 교체 선정 대상이 없을 경우 noeviction과 유사하게 동작한다.

    사용하려는 애플리케이션의 동작 패턴에 따라서 알맞는 교체 정책을 선택하는 것이 중요하다. 애플리케이션이 동작중에도 실시간으로 교체 정책을 변경할 수 있으며, INFO 출력을 통해 캐시 미스, 캐시 히트 등과 같은 관련 정보를 확인하면서 튜닝을 수행할 수 있다.

    경험적으로 아래와 같은 규칙이 가장 일반적이다:

    • 사용하는 애플리케이션의 용도나 데이터 접근 패턴이 확실하지 않거나, 데이터의 일부가 다른 부분보다 상대적으로 많이 접근되는 경우라면 allkeys-lru를 사용하는 것이 좋다.
    • 데이터 접근 패턴이 모든 데이터에 대해 동일한 확률로 접근하는 패턴이거나, 모든 데이터를 순회하는 패턴일 경우 allkeys-random을 사용하는 것이 좋다.
    • 데이터들에 대해 TTL 힌트를 줄 수 있는 경우 캐싱할 데이터를 생성하는 시점에서 서로다른 TTL 값을 적용한뒤, volatile-ttl 정책을 사용하는 것이 좋다.

    하나의 Redis 인스턴스를 캐싱 목적과 persistent 데이터베이스 목적으로 섞어서 사용하는 경우 volatile-lruvolatile-random 정책을 사용하는 것이 좋다. 그러나 이 경우는 보통 서로 다른 두개의 Redis 인스턴스로 분리하여 따로 관리하는 것이 더욱 효율적이다.

    각 키에 대해 만료 기한을 부여할때는 추가적인 메모리가 필요하므로 allkeys-lru 등의 정책을 사용하는 것이 훨씬 메모리 효율적이다.

    캐시 교체 프로세스

    캐시 대상 교체 프로세스는 아래와 같이 동작한다.

    • 클라이언트에서 추가 메모리를 요구하는 명령을 실행한다.
    • Redis에서는 메모리 사용량을 확인하고 maxmemory보다 큰 메모리가 필요할 경우, 캐시 교체정책에 따라서 캐시 제거를 수행한다.
    • 캐시 제거 이후 새로운 명령을 수행하고, 제거된 위치에 새롭게 캐싱하여 캐시 교체 프로세스를 완료한다.

    캐시 교체 프로세스는, 메모리 제한 초과 -> 메모리 비우기 -> 새 명령 실행을 통해 메모리 증가로 단순화 할 수 있다. 이러한 과정이 반복 되던중, 매우 큰 메모리를 요구하는 명령이 들어올 경우 메모리 제한보다 훨씬 많은 메모리를 사용하는 상황이 나올 가능성도 있다.

    근사 LRU 알고리즘

    Redis의 LRU 알고리즘은 정확하게 LRU를 구현한 것이 아니다. 정확하게 LRU 알고리즘에 따른 최서느이 선택을 하지 않고, 몇몇의 키를 샘플링하여 샘플링 한 키들 중에서 접근이 가장 오래된 키를 선택하여, LRU와 유사하게 동작하도록 구현하였다. Redis 3.0부터는 풀 개념을 도입하여 알고리즘을 정확성을 높였다. 원하는 경우 샘플링할 키의 갯수를 조절하여 LRU 알고리즘을 튜닝할 수 있다. 해당 옵션은 maxmemory-samples 5의 명령을 통해 조절 가능하다.

    정확한 LRU 알고리즘을 구현할 경우 모든 키에 대한 LRU 리스트를 유지하는데에 많은 메모리가 소모되므로, 근사 LRU 기법을 사용한 것이다. 아래의 그림은 각 LRU 알고리즘별 캐시 상황을 보여준다.

    LRU 알고리즘 비교

    위의 그래프는 Redis server 주어진 키 갯수만큼의 데이터 입력을 통해 생성하였다. 키 접근은 첫 키부터 마지막 키까지 순차적으로 접근하였으므로, 처음 접근한 키가 LRU의 최선 선택지라고 확신할 수 있다. 이후 기존 데이터베이스 크기의 50% 만큼의 키를 추가 하여, 기존 데이터베이스의 50%가 완전히 오래된 키로 선정될 수 있도록 하였다. 그래프를 통해서 3개의 영역으로 분리하는 3종류의 점들을 확인할 수 있다.

    • 밝은 회색은 기존 데이터베이스의 교체 대상이다
    • 어두운 회색은 기존 데이터베이스이지만 교체 대상이 아니다
    • 초록색은 새롭게 추가된 키 데이터베이스이다

    이론적인 LRU 구현을 할 경우 명확하게 분리가 되는걸을 알 수 있다. Redis 3.0의 경우 5개의 키를 샘플링만 해도 2.8 버전보다 나은 성능을 보여주지만, 새롭게 추가된 키에대해서는 비슷한 결과를 나타냄을 알 수 있다. 샘플링 키를 10개로 늘릴경우 이론적인 LRU에 거의 근접한 결과를 보여주는 것을 알 수 있다.

    LRU 알고리즘은 향후 데이터 접근 패턴이 어떻게 될것인지를 예측하는 방식이다. 위의 그래프에서 볼 수 있듯이 자주 접근되는 데이터들은 근사 LRU를 사용하더라도 캐시 내에 매우 잘 유지가 됨을 알 수 있다.

    LFU 정책

    Redis 4.0부터 LFU (Least Frequently Used eviction mode)를 사용할 수 있다. 이 모드는 특정 상황에서 더 좋은 성능을 나타낼 수 있는데, 이는 각 item에 대한 접근 빈도를 추적하여 덜 접근 되는 데이터를 교체 대상으로 선정하는 특징을 지닌다.

    LRU를 사용할때에 최근에 접근된 데이터 이지만 이전까지 전혀 접근이 없었던 데이터라면, 계속해서 캐시메모리에 남겨두는 것이 적절하지 않을 수 있다. LFU는 이러한 상황을 방지하는 알고리즘이 될 수 있다.

    LFU는 아래의 캐시 정책 선언을 통해 사용할 수 있다.

    • volatile-lfu: expire set에 LFU 알고리즘을 적용한다
    • allkeys-lfu: 모든 키에 대한 LFU 알고리즘을 적용한다

    LFU는 LRU와 마찬가지로 이론적으로 완벽한 구현이 아닌 Morris counter를 통해 근사 LFU를 사용한다.

    카운트크기와 초기화 단위는 redis.conf 파일에 아래의 옵션 선언을 통해 조절할 수 있다.

     

Designed by Tistory.