0.1초를 위한 전쟁, 캐시(Cache)의 역할
웹 서비스의 성능 병목은 90% 이상 데이터베이스(DB)에서 발생합니다. 디스크 기반의 DB는 태생적으로 메모리 기반의 애플리케이션 속도를 따라갈 수 없기 때문입니다. 이때 구원투수로 등장하는 것이 Redis나 Memcached같은 인메모리(In-Memory) 캐시 솔루션입니다.
하지만 캐시를 단순히 "DB 앞에 두는 것"만으로는 충분하지 않습니다. 애플리케이션이 캐시와 DB 사이에서 데이터를 어떻게 읽고 쓸 것인가에 대한 전략, 즉 캐싱 패턴(Caching Pattern)을 제대로 설계하지 않으면 오히려 데이터가 꼬이거나 성능이 저하될 수 있습니다. 오늘은 가장 널리 쓰이는 두 가지 전략, Look-aside와 Write-through를 비교 분석해 봅니다.
Look-aside (Lazy Loading): 가장 대중적인 읽기 전략
현업에서 가장 많이 사용되는 방식입니다. 이름처럼 캐시를 옆(Aside)에 두고 필요할 때만 데이터를 가져오는 구조입니다. '지연 로딩(Lazy Loading)'이라고도 불립니다.
작동 메커니즘
- Cache Hit: 애플리케이션은 먼저 캐시에 데이터가 있는지 확인합니다. 있으면 바로 가져옵니다. (매우 빠름)
- Cache Miss: 캐시에 데이터가 없으면, 애플리케이션이 DB에 직접 쿼리를 날려 데이터를 가져옵니다.
- Update: DB에서 가져온 데이터를 캐시에 저장하고, 사용자에게 반환합니다.
장점: 안정성과 효율성
- 장애 대응력: Redis가 다운되더라도 서비스는 멈추지 않습니다. 모든 요청이 DB로 직접 가서 부하가 늘어나긴 하겠지만, 서비스 자체는 계속 운영될 수 있습니다.
- 리소스 절약: 실제로 사용자가 요청한 데이터만 캐시에 저장됩니다. 불필요한 데이터가 메모리를 차지하는 것을 막을 수 있습니다.
단점: 초기 지연과 정합성
- Cache Miss 비용: 처음 조회되는 데이터는 캐시 확인 → DB 조회 → 캐시 저장이라는 3단계를 거쳐야 하므로 초기 응답 속도가 느립니다. 이를 해결하기 위해 서비스 오픈 전에 미리 데이터를 넣어두는 Cache Warming 작업을 하기도 합니다.
- 데이터 불일치: DB의 데이터가 변경되었을 때, 캐시에는 여전히 옛날 데이터가 남아있을 수 있습니다. 따라서 적절한 TTL(만료 시간) 설정이 필수입니다.
Write-through: 데이터 일관성이 최우선
데이터를 쓸 때(Write) 캐시와 DB에 동시에 업데이트하는 방식입니다. 캐시는 항상 최신 상태를 유지하며, DB와 동기화됩니다.
작동 메커니즘
- 애플리케이션이 데이터를 저장할 때, 캐시와 DB 양쪽에 모두 씁니다.
- 두 단계가 모두 성공해야 저장이 완료된 것으로 봅니다.
- 데이터를 읽을 때는 항상 캐시에서 가져옵니다.
장점: 데이터 정합성
- 항상 최신 데이터: DB와 캐시가 실시간으로 동기화되므로, 사용자는 언제나 최신의 데이터를 조회할 수 있습니다. 데이터 불일치(Inconsistency) 문제가 거의 발생하지 않습니다.
- 빠른 읽기: 데이터가 항상 캐시에 준비되어 있으므로, Look-aside 방식에서 발생하는 Cache Miss 페널티가 없습니다.
단점: 쓰기 지연과 리소스 낭비
- 쓰기 속도 저하: 데이터를 두 군데(캐시+DB)에 써야 하므로 저장 시간이 길어집니다. 쓰기 작업이 빈번한 서비스에서는 성능 저하의 원인이 될 수 있습니다.
- 메모리 낭비: 자주 조회되지 않는 데이터(예: 회원가입 후 한 번도 로그인 안 한 유저 정보)까지 무조건 캐시에 올라가므로, 비싼 메모리 자원을 낭비하게 됩니다. 이를 막기 위해 TTL을 설정하여 사용하지 않는 데이터를 주기적으로 삭제해야 합니다.
Write-back (Write-behind): 극강의 쓰기 성능(참고)
Write-through보다 더 공격적인 방식입니다. 데이터를 캐시에만 먼저 쓰고, 건바이건이 아니라 일정 주기로 모아서 DB에 비동기적으로 저장합니다.
- 장점: 쓰기 쿼리가 100개 들어와도 캐시에만 쓱 쓰고 끝내므로 속도가 비약적으로 빠릅니다. DB 부하를 획기적으로 줄일 수 있습니다.
- 치명적 단점: 캐시 서버가 장애로 꺼지면, 아직 DB에 저장되지 못한 데이터는 영구적으로 유실됩니다. 로그 수집이나 조회수 카운트처럼 일부 데이터가 날아가도 치명적이지 않은 경우에만 사용해야 합니다.
무엇을 선택해야 할까?
정답은 '서비스의 특성'에 달려 있습니다.
- 일반적인 웹 서비스 (게시판, 상품 목록): Look-aside 패턴을 기본으로 사용하세요. 구현이 쉽고 DB 부하를 효과적으로 분산시킵니다. 여기에 적절한 TTL을 설정하면 데이터 불일치 문제도 대부분 해결됩니다.
- 금융 거래, 게임 아이템 등 정합성이 중요한 데이터: Write-through 패턴을 고려하세요. 쓰기 속도를 약간 희생하더라도 데이터의 정확성이 중요하다면 이 방식이 안전합니다.
- 대용량 로그, 실시간 위치 정보: 데이터 유실을 감수하고서라도 빠른 처리가 필요하다면 Write-back을 사용할 수 있습니다.
현업에서는 보통 Look-aside를 기본으로 하되, 빈번하게 변경되는 특정 데이터에 대해서만 Write-through를 혼합하여 사용하는 하이브리드 전략을 많이 채택합니다.