1 Cache
- 캐시는 값비싼 연산 결과 또는 자주 참조되는 데이터를 메모리에 두고 뒤 이은 요청이 보다 빨리 처리될 수 있도록 하는 저장소입니다.
- 캐시 계층은 데이터가 잠시 보관되는 곳으로 데이터베이스보 다 훨씬 빠릅니다.
- 별도의 캐시를 두면 성능이 개선될 뿐 아니라 데이터베이스의 부하도 줄일 수 있습니다.
2 캐시 전략
- 캐시 전략은 다양하며 캐시할 데이터의 종류, 크기, 액세스 패턴에 맞는 캐시 전략을 선택해야 합니다.
2.1 Lazy Loading (Cache-Aside)
- Lazy Loading은 데이터가 요청될 때만 캐시에 로드되는 전략입니다.
- 이 전략은 Redis 공식 문서에서 'Cache-Aside'라고도 불립니다.
- 작동 방식:
- 애플리케이션이 캐시에서 데이터를 확인합니다.
- 캐시에 데이터가 있으면 (캐시 히트) 바로 반환합니다.
- 캐시에 데이터가 없으면 (캐시 미스) 데이터베이스에서 데이터를 조회합니다.
- 데이터베이스에서 가져온 데이터를 캐시에 저장하고 클라이언트에 반환합니다.
- 장점:
- 자주 사용되는 데이터만 캐시에 저장되어 메모리를 효율적으로 사용합니다.
- 구현이 상대적으로 간단합니다.
- 단점:
- 캐시 미스 시 데이터베이스 조회로 인한 지연이 발생할 수 있습니다.
- 데이터가 오래되었을 가능성이 있습니다.
2.2 Read-Through
- Read-Through는 캐시가 데이터베이스와의 상호작용을 관리하는 전략입니다.
- 작동 방식
- 애플리케이션은 항상 캐시에만 데이터를 요청합니다.
- 캐시에 데이터가 없으면, 캐시 자체가 데이터베이스에서 데이터를 로드합니다.
- 로드된 데이터를 캐시에 저장하고 애플리케이션에 반환합니다.
- 장점:
- 애플리케이션 코드가 단순화됩니다. (데이터베이스 상호작용 로직이 캐시 계층에 캡슐화됨)
- 데이터 일관성 관리가 더 쉽습니다.
- 단점:
- 캐시 미스 시 여전히 지연이 발생할 수 있습니다.
2.2.1 Lazy Loading vs Read-Through
- Lazy Loading과 Read-Through는 모두 캐시 전략이지만, 가장 큰 차이점은 "누가 데이터 로딩 로직을 관리하는가"입니다.
- Lazy Loading
- 애플리케이션이 직접 데이터베이스에서 데이터를 로드합니다.
- 즉 애플리케이션이 캐시와 데이터베이스 간의 상호작용을 관리합니다.
- Read-Through
- 캐시가 데이터베이스에서 데이터를 로드합니다.
- 즉 애플리케이션은 캐시와만 상호작용하며, 캐시가 데이터베이스와의 상호작용을 관리합니다.
2.3 Write-Through
- Write-Through 전략은 데이터를 캐시와 데이터베이스에 동시에 쓰는 방식입니다.
- Redis 공식 문서에 따르면, 이 전략은 데이터 일관성을 유지하는 데 효과적입니다.
- 작동 방식:
- 애플리케이션이 데이터를 쓸 때 먼저 캐시에 씁니다.
- 캐시는 즉시 이 데이터를 데이터베이스에 동기적으로 쓰기 합니다.
- 쓰기 작업이 완료되면 애플리케이션에 응답합니다.
- 장점:
- 데이터의 일관성이 보장됩니다.
- 읽기 작업이 항상 최신 데이터를 반환합니다.
- 단점:
- 쓰기 작업의 지연 시간이 증가할 수 있습니다.
- 자주 변경되는 데이터의 경우 데이터베이스에 불필요한 쓰기가 발생할 수 있습니다.
2.4 Write-Behind (Write-Back)
- Write-Behind 전략은 데이터를 먼저 캐시에 쓰고, 나중에 비동기적으로 데이터베이스에 쓰는 방식입니다.
- Redis 공식 문서에 따르면, 이 전략은 쓰기 성능을 크게 향상시킬 수 있습니다.
- 작동 방식:
- 애플리케이션이 데이터를 캐시에 씁니다.
- 쓰기 작업이 성공하면 즉시 애플리케이션에 응답합니다.
- 캐시는 나중에 (일정 시간 후 또는 일정 양의 데이터가 쌓였을 때) 비동기적으로 데이터베이스에 씁니다.
- 장점:
- 쓰기 작업의 지연 시간이 크게 감소합니다.
- 데이터베 이스 부하를 줄일 수 있습니다.
- 단점:
- 데이터 불일치가 발생할 수 있습니다. (캐시와 데이터베이스 간)
- 캐시 서버 장애 시 아직 데이터베이스에 쓰이지 않은 데이터가 손실될 위험이 있습니다.
3 적절한 캐시 전략 선택하기
- 각 캐시 전략에는 고유한 장단점이 있으며, 애플리케이션의 특정 요구에 따라 적합한 전략을 선택해야 합니다.
- 아래는 각 캐시 전략의 특징과 장단점을 비교한 내용입니다.
- 읽기 주도형 캐시 전략 (Read-Through Caching)
- 장점
- 빠른 읽기 성능 : 캐시에서 데이터를 직접 반환하므로 응답 시간이 매우 빠릅니다.
- 간단한 구현 : 캐시에 데이터가 없는 경우에만 데이터베이스에 접근하므로 구현이 비교적 간단합니다.
- 단점
- 캐시 미스 발생 시 지연 : 처음 요청 시 캐시에 데이터가 없으면 데이터베이스 접근으로 인해 응답이 지연될 수 있습니다.
- 캐시 일관성 문제 : 데이터베이스와 캐시 간의 데이터 일관성을 유지하기 위한 추가 메커니즘이 필요할 수 있습니다.
- 장점
- 쓰기 주도형 캐시 전략 (Write-Through Caching)
- 장점
- 데이터 일관성 : 캐시와 데이터베이스가 항상 동일한 데이터를 유지하므로 데이터 일관성이 보장됩니다.
- 간단한 데이터 관리 : 데이터가 항상 캐시에 최신 상태로 유지되므로 별도의 캐시 무효화 전략이 필요하지 않습니다.
- 단점
- 쓰기 성능 저하 : 모든 쓰기 작업이 캐시와 데이터베이스에 동시에 적용되므로 쓰기 성능에 부정적인 영향을 미칠 수 있습니다.
- 비용 증가 : 모든 데이터를 캐시에 저장해야 하므로 메모리 사용량이 증가할 수 있습니다.
- 장점
- 쓰기 지연 캐시 전략 (Write-Behind Caching)
- 장점
- 쓰기 성능 향상 : 데이터베이스에 대한 쓰기 작업이 지연되므로 빠른 쓰기 성능을 제공합니다.
- 효율적인 자원 사용 : 비동기적으로 데이터베이스를 업데이트하므로 자원을 효율적으로 사용할 수 있습니다.
- 단점
- 데이터 일관성 문제 : 데이터베이스와 캐시 간의 데이터 일관성 유지가 어렵고, 적절한 동기화 메커니즘이 필요합니다.
- 데이터 손실 위험 : 시스템 장애가 발생하면 데이터베이스에 기록되지 않은 캐시 데이터가 손실될 수 있습니다.
- 장점
3.1 강한 일관성이 필요한 경우
3.1.1 Write-Through 전략
- 강한 일관성이 필요한 경우에는 Write-Through 전략을 사용하는 것이 좋습니다.
- 이 전략은 데이터베이스와 캐시 간의 일관성을 보장하며, 데이터베이스에 직접 쓰기 작업을 수행합니다.
- 따라서 모든 데이터 변경 작업이 DB와 캐시에 동시에 적용됩니다.
- 모든 쓰기 작업에 오버헤드 발생, 트랜잭션 실패 시 캐시 불일치 가능성이 있습니다.
@Service
@Transactional
public class StrongConsistencyService {
@Autowired
private Repository repository;
@Autowired
private CacheManager cacheManager;
public Entity update(EntityDto dto) {
// 1. DB 업데이트
Entity updated = repository.save(convertToEntity(dto));
// 2. 캐시 즉시 업데이트 (동일 트랜잭션 내)
cacheManager.getCache("entityCache").put(updated.getId(), updated);
return updated;
}
}
- update 메서드에서 DB를 업데이트한 후 캐시를 즉시 업데이트합니다.
- 따라서 다음 findById 호출 시 캐시가 최신 데이터를 반환합니다.