클라우드

캐시, 앞에 두면 뒤가 얼마나 버티는지 모르게 된다

캐시가 빠른 이유부터, 비어 있는 순간 원본으로 요청이 몰리는 이유까지 살펴봐요. 상태를 바꿔 보며 적중률과 원본 부하의 관계도 확인할 수 있어요.

이 글의 목차캐시에 없으면 누가 답할까요?4
  1. 캐시에 없으면 누가 답할까요?
  2. 만료 시간은 정답의 유통기한일까요?
  3. 잘 작동할수록 가려지는 부하
  4. 저는 캐시를 붙이기 전에 멈췄어요

식당 앞에 미리 만든 도시락을 놓으면 손님이 주방까지 주문하러 갈 필요가 없어요. 주방은 한가해 보이겠죠. 그런데 도시락이 모두 팔린 뒤에도 손님이 같은 속도로 들어온다면 어떨까요? 그제야 주방이 한 번에 처리할 수 있는 주문 수가 드러나요.

캐시도 자주 찾는 데이터를 앞에 꺼내 두는 장치예요. 메모리에 둔 사본으로 응답하면 매번 원본을 읽는 시간을 줄일 수 있어요. 여기서 배워 둘 것은 빠른 이유만이 아니에요. 캐시가 답하지 못할 때 요청이 어디로 가는지까지 따라가 봐야 해요.

캐시 적중 여부에 따른 원본 부하 평소의 캐시 적중률만으로는 캐시가 비었을 때 원본이 전체 요청을 감당할지 알 수 없다. 개념 도해 캐시 적중 여부에 따른 원본 부하 요청 처리 경로 요청 ① 캐시 조회 적중 미스 반환 원본에 닿지 않는다 ② 원본 조회 ③ 캐시 적재 반환 왕복 두 번이 더 붙는다 원본 도달량 원본 처리 한도 정상 상태 미스분만 도달 캐시 소실 한도 초과 전량 도달 대기 시간 증가 요청 실패 평소의 캐시 적중률만으로는 캐시가 비었을 때 원본이 전체 요청을 감당할지 알 수 없다. SOSHIN · DEV NOTES
원본의 처리 여유가 작은 경우를 그렸어요. 평소의 적중률만으로 전체 요청을 감당할지는 알 수 없어요.

캐시에 없으면 누가 답할까요?

예를 들어 상품 소개를 읽는다고 해 볼게요. 앱은 먼저 캐시에 묻고, 값이 없으면 데이터베이스에서 읽어 와요. 받아 온 값을 캐시에도 넣으면 다음 사람은 더 빨리 볼 수 있죠. 이렇게 필요할 때 채우는 방식을 지연 로딩(lazy loading)이라고 해요. AWS의 캐시 전략 문서는 이 방식과, 원본에 쓸 때 캐시도 갱신하는 방식을 비교해요.

읽을 때 채우면 쓰이지 않는 데이터를 미리 넣지 않아도 돼요. 대신 원본의 상품 소개가 바뀌어도 캐시에 예전 소개가 남을 수 있어요. 쓸 때 함께 갱신하는 라이트 스루(write-through)는 이 틈을 줄이지만, 모든 쓰기가 같은 갱신 경로를 지나고 두 저장이 성공한다는 전제가 필요해요. 이름만 붙인다고 최신값이 보장되지는 않아요.

캐시가 비어 있을 때 원본을 읽는 길이 있다는 것과 서비스가 계속 버틴다는 것도 구별해야 해요. 길은 있어도, 그쪽으로 몰린 요청을 데이터베이스가 처리하지 못하면 응답은 느려지거나 실패해요. 캐시 서버의 오류를 처리하고 원본으로 우회하는 동작도 앱이 맡아야 하고요.

만료 시간은 정답의 유통기한일까요?

캐시에는 TTL(Time to Live), 곧 값을 보관할 시간을 붙일 수 있어요. 시간이 지나면 그 사본을 다시 쓰지 않고 새로 가져오는 거예요. 다만 5분 동안 보관하기로 했다고 5분 내내 최신인 것은 아니에요. 저장 직후 원본이 바뀌면 남은 시간 동안 예전 값이 보일 수 있죠.

