
Resilience4j분산 시스템에서 성능을 높이는 비법, 로컬 캐시를 알고 계신가요?🤔
오늘은 데이터베이스 부하를 줄이고 빠른 응답 속도를 제공하는 로컬 캐시의 필요성에 대해 소개합니다!
분산 시스템에서는 데이터베이스 호출을 줄이기 위해 로컬 캐시를 활용하는 것이 중요합니다.
이를 통해 데이터베이스의 부하를 줄이고, 빠른 데이터 접근을 가능하게 하여 서비스 성능을 크게 향상시킬 수 있습니다.
로컬 캐시는 네트워크 트래픽을 줄여 빠른 응답 속도를 제공하며, 글로벌 캐시와는 다른 특성을 갖고 있습니다.
각 캐시의 특성을 이해하고, 환경에 맞는 적절한 캐시 전략을 선택하는 것이 중요합니다.
로컬 캐시와 Redis 캐시를 목적에 맞게 구분하여 사용하고, CacheManager를 통해 두 캐시 시스템을 통합하여 효율적으로 관리할 수 있습니다.
이렇게 하면 데이터 조회 속도를 개선하고, 서버 성능을 최적화할 수 있습니다.
문제: 분산 환경에서 데이터베이스와 캐시 간 데이터 일관성 문제가 발생.
예: 상품의 재고 정보가 갱신되었으나, 캐시에 반영되지 않아 주문 과정에서 오류 발생.
해결 전략
코드 예시 - 이벤트 기반 캐시 무효화
@Component
class CacheInvalidationListener {
@Autowired
private lateinit var multiLevelCacheManager: MultiLevelCacheManager
@KafkaListener(topics = ["product-update-topic"])
fun onProductUpdate(event: ProductUpdateEvent) {
// 변경된 데이터를 캐시 무효화
multiLevelCacheManager.invalidate(event.productId)
}
}
data class ProductUpdateEvent(
val productId: String,
val updatedAt: LocalDateTime
)
문제: Redis 노드가 과부하 상태로 장애 발생.
해결 전략
코드 예시 - 서킷 브레이커 적용
@Component
class ResilientCacheService {
private val circuitBreaker = CircuitBreaker.ofDefaults("redis")
@Autowired
private lateinit var redisClient: RedissonClient
@CircuitBreaker(name = "redis", fallbackMethod = "fallbackToDatabase")
fun getFromCache(key: String): String? {
val bucket = redisClient.getBucket<String>(key)
return bucket.get()
}
// Redis 장애 시 데이터베이스 조회로 전환
fun fallbackToDatabase(key: String, e: Exception): String? {
println("Redis 장애 발생: $e")
// 데이터베이스 조회 로직
return "데이터베이스에서 조회된 값"
}
}
문제: 캐시 히트율이 50% 이하로 낮아짐 → 캐시 활용이 비효율적.
해결 전략
코드 예시 - Micrometer로 캐시 모니터링
@Component
class CacheMetricsCollector {
private val hitCounter = Counter.builder("cache_hit_count").register(Metrics.globalRegistry)
private val missCounter = Counter.builder("cache_miss_count").register(Metrics.globalRegistry)
fun recordHit() {
hitCounter.increment()
}
fun recordMiss() {
missCounter.increment()
}
fun getHitRatio(): Double {
val total = hitCounter.count() + missCounter.count()
return if (total > 0) hitCounter.count() / total else 0.0
}
}
코드 예시 - Redis 클러스터 샤딩 설정
spring:
redis:
cluster:
nodes:
- redis-node1:6379
- redis-node2:6379
- redis-node3:6379
max-redirects: 3
문제: 캐시에 민감한 사용자 데이터(예: 결제 정보)가 저장될 경우 데이터 유출 위험.
해결 전략
- AES256 암호화 후 캐시에 저장.
- 데이터 복호화는 사용자 요청 시점에 수행.
코드 예시 - 데이터 암호화와 복호화
class SecureCacheService {
private val encryptionKey = "1234567890123456" // AES256 Key
fun encrypt(data: String): String {
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(encryptionKey.toByteArray(), "AES"))
return Base64.getEncoder().encodeToString(cipher.doFinal(data.toByteArray()))
}
fun decrypt(data: String): String {
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(encryptionKey.toByteArray(), "AES"))
return String(cipher.doFinal(Base64.getDecoder().decode(data)))
}
}