제가 담당하고 있는 인포크링크라는 서비스는 최근 MAU가 1000만을 돌파했어요.
그런데 오늘은 MAU가 500만명 쯔음이었을 때에 발생한 문제에 대해 이야기해보려고 해요.
아래에 해당하시는 분들이 읽으시면 더 많은 것을 얻어가실 수 있어요.
- 이제 막 트래픽이 늘고 있는데 여기서 무엇을 개선하면 좋을지 모르겠는 분
- 곧 트래픽이 늘 것 같은데 무엇을 준비해두면 좋을지 고민하고 있는 분
이제 터질 때 쯤 된 것 같은데요?
라는 말을 종종 하던 때가 있었는데요. 그 전에 제가 운영하고 있는 인포크링크의 기능과 사용 예시에 대해 먼저 알려드려야 할 것 같아요.
- 유저는 한 페이지짜리 [방문자 페이지]에 보여질 컨텐츠를 입력한다.
- 유저는 [방문자 페이지]를 인스타그램 등의 프로필 영역에 링크를 등록한다.
- 유저가 인스타그램에서 '바이오링크를 클릭해서 들어오세요'라고 유도한다.
인스타그램 사용자분들이 사용하는 서비스인만큼, 영향력이 있는 유저가 인스타그램에 게시해주시고, 또 링크를 클릭하게 유도까지 해주시면 예상치도 못하게 서버가 몰리는 경우가 잦았어요. (그런 상황을 대비해야한다는 생각으로 이런 말을 해주시는 분도 있었는데, 그날이면 항상 서버가 터지곤 했답니다. ㅠㅠ) 그리고 이때는 특히나 더 트래픽이 잦게 터지곤 했고요.
그런데 문제는 비교적 명확했습니다.
모든 부하가 RDS로 쏟아지고 있었어요.
한편 클라이언트와 API 서버는 평온할 정도로 문제가 없었고요.
이 당시에 서버가 터지는 패턴은 꽤 일관적이었습니다.
1. 인플루언서가 인스타그램에 새로운 피드 혹은 새로운 스토리를 올린다.
2. 인플루언서의 팔로워들은 이벤트에 바로 참여하기 위해 프로필 링크를 클릭한다.
3. 프로필 링크로 등록해둔 우리 서비스에 급격한 부하가 온다.
그래서 항상 RDS 모니터링에 달아둔 알람이 슬랙으로 전달이 되면 긴급하게 RDS 증설하는 것 뿐만 아니라 GA로 몇명이 접속해있는지, 어떤 인플루언서에게서 트래픽이 많이 발생하고 있는지를 보곤 했어요. 그리고 역으로 그 인플루언서의 인스타그램을 타고 들어가면 꼭 게시글을 올려둔 상태이거나, 게시글 내에 이벤트를 진행하고 있는 경우가 많았어요.
여러분이라면 이 상황에서 어떤 선택을 하시겠어요?
우리 서버 증설할까요?
사실 이런 상황이 오기까지 "가능하면 비용을 조금 더 쓰고 우리가 해야하는 비즈니스에 집중하자"라는 공감대가 형성되어 있었어요.
그런데 이 상황은 단순히 서버를 증설한다고 해결된다고 하기 어려운 문제였어요.
그 이유는 아래와 같았는데요.
- 트래픽이 늘어나는 것은 어디까지 늘어날지 모른다.
- 트래픽이 올라가는 것을 보면서 증설하기에는 이미 늦다.
- 대부분의 트래픽과 피크를 찍는 트래픽에 간극이 매우 크다.
이런 이유로, 비효율적으로 RDS 를 증설하는 것에 동의할 수 없었어요.
단순 비용 뿐만 아니라 증설을 위해 들어가는 작업도 비효율적이었어요.
서버 증설 말고 캐싱
사실 그것보다도, 너무나도 간단한 해결책이 있었는데요.
바로 바로 쿼리된 결과를 캐싱하는 것이에요
(너무 쉽나요? ㅎㅎ)
왜 캐싱이었나?
서비스의 정체성을 생각해보면 너무나도 당연하고 심플해요.
- 인플루언서들의 컨텐츠 변화가 잦지 않다.
- [방문자 페이지]에 접속하는 팔로워들의 첫번째 요청은 읽기 요청이다.
그래서 꽤 긴 시간동안 캐시를 유지하고 있어도 되는 정보이면서 변화가 잦지 않으니, 변화가 발생할 때마다 그 정보를 캐싱하기로 했어요.
또 기존에 다른 api 에서도 이미 레디스를 통해 가져오는 정보들을 캐싱하고 있었기 때문에, 떠올리기 어려운 아이디어가 아니기도 했어요.
캐싱의 구조는 여러 구조를 고민해봤지만, 가장 심플하게 사용할 수 있는 Key - Value 구조로 사용하기로 했어요.
유저마다 가지는 pk를 key로 삼아서요.
이 과정을 그림으로 표현하면 아래와 같이 보여줄 수 있어요.
캐시를 먼저 조회해서 key 값이 존재하면 바로 응답으로 보내주면 돼요.
그런데 방문자 페이지의 속도를 높이겠다고, 모든 유저에 대해 캐시 데이터를 만들어주는 것도 비효율적이라고 생각했기 때문에, 캐시를 인위적으로 만들어주진 않을 예정이었어요. 그러면 캐시가 없는 유저들에 대한 캐시 데이터는 누가, 언제 만들어줄까요?
그게 누구든, 처음으로 이 데이터에 접근하는 사람이 조회할 수 있게 처리했어요.
캐시 데이터가 없다면 데이터베이스를 조회하는 것이죠.
이미 알고 있긴 했지만 정말 심플한 구조였어요.
변경할 코드 라인 수도 몇 줄 되지 않다 보니 당일 QA를 거쳐 배포하는 걸로 합의했어요.
결과는?
꽤 성공적이었어요.
우선 데이터베이스 사용량의 약 1/3이 줄어들었어요.
기존에 평균 사용량이 28%이던 것을 8% 가까이 줄여냈고, 추후에는 데이터베이스 크기까지 줄일 수 있는 계기가 되었고요.
한편 레디스 사용량은 올라가긴 했지만, 크게 눈에 띄는 정도는 아니었어요.
대망의 API 요청속도는 트래픽이 몰리는 경우를 가정했을 때, 수초씩 걸렸던 데에 반해
10ms ~ 20ms 까지 줄일 수 있었어요.
글을 마치며
캐싱 뿐만 아니라 이외에도 속도를 높이기 위한, 혹은 효율적인 운영을 위한 여러가지 시도들이 있었는데요.
다음 번에는 또 다른 주제로 다시 찾아 오겠습니다. ( 피드백은 언제든지 환영입니다 :) )