어느날, 회사 서비스에 장애가 발생했다. 고객에게 나가야할 알림톡이 나가지 않았고 그 외 몇몇 기능이 제대로 작동하지 않았다. 이유를 찾아보니 celery에서 task가 동시에 엄청나게 많이쌓여 Redis에서 OOM에러가 발생한걸 확인할 수 있었다.
일단 그 때는 팀에서 임시방편으로 메모리를 늘려서 해결했지만 평소에 redis메모리를 전혀 모니터링하지 못하고 있었던 점, 메모리 관리 정책에 대해 아는게 없어 부족함을 느꼈고, 이렇게 글로 정리하게 되었다.
Redis의 메모리
Redis는 In-memory DB이므로 당연히 물리적으로 사용가능한 메모리 이상 사용할 수 없다. 그러므로 운영할때 OOM 에러를 만날 수 있으므로 주의해야한다.
maxmemory
redis는 내부적으로 사용가능한 메모리를 설정할 수 있다. 32bit 컴퓨터면 기본값이 3GB이지만, 64bit 부터는 0으로 설정된다.
제한이 없으므로 redis에서 메모리가 모자라게 되면 결국 swap을 사용하게 되고, 한 번 swap된 페이지에 있는 데이터들에 접근하려고 하면 지연시간이 길어질 것 이다. 만약 허용하는 가상메모리마저 초과한다면 OOM에러가 발생하게 된다.
maxmemory 설정은 아래의 명령어로 확인할 수 있다.
config get maxmemory
config set maxmemory 1
테스트로 1byte로 설정하고 아무 문자열이나 set해주면 1byte가 초과하기 떄문에 OOM에러가 발생하게 된다. 물론 maxmemory를 설정하지 않아도 사용가능한 가상메모리까지 모두 사용하면 OOM 에러가 발생한다.
(error) OOM command not allowed when used memory > 'maxmemory'.
maxmemory 정책
redis의 메모리가 maxmemory에 도달했을때 키를 삭제하는 정책을 설정할 수 있다.
- volatile-lru(기본값): expire set에 존재하는 key들 중 LRU에 의해 설정된 key들을 제거한다.
- allkeys-lru: 모든 key들 중에서 LRU 알고리즘에 의해서 선택된 key를 제거한다.
- volatile-random: expire set에 존재하는 key 들 중 임의의 key를 제거한다.
- allkeys-random: 모든 key 들 중 임의의 key를 제거한다.
- volatile-ttl: expire set에 존재하는 key들 중에서 expire time이 가장 적게 남은 key를 제거한다.
- no eviction: key를 제거하지 않고 쓰기 동작에 error를 반환한다.
redis에서 사용하는 LRU 알고리즘은 근사값으로 사용하며, 모든 key를 보는것이 아니라 소수의 keys를 샘플링해서 거기서 가장 오래된 key를 eviction 한다.
메모리 회수 프로세스는 클라이언트가 새로운 command를 실행할 때마다 트리거된다.
결론
내가 다녔던 회사들은 서비스 사용자 수가 많지 않아 redis 관련 이슈가 거의 없었고, 최소 사양으로 ElasticCache를 사용하고 있었지만 메시지 브로커를 redis로 사용하면서 이런 이슈를 겪을 수 있었다. 이런 이슈를 방지하려면 카프카를 이용한 사용량 제어 등 여러가지 방법을 생각할 수 있지만 그 전에 현재 redis 메모리가 어느 정도 되는지, 데이터들이 expire되도록 설정되어있는지, ttl은 얼마가 적당할지 고려해 보는 것이 우선이라고 생각한다.
24/07/14 추가
config set 명령어를 사용하지 않고도 redis.conf 파일을 수정하는 방법도 있다. homebrew로 로컬 컴퓨터에 설치했을 경우 아래 경로에서 redis.conf를 찾을 수 있고, 수정한 다음 redis를 재실행해줘야 한다.
cd /opt/homebrew/etc # brew로 설치한 패키지들의 설정파일이 있는 폴더
vi redis.conf # redis.conf 파일에서 설정을 수정
brew services restart redis # redis 재시작
참고
https://moss.tistory.com/entry/Redis-서버-설정-정리#maxmemory-policy
'기타' 카테고리의 다른 글
GraphQL DataLoader로 N + 1문제 해결하기 (0) | 2024.07.28 |
---|---|
뮤텍스와 세마포어 (0) | 2024.06.24 |
Lambda@Edge + Typscript로 이미지 리사이징 적용하기 (1) | 2024.06.05 |
계층형 모델 테이블 설계(인접 모델, MPTT) (0) | 2024.04.27 |
GraphQL Federation할때 null을 주의하자 (0) | 2024.03.12 |