개발자를 위한 레디스 튜토리얼 04

레디스, 제대로 알고 쓰나요? — 이것만은 하지 마세요!

GARIMOO
garimoo
7 min readMar 4, 2020

--

지난 이야기

이제 많은 분이 요청주셨던 레디스의 장애 포인트에 대해서 말씀드리려고 합니다. 저도 이 챕터를 기다려 왔습니다. 저의 동기님들 중 한 분이 운영 중인 레디스 서버에서 keys * 명령어를 날리려고 하는 것을 목격한 순간.. 이 시리즈를 기획하게 되었거든요. 아무튼 레디스, 잘 몰라도 괜찮으니 이것만은 하지 마세요!

요구사항 파악하기 (캐시용❓ 저장소용❓)

서비스에서 레디스의 도입을 고민할 때, 캐시용으로 사용할 것인지, 혹은 저장소용으로 사용할 것인지를 분명히 해야 합니다. 즉, 레디스에 저장되었던 데이터가 없어져도 같은 데이터가 RDBMS에 남아 있기 때문에 문제가 없거나, 혹은 일부 값이 유실되어도 괜찮은 경우인지 확인해야 합니다. 데이터운영팀에서는 특별한 경우가 아니면 레디스를 영구 저장소용으로 사용하는 것은 추천드리지 않습니다.

왜냐하면 레디스의 데이터를 파일로 보관하기 위한 persistence 기능(RDB, AOF) 으로 인한 장애발생 가능성이 굉장히 크기 때문입니다. fork()로 인한 COW(Copy-On-Write)로 전체 성능 저하가 발생하거나, 파일이 생성되지 않으면 레디스에 write를 할 수 없는 등 다양한 장애 사례가 존재합니다.

따라서, 꼭 필요한 경우에만 데이터를 저장하는 것이 좋습니다. 레디스를 캐시 용도로 이용할 때에는 RDB와 AOF를 쓰지 마세요. 만약 스토리지 용도로 사용한다면 RDB보다 AOF를 권장드리며, 관련 파라미터도 수정하는 것이 안전합니다. 파라미터에 관한 설명은 아래에서 더 이어가겠습니다.

오버스펙을 피해보아요💸

앞서 살펴본 것과 같이, 레디스를 어떻게 배치하는지에 따라 장단점이 달라집니다. 각 구성에 대한 정확한 이해 없이 대충 레디스를 이용하면 리소스 낭비는 물론, 이로 인해 장애가 발생할 가능성도 존재합니다. (ex. 클러스터가 제일 좋으니까 클러스터 써볼까? 서버 하나에 레디스 인스턴스 6개 띄워보자! -> Max Memory 설정 없고, Persistence 설정 킨 상태에서 사용률 증가한다면 D.I.E ) 따라서 서비스 도입 전 어떤 구성의 레디스를 사용해야 할 지 충분한 고민이 필요합니다.

그래서 도대체 어떤 걸 쓰면 되는걸까? 하는 분들은 아래 그림을 참고해주시기 바랍니다. 물론 서비스에 따라 구성이 약간씩 변할 수 있습니다. Sentinel 구조에서 Replica의 개수가 달라지거나, 혹은 강력한 고가용성을 위해 클러스터를 7개 노드로 구성할 수도 있겠죠. 이렇게 결정된 구조에서 데이터를 영구 저장해야한다면 Persistence 옵션을 추가하면 되겠죠?

위험한 커맨드 ☠️

레디스는 Single Thread로 동작합니다 (정확히는, 커맨드를 처리하는 쓰레드가 한 개입니다). 그렇기 때문에 만약 실행이 오래 걸리는 명령어 한 개가 쓰레드를 잡아먹는다면, 나머지 커맨드는 밀리게 됩니다. 이 또한 장애 상황으로 이어질 수 있는 케이스입니다. 따라서 다음 커맨드는 사용 전에 내가 진짜 지금 이 커맨드를 날려야만 하나?! 다시 한번 생각해보아요.

🚫KEYS *
KEYS *는 레디스에 저장된 모든 key를 출력하는 커맨드입니다. 논리적으로 O(n)만큼의 시간이 필요하기 때문에, 메모리에 저장된 값이 많을 때 이 커맨드를 사용하면 큰일 날 수 있습니다. KEYS는 SCAN 으로 대체 가능합니다. 커서를 통해 key의 일부분을 조회할 수 있으며, KEYS와 동일하게 GLOB 패턴도 사용할 수 있습니다.