이 때문에 데이터마다 허용할 수 있는 지연을 먼저 정해요. 상품 설명이 잠깐 늦게 바뀌는 것과 결제할 재고 수량이 늦게 바뀌는 것은 결과가 다르잖아요. AWS의 전자상거래 캐시 설명도 데이터의 성격에 맞춰 보관 시간을 나눠요. 주문을 확정하는 순간에는 어디의 값을 믿을지 별도로 정해야 해요.

잘 작동할수록 가려지는 부하

요청 10개 중 8개를 캐시가 처리하고 2개만 데이터베이스로 간다고 가정해 볼게요. 캐시가 전혀 답하지 못하면 같은 요청 10개가 모두 원본으로 향해요. 전체 방문자는 그대로인데 원본이 받는 요청은 다섯 배가 되는 셈이에요. 아래에서 캐시 상태를 바꿔 보면 차이가 보여요.

캐시가 비면 요청은 어디로 갈까요?

같은 요청 10개를 두 상태로 비교해 보세요.

캐시에서 응답 8개

원본으로 이동 2개

캐시가 8개에 답하면 원본은 2개를 받아요. 캐시가 비어 10개 모두 원본으로 가면, 같은 방문자 수에서도 원본 요청은 다섯 배가 돼요.

동시에 들어온 요청의 첫 조회를 단순화한 예시예요. 실제 적중률이나 처리량을 측정한 값은 아니에요.

이 수치는 관계를 설명하기 위한 예시예요. 실제 결과는 요청 종류, 캐시를 다시 채우는 속도, 원본의 처리 여유에 따라 달라져요. 한 번의 요청이 데이터베이스 질의 여러 개를 만들 수도 있으니 적중률 하나로 용량을 계산할 수는 없어요.

만료 시각이 겹칠 때의 원본 요청량 비슷한 시각에 채우고 같은 보관 시간을 준 항목은 만료 시점도 겹칠 수 있다. 개념 도해 만료 시각이 겹칠 때의 원본 요청량 일괄 만료 시각 시간 원본 요청량 미스 폭주 만료 분산 적용 시 비슷한 시각에 채우고 같은 보관 시간을 준 항목은 만료 시점도 겹칠 수 있다. SOSHIN · DEV NOTES
비슷한 시각에 채우고 같은 보관 시간을 주면 만료가 겹칠 수 있어요.

값을 비슷한 시각에 채우고 같은 TTL을 주면 만료도 한꺼번에 몰릴 수 있어요. 값을 보관하는 시간을 조금씩 흩뜨리면 그 시점을 나눌 수 있죠. 같은 값을 기다리는 요청을 묶어 원본에 한 번만 묻는 방법도 있어요. 앞의 방법은 요청이 몰리는 시각을 나누고, 뒤의 방법은 중복된 질의 자체를 줄여요.

채우는 방식편해지는 점따로 해결할 문제
읽을 때 채우기실제로 찾는 값만 보관첫 조회와 원본 변경 뒤의 오래된 값
쓸 때 함께 갱신하기변경을 사본에 빨리 반영두 저장의 실패, 새 캐시의 빈 값
만료 시점을 흩뜨리기동시에 원본으로 몰리는 요청 완화데이터마다 허용할 수 있는 오래됨

AWS의 질의 결과 캐싱 문서를 읽을 때도 저장 공간보다 이 질문을 먼저 떠올리면 좋아요. 이 결과는 얼마나 자주 반복되고, 언제 바뀌며, 사본이 없을 때도 다시 계산할 수 있을까요? 앞에 저장소 하나를 더 두는 일이 원본의 책임을 없애 주지는 않아요.

저는 캐시를 붙이기 전에 멈췄어요

제가 만드는 앱에서도 AI가 만든 문장을 다시 계산하지 않도록 캐시를 붙일까 고민했어요. 그런데 사람마다 다른 문장을 받다 보니 같은 요청이 반복될 여지가 생각보다 작았어요.

그때는 캐시를 추가하는 대신 계산을 다른 경로로 넘길 수 있게 하는 쪽을 골랐어요. 캐시가 유용하지 않아서가 아니라, 제가 줄이려던 일이 얼마나 반복되는지부터 봐야 했던 거예요. 이제 캐시 이야기를 하면 빨라질 응답과 함께, 캐시가 비었을 때 돌아갈 곳도 떠올려요.

소신을 상징하는 까마귀 목판화 일러스트
작성자소신 · Soshin

웹과 서버를 만들고, 개발하며 배운 것들을 글로 남깁니다.

좌우로 움직여 보세요