Spring JPA Redis Cache 사용
캐시를 사용하는 이유는 데이터베이스 액세스 비용을 축소하여 성능을 향상 시키는데 있습니다.
Spring에서 제공하는 캐시를 사용하며 외부 캐시로 Redis를 설정하여 하는 방법을 알아 보겠습니다.
기타 내용:
Spring JPA(Hibernate)에는 1차 캐시와 2차 캐시가 있습니다.
먼저 1차 캐시는 Request 시 생성되는 영속성 컨텍스트 내에 엔티티를 보관하는 1차 캐시가 있습니다. 1차 캐시는 Response 후에 자동 삭제 되며,
최초 조회 시에는 데이터베이스에서 조회하고 이후 동일한 값(PK)일 경우 데이터베이스에 조회하지 않고 1차 캐시의 엔티티를 반환합니다.
2차 캐시는 공유 캐시(Shared Cache)라고도 부르며 내부적으로 Hibernate가 애플리케이션 레벨로 각 세션들이 모두 공유할 수 있는 캐시를 관리합니다.
Spring 설정에서 2차 캐시를 활성화 하면 1차 캐시 - 2차 캐시 - 데이터베이스 순으로 조회하게 됩니다.
주로 2차 캐시로 로컬 캐시인 ehcache를 많이 사용합니다.
설정
build.gradle
# local test redis
implementation group: 'it.ozimov', name: 'embedded-redis', version: '0.7.2'
# spring redis
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
application.yml
spring:
cache:
type: redis
redis:
host: localhost
port: 6380
connect-timeout: 10s
lettuce:
shutdown-timeout: 500ms
RedisConfig.java
@Configuration
@EnableCaching
@Slf4j
public class RedisConfig {
private final RedisProperties redisProperties;
public RedisConfig(RedisProperties redisProperties) {
this.redisProperties = redisProperties;
}
@Bean(name = "connectionFactory")
@Profile({"local", "dev", "test"})
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(redisProperties.getHost(), redisProperties.getPort());
}
@Bean(name = "cacheManager")
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheManager.RedisCacheManagerBuilder builder= RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(connectionFactory);
RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig()
.disableCachingNullValues()
.entryTtl(Duration.ofDays(30))
.computePrefixWith(CacheKeyPrefix.simple())
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
return builder.cacheDefaults(configuration).build();
}
Service
@Service
@RequiredArgsConstructor
public class LocationService {
private final LocationSidoRepository locationSidoRepository;
private final LocationSigunRepository locationSigunRepository;
private final LocationDongRepository locationDongRepository;
@Cacheable(cacheNames = "CACHE_DELIVERY_LOCATION_SIDO", cacheManager = "cacheManager")
@Transactional(readOnly = true)
public List<LocationSido> findAllSido() {
return this.locationSidoRepository.findAll();
}
@Cacheable(cacheNames = "CACHE_DELIVERY_LOCATION_SIGUN", key = "#sidoSeq", cacheManager = "cacheManager")
@Transactional(readOnly = true)
public List<LocationSigun> findAllSigunBySidoSeq(Long sidoSeq) {
return this.locationSigunRepository.findAllBySidoSeq(sidoSeq);
}
@Cacheable(cacheNames = "CACHE_DELIVERY_LOCATION_DONG", key = "#sigunSeq", cacheManager = "cacheManager")
@Transactional(readOnly = true)
public List<LocationDong> findAllDongBySigunSeq(Long sigunSeq) {
return this.locationDongRepository.findAllBySigunSeq(sigunSeq);
}
}
redis-cli에서 저장된 정보 확인
localhost:6380> keys *
1) "CACHE_DELIVERY_LOCATION_DONG::146"
2) "CACHE_DELIVERY_LOCATION_SIGUN::8"
3) "CACHE_DELIVERY_LOCATION_SIDO::SimpleKey []"
댓글남기기