-
Spring Data Redis 설정(1) - RedisTemplate 설정Spring Boot 2025. 4. 27. 16:57
- Redis 설정 관련하여 아키텍처, 직렬화/역직렬화에 대한 기록
1. Redis 아키텍처
- 설정에 앞서 아키텍처에 따라 Connection 설정 방법에 차이가 있어 아키텍처를 살펴본다.
- Redis를 사용하는 아키텍처는 크게 4가지가 있다. (참고 - https://docs.spring.io/spring-data/redis/reference/redis/connection-modes.html)
1) StandAlone
- 1대의 Redis 서버
2) Redis Replication (master/slave 구조)
- master (읽기/쓰기), slave (master 데이터 복사 - 읽기전용)로 구성
- master → slave 복사시간이 1-2초 소요되는 단점이라 잘 쓰이지 않는 구조이다.
- redis cluster를 잘 운영하지 못할때 사용한다.
master, slave 구조 3) Redis Sentinel
- master/replica 구조에서 primary 장애 시 자동 장애 조치(auto failover)를 수행 구조
- sentinel 노드 과반 수 이상이 master 장애를 감지하면 slave 하나를 master로 승격
- 지속적으로 모티터링/감시만 하는 node(sentinel)를 따로 관리해줘야한다.
- client가 올바른 노드를 찾을 수 있도록 도움
- 최소 3개 이상의 노드를 권장
- 감시 역할은 Redis와 동일한 노드에 구성해도 되고, 별로로 구성해도 됨
- 지속적으로 모티터링/감시만 하는 node(sentinel)를 따로 관리해줘야한다.
- 일반적으로 단일 master로 구성함,
- master 하나가 모든 쓰기 작업을 처리해야하는 단점 있음
- 대규모 쓰기 작업시 병목 발생
- Slave에 복제되는 Latency 증가
Sentinel 4) Redis Cluster
- 최소 3대의 마스터 노드 필요, 각 노드 당 최소 Slave node 필요함
- 데이터는 16384개 슬롯으로 나뉘어 특정 Master에 할당
- 모든 데이터는 master 단위로 sharding되고, slave 단위로 복제됨 (즉 슬롯으로 sharding함)
- 설정과 관리가 매우 복잡하고 특정 작업(ex - 다중키 작업)은 일부 제한이 있을 수 있음
Cluster 구조 application.yml에서 어떤 Connection mode인지에 따라서 다른 환경 변수를 사용할 수 있다.
Spring Data Redis에서 제공하는 환경변수 3. Redis 설정
- OMS는 본격적으로 Redis를 활용하는 구조가 아니기 때문에 StandAlone으로 설정을 진행한다.
Redis 접속 설정
- Lettue, Jedis 선택지가 있으나 Lettue가 성능상 더 좋다.
@Configuration class RedisConfig { @Bean fun connectionFactory(): LettuceConnectionFactory { val redisConfig = RedisStandaloneConfiguration("localhost", 6379) val clientConfig = LettuceClientConfiguration .builder() .commandTimeout(Duration.ofSeconds(30)) // 명령 실행 타임아웃 .clientOptions( ClientOptions .builder() .autoReconnect(true) // 자동 재연결 .socketOptions( SocketOptions .builder() .connectTimeout(Duration.ofSeconds(5)) // 네트워크 연결 타임아웃 .build(), ).disconnectedBehavior(ClientOptions.DisconnectedBehavior.REJECT_COMMANDS) // // 연결 끊겼을 때 처리 방식 .build(), ) .build() return LettuceConnectionFactory(redisConfig, clientConfig) } }
- disconnectedBehavior 옵션 다른 선택지
- DEFAULT: 요청을 큐에 쌓았다가 나중에 처리 (❗️주의: 메모리 소비 증가 가능)
- REJECT_COMMANDS: 연결이 없으면 명령을 즉시 거부 (→ 예외 발생)
- ACCEPT_COMMANDS: 연결이 끊겨도 명령을 계속 받아서 큐에 저장
RedisTemplate 설정
- Key-Value 구조가 <String, String> 이라면 StringRedisTemplate를 사용한다.
- 라이브러리 자체에서 최적화 설정이 되어 있기 때문이다.
@Bean("stringRedisTemplate") fun stringRedisTemplate( @Qualifier("connectionFactory") connectionFactory: LettuceConnectionFactory, ): StringRedisTemplate = StringRedisTemplate(connectionFactory)
라이브러리 내부 직렬화 최적화 1) RedisTemplate<String, Object> - 가장 기본적인 사용 방법
- 아래와 같이 String은 StringRedisSerializer로, Object는 GenericJackson2JsonRedisSerializer로 객체를 JSON 형태로 직렬화하여 저장한다.
@Bean public RedisTemplate<String, Object> redisTemplate() { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory()); // 직렬화 설정 template.setKeySerializer(new StringRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); return template; }
- 이 방식의 단점
- Json 객체를 그대로 바이트 배열로 변환하여 저장하므로, 저장 공간이 많이 사용됨
- 설정에 따라 직렬화 시에 클래스 정보가 포함되어 여러 문제가 생길 수 있음
- ObjectMapper를 사용하여 직렬화를 시도함
- 참고 - Spring Redis 직렬화의 한계 / https://mangkyu.tistory.com/402
- Redis에 저장 데이터가 크다면 직접 직렬화를 구현하여 압축 저장하는 방법도 있다.
2) RedisTemplate<String, ByteArray>로 구현하고 ObjectMapper를 커스텀하는 방식
- Redis에 byteArray로 관리하고, ObjectMapper를 직접 구현하여 저장 포멧을 최적화한다. (smile, avro..)
@Bean("objectRedisTemplate") fun objectRedisTemplate( @Qualifier("connectionFactory") connectionFactory: LettuceConnectionFactory, ): RedisTemplate<String, ByteArray> { val redisTemplate = RedisTemplate<String, ByteArray>().apply { keySerializer = StringRedisSerializer() hashKeySerializer = StringRedisSerializer() setConnectionFactory(connectionFactory) } return redisTemplate } @Bean("redisObjectMapper") fun redisObjectMapper(): ObjectMapper { return SmileMapper().registerModules(JavaTimeModule()) .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true) .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false) .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) .setSerializationInclusion(JsonInclude.Include.ALWAYS) .disable(MapperFeature.USE_ANNOTATIONS) }
- smile로 저장함으로 JSON 문자열 대비 2/3 절약된 크기 저장
- 전반적인 고성능
- 단점으로는 ObjecMapper 커스텀 구현 및 redis 저장소에서 데이터 읽기가 불편할 수 있다.
반응형'Spring Boot' 카테고리의 다른 글
Jobrunr 사용한 Springboot에 스케줄링 작업 구현 (0) 2025.01.30 [springboot 예외 처리 안티패턴 정리] Exception Handling Worst Practice (13) 2024.12.30 Lombok 사용시 주의점 (쓰지 말아야 할 Lombok) (3) 2024.11.12 springboot 애플리케이션 데이터베이스 초기화 (1) 2024.11.08 스프링 개발자가 가장 많이하는 실수 (0) 2024.06.19