안녕하세요. 혀로그래머이자 구라쟁이 charsyam 입니다. 오늘은 지난 1편 [혀로그래머 charsyam은 구라쟁이 #1] 샤딩은 쉬워요 샤딩하세요. 의 뒤를 이은 2편 “캐시 멤캐시나 레디스 쓰세요. 쉬워요” 편입니다.
일단 캐시를 왜 쓰는걸까요? 캐시라는 의미는 원래는 연산이 오래 걸리는 작업이나, 레이턴시가 긴곳에서 뭔가 가져와야 할때, 그 시간을 위해서 보다 빠른 저장 장치나, 미리 결과를 저장해 놓고 전달하는 것을 말합니다.
즉, DB나 다른 API 서버에서 결과를 받아와야 하는 것이 느리면, 메모리나, 로컬에 미리 저장을 해놓고 그 결과를 던져주는 것으로 시간을 줄이는게 캐시의 역할입니다.
그런데 로컬에만 들고 있으면, 어떤 서버는 해당 값을 들고 있을 수도 있고, 또 어떤 서버는 없을 수도 있고, write가 발생하면 해당 캐시를 지워줘야 하는데…, 그럼 전체 서버에 해당 값을 지우라고 보내야 하니깐… 귀찮아서, 어느 서버에 데이터를 담아두고, 모두가 거기를 참조하게 됩니다.
이때, 보통 멤캐시나 레디스를 쓰게 됩니다. 남들이 많이 쓰니깐, 추천도 많이 받으니깐요. 현재는 보통 멤캐시보다 레디스를 더 많이 사용하게 됩니다. 이 이유를 설명하자면 복잡한데, 간단하게 설명하면, 레디스가 더 많은 기능을 제공해 줍니다. 그래서 사용하기에 더 편합니다.(레디스 말고 캐시로만 쓰실꺼면 Memcache 변형인 Arcus 같은 것도 좋습니다.)
사실, 이런 캐시가 도입되면 사실 여러가지로 편해집니다. 속도도 빨라지고, 알고리즘이 기본적으로 제공되는 것들을 이용하면, 구현도 좀 더 쉬워지고요. 실제로 거의 대부분(99.99999%) 의 웹서비스 업체에서는 멤캐시나, 레디스를 이용하고 있습니다. 아래를 보면 DB 앞에 멤캐시를 추가하고 나서 DB의 query 가 변경되는 실제 예입니다. select query 수가 1000 까지 오르던 것이 거의 100 근처까지 떨어진 것을 알 수 있습니다.
레디스나 멤캐시를 쓰다보면, 처음에는 모든 것이 좋습니다. 그래서 이런 글도 있습니다.
레디스와 함께한 시간 모두 눈부셨다.
날이 좋아서
날이 좋지 않아서
날이 적당해서
모든 날이 좋았다.
(드라마 레디스)
그런데 트래픽이 몰아치고, 메모리가 한계에 이르기 시작하면 새로운 세상이 펼쳐집니다. 보통 말하지 않는 것들이 모두 이 메모리의 이슈와 연관되어 있습니다.
먼저 첫번째 문제로, 캐시가 죽으면 어떻게 해야 할까요? 캐시니깐 죽어도 됩니다라고 말하면, 구라입니다. 트래픽이 적을 때는 큰 문제는 아닙니다. 자, 캐시가 무엇때문에 쓴다고 했었나요? 연산이 오래걸리거나, 시간이 긴 작업의 속도를 빠르게 제공하기 위해서 입니다. 그럼 다음과 같은 상황을 가정해봅시다.
- 현재 트래픽이 굉장히 높습니다.
- 캐시가 죽습니다.
그러면, 이제 기존의 트래픽이 어디로 가게될까요? 빙고, DB로 가게 됩니다.(사실입니까?) 그럼, 캐시가 전혀 없을 때, 이 DB가 버틸 수 있다면, 사실 큰 문제가 없습니다.(아깐 앞에선 구라라며, 여기서도 구라를…) 그런데 보통 캐시가 대부분의 처리를 담당하고 있었다면, 캐시 서버가 죽는 순간 모든 트래픽은 DB로 전달되고, DB가 못버티면, 서비스 장애가 발생하게 됩니다. 그러면 캐시 서버는 몇대를 두는 것이 정답일까요? 정답은…
정답은… 죽어도 디비에 큰 영향이 없을 수 만큼 두어야 합니다.(즉 케바케!!!, 죽어 퍽퍽퍽, 이런걸 답이라고…) 사실, 결국 이런건 적절히 서비스를 운영해보면서 바꿀 수 밖에 없습니다. 약간의 힌트가 있다면, 캐시 없이 버틸 수 있는 QPS가 어느정도인지 확인해보고, 캐시가 커버해주는 처리량등을 잘 확인해야 합니다.
그렇다면, 캐시 서버들이 죽지 않도록 만들면 되지 않을까요? 라는 질문을 할 수 있습니다. 그런데, 그러기 위해서 필요한 것들이 캐시 서버의 메모리 관리입니다. 그런데 이 메모리 관리가 쉽지가 않습니다. 특히 멤캐쉬의 경우는 좀 문제가 적은데, 레디스의 메모리 관리는 많은 노력이 필요합니다.
일단 레디스는 메모리 파편화가 발생하기 쉽습니다. 그로 인해서 항상 레디스의 RSS 실제 물리 메모리 사용량을 모니터링 하다가, 어느 수준의 증가가 보이면, 다른 장비로 이전을 시켜줘야 합니다. 그럼 이 과정은 쉽냐?, 크게 어렵지는 않지만, 또한 실패할 가능성도 있습니다. 그리고, 이를 위해서는 결국 서버 아키텍처가 이런 변화를 쉽게 대응할 수 있도록 해줘야 합니다. 다른 장비로 이전을 한다면, 기존 서버의 도메인이나 ip를 새로운 장비가 가지도록 하거나, 쉽게 시그널을 보내면, 기존 장비 대신에 새로운 서버로 연결되도록 미리 만들어 두어야 합니다.(하지만 실제로 이러기도 쉽지는 않습니다.)
또한 죽지 않더라도, 인메모리 캐시들은 스왑메모리 영역을 사용하게 되면, 그때부터 성능이 많이 떨어지게 되는데, 멤캐시나 레디스를 쓰다보면, 특정 상황에 스왑메모리를 사용하게 되는 경우가 발생합니다. 이렇게 되면 해당 캐시서비를 리스타트 하지 않는 이상 해당 메모리는 계속 스왑을 쓸 수도 있기 때문에, 특정 상황에서 성능이 저하될 수 있습니다.
정리하자면, 레디스나 멤캐시를 사용하는 것은, 쉽고 좋습니다. 하지만, 이를 많이 사용하게 되었을 때 부터의 관리 이슈는, 상당히 심각합니다. 이를 해결하지 못하면, 도리어 레디스나 멤캐시를 사용하는 것이 심각한 문제의 원인이 됩니다.
재밌는 것은 많이 쓰는 곳은 외국의 큰 회사들도 같은 문제를 가지고 있습니다. 1대, 2대가 아니라, 몇십대, 아니면 몇백대, 아니면 몇천대를 쓰는 곳에서는 이런 이슈들을 어떻게 다루어야 할까요? 거기에 대한 많은 고민이 필요합니다.