🚫FLUSHALL / FLUSHDB
메모리에 존재하는 전체 데이터를 삭제하는 커맨드입니다. 당연한 얘기지만 모든 데이터가 삭제되기 때문임은 물론, O(n)의 시간이 필요하기 때문에 주의해서 사용해야 합니다.

🚫SAVE
다른 클라이언트의 커맨드를 차단하며 전체 메모리 내용을 파일로 저장합니다.

🚫MONITOR
레디스에 연결된 모든 클라이언트에서 보낸 모든 커맨드를 보여주는 커맨드입니다. 레디스 공식 문서 에 의하면 한개 클라이언트에서 MONITOR 를 사용해서 모든 커맨드를 모니터링 하는 동안 전체 처리량은 50% 이상 감소할 수 있다고 합니다.

⭐redis.conf⭐

레디스를 설치한 뒤 기본 파라미터값을 그대로 사용하는 것도 생각지 못한 장애를 발생시킬 수 있습니다. 레디스 설치 후 꼭 확인해야 할 파라미터들을 정리해봤습니다.

✔️보안을 위한 파라미터

보안을 위해 protected-modeyes로, requirepass에는 접속할 때 사용할 패스워드를 입력합니다. 레디스에 접속해야 하는 서버의 IP를 bind에 모두 추가해주세요. 이 경우 레디스는 bind에 추가된 서버와만 연결할 수 있습니다.

✔️데이터를 파일로 저장하는 persistence 기능

캐시 용도로만 사용한다면 RDB 파일을 저장하는 옵션인 save"" 로 설정하는 것을 권장합니다. 만약 이 설정을 켜야한다면 stop-writes-on-bgsave-error 는 꺼주세요. 이 옵션이 yes일 때 RDB 파일 저장에 실패하면 곧바로 레디스에 write할 수 없게 됩니다. AOF파일을 저장하는 옵션인 appendonly도 캐시용으로 사용할 때에는 no로 설정하는 것이 좋습니다. 그리고 Append Only한 AOF 파일은 rewrite를 하지 않으면 계속 늘어나기 때문에 auto-aof-rewrite-percentage100으로 (100% 크기) 지정하고 주기적으로 rewrite 될 수 있도록 해야 합니다.

✔️복제기능을 사용한다면

콘솔창에 연결한 상태로 replication을 사용할 수도 있지만, 설정 파일에 마스터의 정보를 지정한 채로 서버를 시작할 수도 있습니다. 이 파라미터는 복제 노드에 추가해야 합니다.

✔️기타 설정

daemonizeno로 설정할 경우(default 설정) 레디스 서버 실행 시 서버 메시지가 화면에 표시되고 리눅스 프롬프트가 떨어지지 않는 foreground로 실행됩니다. 이 경우 Ctrl+C를 하면 레디스 서버가 종료됩니다. 따라서 서버를 background로 실행하기 위해 daemonize를 yes로 설정하는 것이 좋습니다.

maxmemory 값이 0이면 swap메모리를 사용할 때까지 메모리는 계속 커질 수 있습니다. persistence 기능을 사용한다거나, 복제 노드를 가질 경우에는 fork() 가 발생할 수 있으니 메모리 사용에 더 주의해야 합니다. 권장하는 값은 가용 메모리의 60~70%선에서 max memory를 설정하는 것입니다.

또한 메모리가 꽉 찼을 때 데이터들을 어떻게 처리하는지는 maxmemory-policy 값에 따라 달라집니다. 기본값은 noeviction 으로, 메모리가 가득 차면 더이상 새로운 입력을 받지 않겠다는 정책입니다. volatile-lru 는 가장 최근에 사용하지 않은 값을 삭제하는 정책입니다. 서비스 특성에 따라 다르겠지만, 캐시용도로 사용할 경우 필요 없는 값을 삭제하고 새로운 입력을 받아들이는 것이 좋을 테니, 이때에는 volatile-lru을 사용하는 것을 권장합니다.

마치며

처음으로 ‘요약’ 하거나 ‘번역’ 하지 않고 내가 알고 있던 지식들을 가지고 글을 써 봤습니다. 물론 많은 레퍼런스를 참고하긴 했지만, 여러가지 정보들을 한데 모아 글로 풀어 쓰는 경험은 새로웠고, 재미있었습니다. 컨텐츠를 만든다는 것이 생각보다 어렵고, 그만큼 가치있다는 것을 느꼈습니다. 이제 막 이 분야를 시작한 입장에서, 앞으로 회사생활을 해 가며 어떻게 하면 더 좋은 컨텐츠를 만들 수 있을지 고민하고 노력해야겠습니다.

--

--