(Redis)레디스 운영 시 고려사항

1. 메모리 크기

레디스는 메모리에 데이터를 저장하기 때문에 레디스가 사용할 메모리의 크기를 지정하는것은 성능과 가장 밀접한 관련이 있다.
실제로 저장되는 데이터의 크기는 redisObject와 키가 저장된 크기를 뺀 나머지가 된다.
저장되는 데이터가 작은 크기의 문자열이라면 redisObject를 저장하기 위한 오버헤드가 더 크게 된다.
이럴 떄는 문자열을 숫자로 바꾸어 저장하거나, 다른 데이터형을 사용하면 더 많은 데이터를 저장할 수 있다.

만약 레디스에 저장될 데이터의 크기를 산정하지 못하여 redis.conf와 maxmemory 설정에 값을 지정할 수 없게 되었다고 하자.
그러면 레디스에 데이터가 계속 추가되어 물리 메모리를 모두 사용하게 된다.
이때 운영체제에서는 애플리케이션이 시스템에서 사용 가능한 물리 메모리 보다 더 많은 메모리를 사용하려고 할 때 스왑이라는 가상 메모리 공간을 하드디스크에 생성하여 사용하는데, 이 영역을 스왑공간이라 부른다.

이떄 스왑 공간이 충분하지 않으면 운영체제는 메모리에서 동작 중인 프로세스를 제거하여 사용가능한 메모리의 영역을 확보하게 된다.
이와 같이 동작 중인 프로세스를 죽이는 리눅스의 프로그램을  OOM킬러라고한다.
그러므로 OOM킬러를 피하려면 충분한 스왑공간을 확보해야한다. 그런데 데이터가 저장되기 시작하면 레디스의 응답시간은 수십 배에서 수백 배까지 늘어나게 된다.
만약 응답시간이 중요한 서비스라면 반드시 redis.conf의 maxmemory 설정에 설치된 물리 메모리 크기 이내의 값을 지정하여야 한다.

일반적으로 운영체제는 설치된 메모리 크기의 두 배를 스왑공간으로 할당한다.
여기서 말하는 두 배는 메모리가 충분하지 못했던 과거의 기준이다.
메모리의 가격이 저렴해지고 엔트리급 서버 시스템이 16GB이상의 메모리를 갖는 지금은 기준이 조금 달라졌다.

레드햇 엔터프라이즈의 가상 메모리 설정 가이드 문서에 따르면 시스템에 설치된 물리 메모리의 크기에 따라서 다른 값을 설정하도록 권고하고있다.
가상 메모리 설정 문서에는 특별한 이유가 없으면 시스템에 설치된 메모리보다 작은 스왑공간을 설정하라고 되어 있다.

예)
물리 메모리     추천 크기
2GB                 4GB
2~8GB             설치된 물리메모리 2배
8~64GB            설치된 물리 메모리의 1/2배
64GB~              4GB




2. 메모리 설정

남은 메모리가 많은데도 불구하고 fork함수의 수행이 실패하는 경우가 있다. fork함수는 부모 프로세스와 동일한 크기의 메모리를 사용하는 프로세스를 생성한다.
리눅스에서 부모 프로세스가 사용하는 만큼의 메모리가 남아있지 않으면 fork함수가 실패하게 된다.
스냅샷이나 AOF를 위해서 호출한 fork함수가 실패한 이후의 레디스는 모든 쓰기 연산에 대하여 실패 응답을 전송한다.

위와 같은 결과에 따라 4GB의 데이터를 가진 레디스를 운영하기 위해서는 반드시 4GB이상의 유휴 메모리가 있어야 한다는 결과가 도출된다.
대부분의 상황에서는 레디스가 호출한 fork함수에 의해서 생성된 자식 프로세스는 부모 프로세스의 메모리를 복사할 필요가 없다.
현재 부모 프로세스의 메모리를 읽어서 디스크에 저장하기 때문이다.
그럼에도 불구하고 추가적인 4GB 메모리가 없으면 fork함수가 실패한다.
이런 현상을 방지하기 위해서는 /etc/sysctl.conf파일에 vm.overcommit_memory=1을 추가해야한다.

vm.overcommit_memory가 가질 수 있는 설정값은 0, 1, 2이고 디폴트는 0이다.

0 : 메모리 할당 요청인 malloc 함수의 요청이 들어오면 요청된 크기만큼의 물리적 메모리가 존재할 때에만 메모리를 할당한다.

1 : 메모리 할당 요청인 malloc 함수의 요청이 들어오면 남은 물리 메모리가 없더라도 성공을 응답한다. 단, 요청으로 입력된 크기의 스왑영역이 존재할 때만 성공한다.

2 : 사용 중인 메모리 크기가 '스왑공간 크기 + vm.overcommit_ratio * '물리 메모리 크기'
이내일 떄 메모리를 할당한다.


이와같은 설정에서 응답시간을 줄이기 위해 스왑영역을 레디스에 저장된 데이터 크기보다 작게 잡으면 OOM킬러에 의해서 레디스 프로세스가 종료될 수 있다.

레디스 프로세스 종료를 막기 위해서 스왑영역을 크게 잡는다면 fork함수가 수행되는 시간 동안 응답시간이 길어 질 수 있다.

그렇다면 평균적인 응답속도를 유지하기 위해서 가장 적절한 데이터의 크기는 어떻게 산정할까?
8GB 물리 메모리를 가진 시스템을 보자
운영체제를 위한 메모리 영역을 제외하고 사용가능한 물리 메모리 영역은 6GB라고 가정하자.
이 상태에서 저장 가능한 데이터의 크기는 서비스에서 수행되는 쓰기 연산과 읽기 연산의 비율에 따라서 달라진다.
전체 연산횟수를 100이라고 하고 이 중에서 쓰기 연산의 비율이 50을 넘는다면  물리 메모리의 60%를 지정하는 것이 좋다.
이 값을 기준으로 쓰기 비율이 줄어들 수록 지정 가능한 메모리의 비율을 늘릴 수 있다.
만약 운영하는 서비스가 보수적이거나 응답시간에 민감한 서비스라면 50%이하로 설정하는 편이 더 안전하다.

댓글

이 블로그의 인기 게시물

(18장) WebSocekt과 STOMP를 사용하여 메시징하기

(네트워크)폴링방식 vs 롱 폴링방식

(ElasticSearch) 결과에서 순서 정렬