데이터를 어떻게 캐쉬할까

https://www.slideshare.net/gilwonoh/cache-in-api-gateway-47138230

데이터는 DB에 쌓아놓는게 제일 좋지만, 느리고 비싸다. 그러니까 싸고 빠른 캐쉬를 쓴다.

가장 쉬운 캐쉬 전략은 cache-aside다. 일단 캐쉬를 읽고, 없으면 DB에서 읽어서 캐쉬에 쓰는 것이다. 그러면 맨 처음에만 DB를 읽어서 느리고, 그 다음부터는 빠를 것이다. lazy loading이라고도 한다.

이걸 어플리케이션 서버가 캐쉬도 읽고 DB도 읽고 하지 않고, 캐쉬에서 DB도 알아서 읽으라고 하는게 read-through 다. Amazon DynamoDB Accelerator (DAX) 가 그렇다. 하여튼 cache-aside나 read-through나 누가 책임을 가지냐만 다르지, 비슷하다.

그러면 맨 처음 읽는 것도 빠르게 할 수 없을까? write-through 전략을 쓸 수 있다. 데이터를 캐쉬에 쓰고, 캐쉬가 DB에 쓰는 것이다. 그러면 쓰는 시간이 느려지고, 읽는 시간은 빨라진다. 물론 이것은 캐쉬가 최신 데이터를 많이 읽어야 좋을 것이다. 안 그러면 옛날 데이터를 읽어야 하니까 결국 DB를 읽어야 한다. 이것도 cache에 쓰는걸 어플리케이션 서버가 할수도 있고, DAX처럼 캐쉬 서버가 할 수도 있다.

여기서 데이터를 쓰는 시간도 더 줄이고 싶다면 write-behind 전략을 쓸 수 있다. 기본적으로는 write-through와 같지만, 데이터를 쓸 때 일단 캐쉬에만 쓴 다음에, 그 뒤에서 비동기로 천천히 DB에 쓰는 것이다. 그러면 DB에 쓰는 시간이 느려도 캐쉬는 빠릿하게 응답할 것이다. 하지만 DB가 느려지면 DB와 캐쉬간의 시간차가 생기고, 여기에 캐쉬가 죽으면 아직 DB에 쓰지 못한 데이터가 날아갈 수 있다.

그러면 먼저 DB에 쓴 다음에 캐쉬에 쓸 수 있다. 이걸 write-around 라고 한다. 물론 이러면 DB에 쓰고 캐쉬에 쓰는 사이에 옛날 데이터가 보일 수 있다. 그 정도 시간차를 감수하더라도 DB에 쓰는게 더 중요하다면 이렇게 해야 한다.

또 다른 전략은 refresh-ahead다. 앞으로 읽을 것 같은 것들을 미리 읽어놓는 것이다. 근데 예측을 잘 하고, 갱신 주기를 잘 잡아야 한다. 안 그러면 잘 못 맞추거나 불필요한 부하를 줄 수 있다.

캐쉬를 직접 조작할 수도 있다. 예를 들어 매일 0시에 반드시 바뀌어야 할 데이터라면, 0시에 해당 캐쉬를 evict 시킨다. 다만 0시에 대량의 데이터를 동시에 읽어서 DB 응답시간이 순간적으로 느려질 수 있고, 더 나아가 캐쉬 서버까지 느려질 수 있다. 그러니까 정확히 시간을 지켜야 하는 경우에만 쓴다.

마찬가지로 데이터를 즉시 반영해야 할 때 해당 캐쉬를 evict 시킬 수 있다. 아니면 write-through 캐쉬를 써도 된다.

조금 더 나아가 DB를 안 거치고 캐쉬만 직접 조작할수도 있다. 이러면 캐쉬를 간단한 임시 DB처럼 쓰는 것이다. 캐쉬 서버가 다운되서 데이터가 다 날아가도 상관없다면 이렇게 써도 좋다. 예를 들어 Redis 같은 경우 SQL 스타일의 펑션도 지원하고, 더 나아가 Redis를 DB처럼 쓸 수도 있다. Single Sign On (SSO)의 세션 키를 저장해놓기도 좋다. 설령 캐쉬가 날아가서 로그아웃이 되더라도, 다시 로그인하면 되니까 부담이 없다. 경매 입찰할 때 최대 금액을 보여줄때도 좋다. 입찰 데이터는 날아가면 안되니까 카프카로 쓰고, 그러면서 캐쉬에 최대값을 갱신한다. 혹시 캐쉬가 죽어도 카프카 데이터에서 다시 읽어오면 되니까 괜찮다.

그리고 캐쉬 서버도 죽을 수 있으니 평소에 모니터링을 잘 해야 한다. 메모리 사용량이 점점 늘어나지 않는지, CPU나 디스크 사용량이 순간적으로 튀지 않는지 등을 잘 봐야 한다. 특히 Redis는 순간적으로 많은 키들을 만료시키면 죽기도 한다. 이런걸 thundering herd 문제라고 한다. 예를 들어 0시에 하루치 캐쉬를 전부 evict하는 건 부담이 크다. Time To Live (TTL)을 걸어서 evict 없이도 만료되도록 하고, 그 TTL에도 살짝 random을 걸어서 조금씩 어긋나게 하면 더 잘 퍼질 것이다.

그리고 가능하다면 캐쉬 서버가 죽어도 어플리케이션 서버가 비상모드로 돌아가도록 대비를 해놓는 것이 좋다. 예를 들어 광고를 보여줄 때 캐쉬에서 읽어서 JSON으로 파일에 써 놓고, 캐쉬가 실패하면 JSON파일에서 옛날 데이터를 읽어서 광고를 보여준다. 물론 적어도 맨 처음에 한 번은 성공해야 JSON 파일이 생길테니, 맨 처음에는 반드시 성공하도록 해야 한다.

Published
Categorized as xacdo

By xacdo

Kyungwoo Hyun

Leave a comment

Your email address will not be published.