캐시, 앞에 두면 뒤가 얼마나 버티는지 모르게 된다
캐시가 빠른 이유부터, 비어 있는 순간 원본으로 요청이 몰리는 이유까지 살펴봐요. 상태를 바꿔 보며 적중률과 원본 부하의 관계도 확인할 수 있어요.
이 글의 목차캐시에 없으면 누가 답할까요?4
식당 앞에 미리 만든 도시락을 놓으면 손님이 주방까지 주문하러 갈 필요가 없어요. 주방은 한가해 보이겠죠. 그런데 도시락이 모두 팔린 뒤에도 손님이 같은 속도로 들어온다면 어떨까요? 그제야 주방이 한 번에 처리할 수 있는 주문 수가 드러나요.
캐시도 자주 찾는 데이터를 앞에 꺼내 두는 장치예요. 메모리에 둔 사본으로 응답하면 매번 원본을 읽는 시간을 줄일 수 있어요. 여기서 배워 둘 것은 빠른 이유만이 아니에요. 캐시가 답하지 못할 때 요청이 어디로 가는지까지 따라가 봐야 해요.
캐시에 없으면 누가 답할까요?
예를 들어 상품 소개를 읽는다고 해 볼게요. 앱은 먼저 캐시에 묻고, 값이 없으면 데이터베이스에서 읽어 와요. 받아 온 값을 캐시에도 넣으면 다음 사람은 더 빨리 볼 수 있죠. 이렇게 필요할 때 채우는 방식을 지연 로딩(lazy loading)이라고 해요. AWS의 캐시 전략 문서는 이 방식과, 원본에 쓸 때 캐시도 갱신하는 방식을 비교해요.
읽을 때 채우면 쓰이지 않는 데이터를 미리 넣지 않아도 돼요. 대신 원본의 상품 소개가 바뀌어도 캐시에 예전 소개가 남을 수 있어요. 쓸 때 함께 갱신하는 라이트 스루(write-through)는 이 틈을 줄이지만, 모든 쓰기가 같은 갱신 경로를 지나고 두 저장이 성공한다는 전제가 필요해요. 이름만 붙인다고 최신값이 보장되지는 않아요.
캐시가 비어 있을 때 원본을 읽는 길이 있다는 것과 서비스가 계속 버틴다는 것도 구별해야 해요. 길은 있어도, 그쪽으로 몰린 요청을 데이터베이스가 처리하지 못하면 응답은 느려지거나 실패해요. 캐시 서버의 오류를 처리하고 원본으로 우회하는 동작도 앱이 맡아야 하고요.
만료 시간은 정답의 유통기한일까요?
캐시에는 TTL(Time to Live), 곧 값을 보관할 시간을 붙일 수 있어요. 시간이 지나면 그 사본을 다시 쓰지 않고 새로 가져오는 거예요. 다만 5분 동안 보관하기로 했다고 5분 내내 최신인 것은 아니에요. 저장 직후 원본이 바뀌면 남은 시간 동안 예전 값이 보일 수 있죠.
이 때문에 데이터마다 허용할 수 있는 지연을 먼저 정해요. 상품 설명이 잠깐 늦게 바뀌는 것과 결제할 재고 수량이 늦게 바뀌는 것은 결과가 다르잖아요. AWS의 전자상거래 캐시 설명도 데이터의 성격에 맞춰 보관 시간을 나눠요. 주문을 확정하는 순간에는 어디의 값을 믿을지 별도로 정해야 해요.
잘 작동할수록 가려지는 부하
요청 10개 중 8개를 캐시가 처리하고 2개만 데이터베이스로 간다고 가정해 볼게요. 캐시가 전혀 답하지 못하면 같은 요청 10개가 모두 원본으로 향해요. 전체 방문자는 그대로인데 원본이 받는 요청은 다섯 배가 되는 셈이에요. 아래에서 캐시 상태를 바꿔 보면 차이가 보여요.
같은 요청 10개를 두 상태로 비교해 보세요.
캐시에서 응답 8개
원본으로 이동 2개
캐시가 8개에 답하면 원본은 2개를 받아요. 캐시가 비어 10개 모두 원본으로 가면, 같은 방문자 수에서도 원본 요청은 다섯 배가 돼요.
10개 중 8개는 캐시에서, 2개는 원본에서 처리해요.
동시에 들어온 요청의 첫 조회를 단순화한 예시예요. 실제 적중률이나 처리량을 측정한 값은 아니에요.
이 수치는 관계를 설명하기 위한 예시예요. 실제 결과는 요청 종류, 캐시를 다시 채우는 속도, 원본의 처리 여유에 따라 달라져요. 한 번의 요청이 데이터베이스 질의 여러 개를 만들 수도 있으니 적중률 하나로 용량을 계산할 수는 없어요.
값을 비슷한 시각에 채우고 같은 TTL을 주면 만료도 한꺼번에 몰릴 수 있어요. 값을 보관하는 시간을 조금씩 흩뜨리면 그 시점을 나눌 수 있죠. 같은 값을 기다리는 요청을 묶어 원본에 한 번만 묻는 방법도 있어요. 앞의 방법은 요청이 몰리는 시각을 나누고, 뒤의 방법은 중복된 질의 자체를 줄여요.
| 채우는 방식 | 편해지는 점 | 따로 해결할 문제 |
|---|---|---|
| 읽을 때 채우기 | 실제로 찾는 값만 보관 | 첫 조회와 원본 변경 뒤의 오래된 값 |
| 쓸 때 함께 갱신하기 | 변경을 사본에 빨리 반영 | 두 저장의 실패, 새 캐시의 빈 값 |
| 만료 시점을 흩뜨리기 | 동시에 원본으로 몰리는 요청 완화 | 데이터마다 허용할 수 있는 오래됨 |
AWS의 질의 결과 캐싱 문서를 읽을 때도 저장 공간보다 이 질문을 먼저 떠올리면 좋아요. 이 결과는 얼마나 자주 반복되고, 언제 바뀌며, 사본이 없을 때도 다시 계산할 수 있을까요? 앞에 저장소 하나를 더 두는 일이 원본의 책임을 없애 주지는 않아요.
저는 캐시를 붙이기 전에 멈췄어요
제가 만드는 앱에서도 AI가 만든 문장을 다시 계산하지 않도록 캐시를 붙일까 고민했어요. 그런데 사람마다 다른 문장을 받다 보니 같은 요청이 반복될 여지가 생각보다 작았어요.
그때는 캐시를 추가하는 대신 계산을 다른 경로로 넘길 수 있게 하는 쪽을 골랐어요. 캐시가 유용하지 않아서가 아니라, 제가 줄이려던 일이 얼마나 반복되는지부터 봐야 했던 거예요. 이제 캐시 이야기를 하면 빨라질 응답과 함께, 캐시가 비었을 때 돌아갈 곳도 떠올려요.