갱신 대신 삭제를 쓰는 것이 핵심입니다. 삭제는 멱등해서 여러 번 해도 결과가 같고, 순서가 뒤바뀌어도 안전합니다.
캐시 읽기와 쓰기 전략
전략 비교
| 전략 | 쓰기 경로 | 강점 | 약점 |
|---|
| Cache-Aside | DB 쓰고 캐시 삭제 | 단순, 캐시 장애 내성 | 첫 요청은 항상 미스 |
| Write-Through | 캐시와 DB 동시 쓰기 | 캐시가 항상 최신 | 쓰기 지연 증가 |
| Write-Back | 캐시만 쓰고 나중에 DB | 쓰기 폭주에 강함 | 캐시 유실 시 데이터 손실 |
| Write-Around | DB만 쓰고 캐시는 건너뜀 | 안 읽는 데이터 낭비 없음 | 쓰기 직후 읽기는 미스 |
Cache-Aside 읽기 흐름
GET key -> 미스
-> DB SELECT
-> SET key value EX ttl
-> 응답
왜 지우는 쪽이 안전한가
값을 고칠 때 캐시를 새 값으로 덮는 것과 지우는 것 중에 지우는 쪽이 대개 낫습니다.
| 방식 | 두 요청이 겹치면 |
|---|
| 새 값으로 덮는다 | 늦게 도착한 옛 값이 새 값을 덮을 수 있다 |
| 지운다 | 다음 읽기가 원본에서 가져오므로 옛 값이 남지 않는다 |
덮기는 순서를 보장할 수 없습니다. 두 갱신이 거의 동시에 일어나면 캐시에 어느 것이 남을지
알 수 없습니다. 지우면 그 문제가 사라집니다. 대신 다음 읽기 하나가 느립니다.
쓰기를 미루면 무엇을 잃나
캐시에 먼저 쓰고 원본에 나중에 쓰면 쓰기가 빨라집니다. 대가는 명확합니다.
캐시가 죽으면 아직 원본에 안 간 것이 사라진다
그래서 잃어도 되는 것에만 쓴다
조회수나 마지막 접속 시각 같은 것
돈이나 주문에는 쓰지 않습니다. 반대로 조회수를 매번 원본에 쓰면 그것이 DB 부하의 큰
몫이 되므로, 이쪽은 미루는 것이 맞습니다.
실무 포인트
- 대부분의 서비스 기본값은 Cache-Aside. 캐시가 죽어도 DB로 서비스가 유지된다
- 갱신 시 캐시를 새 값으로 덮기보다 삭제가 안전하다. 동시 갱신에서 옛 값이 덮어쓰는 경합을 피한다
- Write-Back은 주문과 결제처럼 유실이 치명적인 데이터에는 쓰지 않는다
- 작성 직후 상세 화면처럼 쓰고 바로 읽는 경로가 있으면 쓰기 시점에 캐시를 채우는 방식을 검토
Q.Cache-Aside와 Write-Through 중 무엇을 선택하고 왜 그렇게 판단했나요?
대부분 Cache-Aside 를 씁니다. 쓰기 경로가 단순하고 캐시 장애에 강합니다.
| 항목 | Cache-Aside | Write-Through |
|---|
| 읽기 | 캐시 확인 후 없으면 DB, 그리고 캐시에 채운다 | 같다 |
| 쓰기 | DB 갱신 후 캐시 삭제 | DB 와 캐시를 함께 갱신 |
| 캐시 장애 시 | DB 로 계속 동작한다 | 쓰기 경로가 함께 막힐 수 있다 |
| 첫 조회 | 미스가 난다 | 쓴 값이 이미 캐시에 있다 |
| 안 읽는 데이터 | 캐시를 차지하지 않는다 | 써 두었다가 안 읽히면 낭비 |
Write-Through 가 나은 경우는 쓰고 바로 읽는 패턴이 확실할 때입니다. 방금 쓴 값을 곧 조회하는 화면이면 첫 미스를 없앨 수 있습니다.
흔한 실수: Write-Through 를 정합성이 더 좋은 방식으로 답하는 것. 캐시와 DB 를 함께 쓰는 것은 원자적이지 않아서, 한쪽만 성공하는 창이 여전히 남습니다.
Q.데이터를 갱신할 때 캐시를 새 값으로 덮지 않고 삭제하는 이유는?
동시 쓰기에서 순서가 뒤바뀔 수 있기 때문입니다. 삭제는 여러 번 해도 결과가 같습니다.
요청 X 가 값을 10으로, Y 가 20으로 바꾸고 Y 가 나중이라고 해봅시다.
| 방식 | 벌어지는 일 | 결과 |
|---|
| 새 값으로 갱신 | X 의 캐시 쓰기가 늦게 도착한다 | DB 는 20, 캐시는 10. 만료까지 어긋난다 |
| 삭제 | 두 번 지워도 같다 | 캐시가 비어 다음 조회가 DB 에서 20을 읽는다 |
삭제가 순서에 둔감하다는 것이 핵심입니다.
그리고 안 읽힐 값을 미리 채우지 않는 이점도 있습니다. 갱신은 잦고 조회는 드문 데이터라면 갱신마다 캐시를 채우는 것이 낭비입니다.
흔한 실수: 삭제가 완벽하다고 보는 것. 조회가 DB 에서 옛 값을 읽은 뒤 삭제가 끼어들고, 그 다음 조회가 옛 값을 캐시에 채우는 좁은 창이 남습니다. 만료 시간을 함께 두어 수렴시킵니다.
Q.Write-Back의 위험은 무엇이고 어떻게 완화하나요?
캐시에만 쓰고 DB 반영을 미루는 방식이라, 캐시가 죽으면 아직 안 내려간 쓰기가 사라집니다.
| 위험 | 내용 |
|---|
| 유실 | 캐시 장애 시 미반영 데이터가 없어진다 |
| 정합성 | DB 를 직접 읽는 다른 시스템이 옛 값을 본다 |
| 복구 어려움 | 무엇이 반영되지 않았는지 알기 어렵다 |
| 완화 | 방법 |
|---|
| 캐시를 영속화한다 | 로그를 남기는 저장소를 쓴다 |
| 복제를 둔다 | 노드 하나가 죽어도 남는다 |
| 반영 지연을 짧게 | 유실 범위를 줄인다 |
| 유실 가능한 데이터에만 쓴다 | 조회수, 좋아요 수 |
그래서 실무에서는 잃어도 되는 값에만 씁니다. 게시글 조회수를 캐시에서 올리고 주기적으로 DB 에 반영하는 식입니다. 잔액이나 주문에는 쓰지 않습니다.
흔한 실수: 쓰기 성능만 보고 도입하는 것. 얻는 것은 쓰기 지연 감소이고 내주는 것은 지속성입니다. 그 데이터를 잃어도 되는지가 판단 기준입니다.
Q.캐시 서버가 다운되면 서비스는 어떻게 동작해야 하나요?
DB 로 계속 동작해야 하고, 동시에 DB 가 쓸려나가지 않게 막아야 합니다.
| 준비 | 내용 |
|---|
| 캐시 실패를 미스로 처리 | 예외를 던지지 않고 DB 조회로 넘어간다 |
| 타임아웃을 짧게 | 캐시 응답을 오래 기다리면 그것이 지연 원인이 된다 |
| DB 보호 | 동시 조회 수 제한, 차단기, 요청 병합 |
| 축소 운영 | 무거운 기능을 잠시 끄고 핵심만 유지한다 |
| 복구 시 점진 재적재 | 한꺼번에 채우면 DB 가 다시 부하를 받는다 |
세 번째가 실제 장애의 관건입니다. 히트율 90%로 돌던 서비스가 캐시를 잃으면 DB 부하가 10배가 되어, 캐시 장애가 DB 장애로 번집니다.
흔한 실수: 캐시 조회 실패를 예외로 던져 요청을 실패시키는 것. 캐시는 없어도 동작해야 하는 계층입니다. 그리고 타임아웃을 길게 두면 캐시가 느릴 때 전체 응답이 그만큼 느려집니다.
먼저 스스로 답해보고 아래 답변과 견줘보세요. 막히는 부분은 문제로 확인할 수 있어요.
읽었으면 문제로 확인해보세요
캐시/Redis 문제를 풀면 틀린 문제가 자동으로 노트에 쌓입니다. 가입 없이 5문제를 먼저 풀어볼 수도 있어요.