[Redis] 4. 메모리 운영기법 및 주의점

2019. 9. 11. 09:20개발/기타

메모리 운영기법

1. Maxmemory : 메모리 사용량 제한

메모리 사용량 제한을 위해 사용하는 maxmemory는 32bit/64bit 환경에 따라 초기값이 다르게 설정된다. 32bit 환경에서는 초기값이 3GB로 설정되어 최대 3GB 메모리만 사용 가능한 반면에, 64bit 환경에서는 초기값이 0으로 설정된다. 즉, 64bit 환경에서는 메모리 사용량 제한이 없으며 운영체제의 가상메모리(스왑)까지 사용한다.(이 때 시스템의 메모리 한계를 인식하지 못해 더 많은 메모리를 요구하여 문제가 발생할 수 있기 때문에 따로 설정을 해주어야 한다.)

주의: maxmoery 설정 된 instance에 slave가 존재 할 때, slave에게 data를 제공하기위해서 사용되는 output buffer의 size는 used memory count에서 제외된다. 왜냐하면 network 문제나 재동기화가 keys들이 제거된 loop에 대한 trigger를 발생시키지 않게 하기 위해서이다. loop가 발생하면 slave의 output buffer가 제거된 key들의 DEL 명령으로 가득 찰 것이다. 그리고 이것은 database가 완전히 빌 때 까지 지속된다. Redis 서버 설정 정리

redis.conf

maxmemory 1gb

논리적으로 사용되고 있는 메모리의 크기가 maxmemory보다 커지는 경우 메모리 정책(Memory-policy)에 따라 아래 방식처럼 처리된다.

  1. 메모리 정책이 설정되어 있지 않은 경우, 에러가 발생한다.
  2. 메모리 정책이 설정되어 있는 경우, 값이 추가될 때마다 메모리 정책에 따라 데이터가 삭제된다.

2. Maxmemory-policy : 메모리 정책

max-memory만큼 메모리를 사용하게 되면, 메모리 정책에 따라 과거에 만들어진 키들이 삭제된다.

Redis 서버는 In-Memory 기반의 데이터 저장 관리기술을 제공하지만 시스템 메모리 크기는 제한적일 수 밖에 없고 상대적으로 사용자 데이터는 이보다 훨씬 더 클 수 밖에 없기 때문에 모든 데이터를 100% 메모리에서 저장 관리할 수는 없습니다. 이와 같은 문제점을 개선하기 위해 Redis 서버는 4.0 버전부터 LRU(Least Recently Used: 가장 최근에 사용되지 않은 것) 알고리즘과 LFU(Least-Frequently-Used: 가장 적게 사용된 것) 알고리즘을 제공하고 있습니다.

  1. noeviction
    캐시를 지우지 않는 정책이다. 메모리가 maxmemory 이상을 사용하게 되면 error를 발생시킨다.

  2. allkey
    각 정책에 따라 모든 키를 대상으로 정리한다.

    • allkeys-lru
      LRU 알고리즘 기반으로 키를 삭제한다.
    • allkey-random
      랜덤하게 키를 삭제한다.
    • allkeys-lfu
      LFU 알고리즘 기반으로 키를 삭제한다.
  3. volatile
    각 정책에 따라 EXPIRE SET에 있는 키들을 대상으로 정리한다.

    • volatile-lru
      LRU 알고리즘 기반으로 키를 삭제한다.
    • volatile-random
      랜덤하게 키를 삭제한다.
    • volatile-ttl
      TTL이 짧은 순으로 삭제한다.
    • volatile-lfu
      LFU 알고리즘 기반으로 키를 삭제한다.

3. LazyFree

빅데이터에 대한 쓰기 작업이 발생하면 Max 메모리에 도달하게 되는데 이 때, 연속 범위의 키에 대한 삭제 작업을 수행하게 되면 성능 지연 문제가 발생하게 됩니다. LazyFree 파라메터는 이 경우 LazyFree 쓰레드를 이용하여 백그라운드 작업을 수행해여 성능 지연 문제를 해결해줍니다.

$ vi redis_5000.conf

lazyfree-lazy-eviction        yes
lazyfree-lazy-expire        yes
lazyfree-lazy-server-del    yes
slave-lazy-flush            yes

1. lazyfree-lazy-eviction

메모리 영역이 Full 되었을 때 연속적인 범위의 Key 값을 삭제하면 기존 메모리 영역에 저장되어 있던 데이터는 DEL 명령어에 의해 삭제하는데 이 작업은 서버 프로세스의 main thread에 의해 실행되면서 블록킹(Blocking: 작업지연상태) 현상이 발생합니다. 이 파라메터 값을 yes로 설정하면 UNLINK 명령어가 실행되고 서버 프로세스의 Sub Thread3에 의해 백그라운드에서 수행되기 떄문에 블록킹 현상을 피할수 있습니다.

2. lazyfree-lazy-expire

