Redis 와 캐싱 · 캐시 패턴(look-aside·write-through) · 이론
세 가지 쓰기 전략과 스탬피드
한 줄 요약
읽기 전략과 쓰기 전략은 서로 다른 축이다. 그리고 인기 있는 키일수록 만료 순간이 위험하다는 역설이 스탬피드의 핵심이다.
왜 이게 필요했나
캐시 패턴을 이야기할 때 사람들이 섞어 쓰는 세 이름이 있습니다. cache-aside, write-through, write-behind 입니다. 이것들은 대립하는 선택지가 아니라 서로 다른 질문에 대한 답입니다. 앞의 것은 읽기를 누가 채우는가에 대한 답이고, 뒤의 둘은 쓰기를 어떻게 전파하는가에 대한 답입니다.
cache-aside 는 애플리케이션이 캐시를 직접 관리합니다. 읽을 때 캐시를 보고, 없으면 원본에서 읽어 캐시에 넣습니다. 쓸 때는 원본을 갱신하고 캐시를 삭제합니다. 갱신이 아니라 삭제라는 점이 중요합니다 — 두 쓰기가 엇갈리면 캐시에 옛 값이 남을 수 있지만, 삭제는 그 위험이 없습니다.
write-through 는 쓰기가 캐시와 원본을 동시에 갱신합니다. 캐시가 항상 최신이라 읽기가 늘 빠릅니다. 대신 쓰기가 느려지고, 한동안 읽히지 않을 데이터까지 캐시에 채워 공간을 낭비합니다.
write-behind 는 캐시에만 쓰고 원본 반영을 나중에 배치로 합니다. 쓰기가 아주 빠르고 여러 쓰기를 모을 수 있지만, 반영 전에 캐시가 죽으면 데이터가 사라집니다. 유실을 감당할 수 있거나 별도의 내구성 장치가 있을 때만 씁니다.
어떻게 동작하나
세 전략을 한 표로 정리하면 이렇습니다.
| 전략 | 쓰기 속도 | 읽기 최신성 | 유실 위험 | 대표 상황 |
| --- | --- | --- | --- | --- |
| cache-aside | 보통 | 미스 후 최신 | 낮음 | 범용, 읽기 위주 |
| write-through | 느림 | 항상 최신 | 낮음 | 최신성이 중요 |
| write-behind | 매우 빠름 | 항상 최신(캐시 기준) | 높음 | 쓰기 폭주, 유실 허용 |
그리고 캐시에서 가장 악명 높은 함정이 스탬피드입니다. 초당 수천 번 읽히던 인기 키가 만료되는 그 찰나, 수천 개 요청이 동시에 미스를 겪고 모두 원본으로 몰려가 같은 값을 다시 계산합니다. 하나면 충분했을 조회가 수천 개로 폭증하고, 원본이 짓눌립니다. 최악의 경우 원본이 죽고 더 많은 미스가 나며 연쇄적으로 무너집니다.
역설은 이것입니다 — 인기 있는 항목일수록 위험합니다. 자주 읽히는 값일수록 만료 순간의 동시 미스가 크기 때문입니다.
완화책은 넷입니다. 첫째 잠금 또는 싱글플라이트 — 미스 때 첫 요청만 원본을 조회하고 나머지는 기다립니다. 둘째 TTL 지터 — 만료 시점을 흩뜨립니다. 셋째 stale-while-revalidate — 만료돼도 옛 값을 즉시 돌려주고 백그라운드에서 한 번만 갱신합니다. 넷째 확률적 조기 만료 — 만료가 임박할수록 높은 확률로 미리 갱신합니다.
실무에서는 조합합니다. 지터로 만료를 흩뜨리고, stale-while-revalidate 로 대기를 없애고, 잠금으로 갱신을 하나로 모읍니다.
현장에서 만나는 모습
잠금을 쓸 때 반드시 함께 챙길 것이 둘입니다. 잠금에 TTL 을 걸어 소유자가 죽어도 풀리게 하는 것, 그리고 소유권 토큰을 두어 남의 잠금을 실수로 풀지 않게 하는 것입니다. 후자를 빠뜨리면 A 의 잠금이 TTL 로 만료된 뒤 B 가 잡았는데, 뒤늦게 깨어난 A 가 B 의 잠금을 해제하는 일이 생깁니다.
다음 실습에서 할 것
느린 원본을 상대로 cache-aside 를 구현해 적중률을 재고 버전 키로 전체 무효화를 해 봅니다. 그다음 별도 실습에서 스탬피드를 실제로 일으키고 네 가지 방어를 하나씩 붙여 원본 호출 수를 숫자로 비교합니다.