티스토리 뷰

Redis Eviction 정책을 적용하여 효율적인 캐시 띄우기

Overview

저번 글에서 Redis를 캐시 저장소로 선택하고 Spring Cache를 도입하여 읽기 작업의 성능을 높인 것을 확인할 수 있었다. 그러나 성능을 높이는데 많은 역할을 하는 캐시라도 데이터를 제대로 관리하지 못하면 문제가 발생할 수 밖에 없다. 예를 들어, 내가 개발하고 있는 SNS 서비스를 많은 사람들이 이용한다고 가정했을 때, 짧은 시간 안에 Redis 서버에는 다양한 캐시 데이터들로 가득 차게 될 것이다. 이때 메모리 사용에 대한 어떠한 제한이 없다면 캐시 데이터가 메모리를 계속 차지하게 되므로 어플리케이션의 전체적인 성능까지도 영향을 끼칠 수 있다. 이러한 상황을 막기 위해 Redis에서는 다양한 Eviction 정책을 제공한다. 그 중에서 어떤 것이 나의 프로젝트에 적합한지 알아볼 것이다.

 

메모리를 제한하지 않으면 어떤 일이 생길까?

Redis 공식 문서에 따르면 32bit 시스템에서는 메모리 제한 사용량 기본값이 3GB로 설정되어 있지만, 64bit 시스템에서는 0으로 설정되어 있다. 즉, 메모리 사용량에 대해 상한선이 없다는 뜻이다. 물리적인 메모리의 용량 이상을 사용하게 되면 스왑 영역까지 사용하게 된다. 스왑 영역이란 물리 메모리에 공간이 부족할 때 빈 공간을 만들기 위해 현재 사용 중인 메모리 일부분을 저장하는데 사용하는 저장 장치의 한 구역이다.

 

 

이는 시스템이 사용할 수 있는 메모리 용량이 실제 물리 메모리의 크기보다 커져보인다는 장점이 있지만 스왑 영역을 사용하는 것은 성능을 저하시키는 요인 중 하나이다. 왜냐하면 메모리에 접근하는 속도에 비해 저장 장치에 접근하는 속도가 굉장히 느리기 때문이다.

 

따라서, 스왑 영역까지 사용하지 않도록 Redis가 사용할 메모리의 용량을 제한해야 하며, 다음과 같이 redis.conf 파일의 maxmemory 옵션으로 지정할 수 있다.

maxmemory 100mb

 

Redis Cache Eviction Policy

Redis가 사용하는 메모리가 maxmemory 에 지정한 크기보다 커지면 Redis는 사용자가 지정한 Eviction 정책에 따라 저장되어 있는 데이터를 제거한 후, 새로운 데이터를 저장하게 된다. Redis에서 제공하는 Eviction 정책은 다음과 같다.

  1. noevitcion

    : maxmemory 에 도달한 상태에서 클라이언트가 새로운 데이터를 저장하려고 할 때 기존 캐시 데이터를 지우지 않고 에러를 발생

  1. LRU

    : LRU(Least Recently Used) 알고리즘은 사용한지 가장 오래된 데이터부터 삭제하는 알고리즘이다. 이론 상의 LRU 알고리즘을 구현하려면 메모리가 많이 필요하다. 그래서 Redis에서는 maxmemory-samples 옵션에서 지정한 수의 키로 샘플링하여 LRU 알고리즘의 근사치를 계산한다. 샘플 수가 많을수록 메모리를 많이 사용하지만 알고리즘의 정밀도가 올라간다.

    • allkeys-lru : 모든 키를 대상으로 LRU 알고리즘을 적용하여 키를 삭제

    • volatile-lru : EXPIRE SET 안에 있는 키를 대상으로 LRU 알고리즘을 적용하여 키를 삭제

      ​ (EXPIRE SET 안에 있는 키: 만료 기간이 설정된 키)

  1. Random

    : 무작위로 데이터를 삭제한다.

    • allkeys-random : 모든 키를 대상으로 무작위로 데이터를 삭제
    • volatile-random : EXPIRE SET 안에 있는 키를 대상으로 무작위로 데이터를 삭제
  1. TTL

    : TTL이 짧은 데이터부터 삭제한다.

    • volatile-ttl : EXPIRE SET 안에 있는 키를 대상으로 TTL이 짧은 데이터부터 삭제
  1. LFU

    : LFU(Least Frequently Used) 알고리즘은 사용 빈도수가 가장 적은 데이터부터 삭제하는 알고리즘이다. LRU와 달리 최근에 사용된 데이터라도 자주 사용되지 않는다면 제거 대상이 될 수 있다.

    • allkeys-lfu : 모든 키를 대상으로 LFU 알고리즘을 적용하여 키를 삭제
    • volatile-lfu : EXPIRE SET 안에 있는 키를 대상으로 LFU 알고리즘을 적용하여 키를 삭제

 

어플리케이션 특성에 맞게 Eviction 정책을 선택하여 redis.conf 파일의 maxmemory-policy 옵션에 지정한다.

maxmemory-policy allkeys-lru

 

LRU 알고리즘과 LFU 알고리즘의 기본 로직

Eviction 정책에서 언급한 LRU 알고리즘과 LFU 알고리즘은 언뜻 보면 비슷하게 보일 수도 있다. 비교를 위해 두 알고리즘의 기본적인 로직을 간단하게 표현하면 다음과 같다.

 

LRU

 

LFU

 

어떤 Eviction 정책이 적합할까?

어플리케이션에 따라 데이터 액세스 패턴이 다르기 때문에 최대한 어플리케이션 특성을 고려하여 올바른 Eviction 정책을 선택해야 한다. 첫 문단에 언급한 것처럼 나는 현재 개발하고 있는 SNS 서비스를 많은 사람들이 이용하는 상황을 가정하고 있다. 그만큼 빠르게 증가하는 캐시 데이터를 효율적으로 관리하면서도 요청이 많은 데이터를 선별적으로 보존해야 한다는 것이다.

 

먼저, noevictionvolatile-* 정책은 위의 상황에 적합하지 않은 정책이라고 생각했다. noeviction 정책은 기존의 데이터를 삭제하지 않기 때문에 어플리케이션을 데이터 보존용으로 운영할 때 적합하다고 판단했다. volatile-* 정책들은 "만료 기간이 설정된 데이터"라는 전제 조건을 만족한 데이터들만 제거 대상으로 선정한다. 만약 전제 조건을 만족하는 데이터들이 없다면 noeviction 정책처럼 처리되기 때문에 후보에서 제외시켰다.

 

나머지 allkeys-lru , allkeys-random , allkeys-lfu 중에서 나는 allkeys-lfu 정책을 선택했다. allkeys-lru 는 최근에 사용되었는지에 따라 데이터 보존 여부가 나뉘기 때문에 사람들이 잘 찾지 않는 리소스더라도 어떤 사람이 최근에 요청했다면 삭제 대상에서 제외된다. 즉, 재요청될 가능성이 높은 데이터가 순위에 밀려 삭제가 될 수 있다는 것이다. 요청이 많이 들어오는 데이터가 삭제된다면 그만큼 데이터베이스에 접근하는 횟수가 높아질 수 있다. 따라서, allkeys-lfu 정책을 적용하여 위와 같은 문제 상황이 없도록 했다.

댓글