EXPIRE 명령어를 실행하면 메모리 상에 무효화된 키 값을 삭제하는데 이 때 역시 DEL 명형어가 실행되면서 블록킹 현상이 발생합니다. 이 파라메터값을 yes로 설정하면 UNLINK 명형어가 실행되면서 블록킹 현상을 피할 수 있습니다.

3. lazyfree-lazy-server-del

메모리 상에 이미 저장되어 있는 키 값에 대해 SET 또는 RENAME 명령어를 실행하면 내부적으로 DEL명령어가 실행되면서 블록킹 현상이 발생합니다. 이를 피하기 위해서는 파라메터 값을 yes로 설정해주어야합니다.

4. slave-lazy-flush

Master-Slave 또는 Partition-Replication 서버 환경에서 복제 서버는 마스터 서버의 데이터를 복제할 때 변경된 부분에 대해서만 부분 복제하는 경우도 있지만 때에 따라서는 기존 복제 데이터를 모두 삭제한 후 다시 복제하는 경우도 있습니다. 이 경우 기존 복제 데이터를 빠른 시간내에 삭제하고 동시에 다시 복제작업을 빠르게 수행해야 합니다. 이 파라메터 값을 yes로 설정하면 빠른 동기화 작업을 수행할 수 있습니다.

4. 데이터 Persistence

RDB(Snapshotting)

SAVE 명령어를 이용하여 일정한 주기마다 일정한 개수의 Key 데이터-셋 값을 디스크상에 dump.rdb 파일로 저장합니다. RDB파일에는 SAVE 명령어를 실행한 시점에 메모리 상에 저장된 모든 데이터를 SnapShot 형태로 저장해 줍니다.

1) SAVE 순간적으로 redis의 모든 동작을 정지시키고 그때의 snapshot을 disk에 저장합니다.(blocking 방식)
2) BGSAVE 별도의 process를 띄워 명령어 수행 당시의 메모리 snapshot을 disk에 저장하는데(non-blocking 방식), 이 때 redis는 정지하지 않고 정상적으로 동작합니다.

  • 장점: 사용자가 저장 주기와 저장 단위를 결정할 수 있고 시스템 자원이 최소한으로 요구됩니다. 메모리의 snapshot을 그대로 뜬 것이기 때문에, 서버 restart시 snapshot만 load하면 되므로 restart 시간이 빠릅니다.
  • 단점: 최종 저장한 이후 새로운 저장이 수행되는 시점 전에 시스템 장애가 발생하는 경우 데이터 유실이 발생할 수 있기 때문에 지속성이 떨어집니다. snapshot을 추출하는데 시간이 오래 걸립니다.

RDB작업시 single thread 문제를 해결하기 위해 Fork() 분기 기능 이용하는데, 이 때 메모리를 2배로 잡아먹어 용량 부족에 따른 오류 발생 가능성이 존재합니다.

AOF(Append On File)

AOF(Append On File) 방식은 redis의 모든 write/update 연산 자체를 모두 log 파일에 기록하는 형태입니다. 서버가 재 시작될때 기록된 write/update operation을 순차적으로 재 실행하여 데이터를 복구합니다. operation이 발생할때마다 매번 기록하기 때문에, RDB 방식과는 달리 특정 시점이 아니라 항상 현재 시점까지의 로그를 기록할 수 있으며, 기본적으로 non-blocking call입니다.

  • 장점: Log file에 대해서 append만 하기 때문에, log write 속도가 빠르며, 어느 시점에 server가 down되더라도 데이타 유실이 발생하지 않습니다.
  • 단점: 모든 write/update operation에 대해서 log를 남기기 때문에 로그 데이타 양이 RDB 방식에 비해서 과대하게 크며, 복구시 저장된 write/update operation을 다시 replay 하기 때문에 restart속도가 느립니다.

결론

RDB와 AOF 방식의 장단점을 상쇄하기 위해서 두가지 방식을 혼용해서 사용해야합니다.

주기적으로 snapshot으로 백업하며 다음 snapshot까지의 저장은 AOF 방식 수행합니다.

이렇게 하면 서버가 restart될 때 백업된 snapshot을 reload하고, 소량의 AOF 로그만 replay하면 되기 때문에, restart 시간을 절약하고 데이타의 유실을 방지할 수 있습니다.

주의할 명령어

  1. keys, flushall ..
    1번에 1개의 명령어만 실행 가능하기 때문에 사용하면 안되는 명령어들이 있으니 주의하자
    ex. keys(모든 데이터 조회), flushall(모든 데이터 소거)

    KEYS의 단점을 보완할 명령어 SCAN
    http://tech.kakao.com/2016/03/11/redis-scan/

  2. Collection

    콜렉션에 데이터 100만건을 넣으면 처리시간이 10초, 1천만건 넣으면 100초씩 걸리는 식으로 늘어나기 때문에 굳이 쓰려면 일단 데이터를 1만건 미만으로 관리해야 한다 고 권고했다.


References