Contents
see List작성일 2025. 11. 30.
Redis 캐싱 전략 - Cache Aside, Write Through, Write Behind
Redis를 활용한 캐싱 전략을 이해하면 애플리케이션 성능을 크게 향상시킬 수 있습니다.
캐싱 전략 비교
| 전략 | 읽기 | 쓰기 | 일관성 |
|---|---|---|---|
| Cache Aside | 캐시 → DB | DB 직접 | 낮음 |
| Write Through | 캐시 | 캐시 + DB 동시 | 높음 |
| Write Behind | 캐시 | 캐시 → 비동기 DB | 중간 |
| Read Through | 캐시 자동 로드 | - | 중간 |
Cache Aside (Lazy Loading)
// 읽기
public User getUser(Long id) {
String key = "user:" + id;
User user = redisTemplate.opsForValue().get(key);
if (user == null) { // Cache Miss
user = userRepository.findById(id);
redisTemplate.opsForValue().set(key, user, Duration.ofHours(1));
}
return user;
}
// 쓰기 (캐시 무효화)
public void updateUser(User user) {
userRepository.save(user);
redisTemplate.delete("user:" + user.getId()); // 캐시 삭제
}Write Through
// 쓰기 시 캐시와 DB 동시 업데이트
@Transactional
public void updateUser(User user) {
userRepository.save(user);
redisTemplate.opsForValue().set("user:" + user.getId(), user);
}Write Behind (Write Back)
// 캐시에 먼저 쓰고 나중에 DB 동기화
public void updateUser(User user) {
redisTemplate.opsForValue().set("user:" + user.getId(), user);
// 비동기로 DB 동기화 (배치 처리)
asyncDbWriter.enqueue(user);
}
// 주기적으로 배치 처리
@Scheduled(fixedRate = 5000)
public void flushToDatabase() {
List<User> users = asyncDbWriter.drainQueue();
userRepository.saveAll(users);
}TTL (Time To Live) 설정
// 고정 TTL
redisTemplate.opsForValue().set(key, value, Duration.ofMinutes(30));
// 조건부 TTL
long ttl = isPopular(id) ? 3600 : 300; // 인기 데이터는 1시간
redisTemplate.expire(key, Duration.ofSeconds(ttl));캐시 Stampede 방지
// 분산 락으로 중복 조회 방지
public User getUserSafe(Long id) {
String key = "user:" + id;
User user = redisTemplate.opsForValue().get(key);
if (user == null) {
String lockKey = "lock:user:" + id;
boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, "1", Duration.ofSeconds(10));
if (locked) {
try {
user = userRepository.findById(id);
redisTemplate.opsForValue().set(key, user);
} finally {
redisTemplate.delete(lockKey);
}
} else {
Thread.sleep(100); // 대기 후 재시도
return getUserSafe(id);
}
}
return user;
}Spring Cache 통합
@Cacheable(value = "users", key = "#id")
public User getUser(Long id) {
return userRepository.findById(id);
}
@CacheEvict(value = "users", key = "#user.id")
public void updateUser(User user) {
userRepository.save(user);
}
@CachePut(value = "users", key = "#user.id")
public User createUser(User user) {
return userRepository.save(user);
}전략 선택 가이드
- Cache Aside: 읽기 위주, 단순한 구현
- Write Through: 쓰기 일관성 중요
- Write Behind: 쓰기 성능 중요, 약간의 데이터 손실 허용
database
| No | 작성일 | Title |
|---|---|---|
| 2189 | 2026. 02. 11. | Vector Database 비교와 활용 전략 |
| 2188 | 2026. 02. 11. | PostgreSQL 17 신기능과 성능 최적화 |
| 2187 | 2026. 02. 11. | Supabase Edge Functions 실전 활용 가이드 |
| 2121 | 2026. 02. 11. | Time-Series DB 비교: TimescaleDB vs InfluxDB vs QuestDB |
| 2120 | 2026. 02. 11. | Supabase Edge Functions와 실시간 구독 활용 |
| 2119 | 2026. 02. 11. | NewSQL 데이터베이스: CockroachDB vs TiDB 비교 |
| 2118 | 2026. 02. 11. | SQL 성능 최적화: 실행 계획 분석부터 인덱스 설계까지 |
| 2117 | 2026. 02. 11. | 데이터베이스 마이그레이션 전략과 도구 비교 |
| 2116 | 2026. 02. 11. | DuckDB: 분석용 인메모리 DB의 부상 |
| 2115 | 2026. 02. 11. | Redis 8.0과 실시간 데이터 처리 패턴 |