Redis와 연결하려면 Spring Data Redis API 세트만 사용하면 된다고 합니다.
여기서 말하는 Spring Data Redis API는 Redis에 대한 활성 연결 작업을 위한 패키지인데 org.springframework.data.redis.connection 패키지와 해당 RedisConnection 및 RedisConnectionFactory 인터페이스를 말하는 것이라고 합니다.
이는 build.gradle setting을 통해서 획득할 수 있습니다.
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
RedisConnection은 Redis와 통신을 하기 위한 core building block을 제공한다고 합니다. Redis와 연결을 하려면 결국 RedisConnection을 획득해야 한다는 것이겠죠.
RedisConnection은 RedisConnectionFactory를 통해 생성되며, 팩토리는 PersistenceExceptionTraslator 객체로 동작을 합니다. 즉 일단 선언되면 transparent exception translation을 수행할 수 있다고 합니다. 예를들어 @Repository annotation과 AOP를 이용하여 exception translation을 수행할 수 있다고 합니다.
포인트는 RedisConnectionFactory를 통해 RedisConnection을 획득할 수 있다는 것입니다.
RedisConnectionFactory를 이용하는 방법은 적절한 구현체를 사용하는 것인데 RedisConnectionFactory 의 구현체로 Lettuce와 Jedis에 대해서 간략하게 보여주고 있습니다.
안타깝게도 현재 모든 커넥터 (RedisConnectionFactory 구현체) 가 모든 Redis 기능을 지원하는 것은 아닙니다. 기본 라이브러리에서 지원하지 않는 연결 API에서 메서드를 호출하면 UnsupportedOperationException이 발생합니다. 아래 그림에서는 개별 Redis 커넥터에서 지원하는 기능을 설명합니다.
Jedis는 예전부터 Java의 표준 Redis Client로 사용되었습니다. 그래서 예전 프로젝트는 대부분 Java & Redis 예제가 Jedis로 되어 있었습니다. 하지만 현재는 대부분 Lettuce를 이용하는 추세인데 Lettuce가 Jedis에 비해 성능면에서 많은 이점이 있기 때문입니다.
둘의 성능테스트를 비교한 글이 있어서 URL을 추가합니다.
https://jojoldu.tistory.com/418
결과적으로 Redis를 사용하기 위해서는 Connection이 필요하며
Connection은 ConnectionFactory의 구현체를 이용해서 얻을 수 있습니다.
ConnectionFactory의 구현체는 두가지가 있었지만 성능상으로 뛰어 LettuceConnectionFactory를 사용하도록 하겠습니다.
1. Lettuce Connector 획득 방법
Lettuce는 Netty (비동기 이벤트 기반 고성능 네트워크 프레임워크) 기반의 Redis 클라이언트입니다.
비동기로 요청을 처리하기 때문에 고성능을 자랑합니다. 고성능을 자랑하는 LettuceConnectionFactory를 생성하는 방법을 알아보겠습니다.
@Configuration
class AppConfig {
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(new RedisStandaloneConfiguration("server", 6379));
}
}
2. RedisTemplate을 이용하는 작업
대부분의 사용자는 RedisTemplate을 사용할 가능성이 높습니다. RedisTemplate은 Redis module의 central class 이며 고도로 추상화 되어 있기 때문에 사용자들은 특별한 설정 없이 사용할 수 있습니다.
또한 레디스는 편하게 작업할 수 있도록 Redis template의 서브인테페이스인 Operations View를 제공하고 있습니다. 이 Operation view는 Redis 명령을 구현하고, Type Safety를 제공하며, 유형에 특화된 메서드를 제공하여 개발 작업을 단순화해줍니다.
RedisTemplate는 대부분의 작업에 대해 Java 기반 serializer를 사용합니다. 이는 redistemplate을 통해 저장되거나 읽히는 모든 객체가 java를 통해 직렬화 또는 역직렬화 될 수 있다는 것입니다.
직렬화 메커니즘은 변경할 수 있으며 Redis 모듈은 여러 구현체를 제공하는데 org.springframework.data.redis.serialize 패키지에서 확인할 수 있습니다.
자세한 내용은 아래 url에서 확인할 수 있습니다.
https://docs.spring.io/spring-data/redis/docs/2.7.12/reference/html/#redis:serializer
Redis에 저장된 데이터는 기본적으로 바이트(byte) 형태입니다. Redis 자체는 다양한 type을 지원하지만, 대부분은 데이터가 저장되는 방식을 나타내는 것이며, 데이터가 어떤 값을 나타내는지에 대해서는 사용자가 결정해야 합니다.
Spring Data에서는 사용자(사용자 정의) 유형과 원시 데이터 간의 변환(양방향)을 org.springframework.data.redis.serializer 패키지에서 처리합니다.
이 패키지에는 직렬화 과정을 처리하는 두 가지 유형의 직렬화기(serializer)가 있습니다:
- RedisSerializer를 기반으로 Two-way serializers
- RedisElementReader와 RedisElementWriter를 사용하는 Element readers and writers
이러한 변형들 간의 주요 차이점은 RedisSerializer가 주로 byte[]로 직렬화하는 반면, readers and writers는 ByteBuffer를 사용한다는 것입니다.
여러 구현체가 제공됩니다. (이 문서에서 이미 언급된 것 두 가지 포함):
- RedisCache와 RedisTemplate에서 기본적으로 사용되는 JdkSerializationRedisSerializer.
- StringRedisSerializer
- Spring OXM 지원을 통해 객체/XML 매핑에 OxmSerializer를 사용
- 데이터를 JSON 형식으로 저장하기 위해 Jackson2JsonRedisSerializer 또는 GenericJackson2JsonRedisSerializer를 사용할 수도 있습니다.
3. Hash mapping
Redis는 다양한 데이터 구조를 사용하여 데이터를 저장할 수 있습니다. Jackson2JsonRedisSerializer를 사용하여 객체를 JSON 형식으로 변환할 수 있습니다. 일반적으로 JSON은 평범한 키를 사용하여 값으로 저장될 수 있습니다. Redis 해시를 사용하면 구조화된 객체의 더 복잡한 매핑을 구현할 수 있습니다. Spring Data Redis는 데이터를 해시에 매핑하기 위해 다양한 전략을 제공합니다 (사용 사례에 따라):
직접 매핑: HashOperations와 serializer를 사용합니다.
Redis Repositories를 사용합니다.
HashMapper와 HashOperations를 사용합니다.
4. Hash Mapper
spring-data-redis 공식 문서 10.9.1에 hash mappers에 관한 설명이 나옵니다. 두가지 기능에 대한 설명과 간단한 사용법에 대한 예시가 있었습니다.
toHash : 객체를 redis hash가 사용할 수 있는 map 형태로 변경
fromHash: map 형태를 다시 객체로 변경
public class StudyHashMapping {
private final RedisTemplate<String, Object> redisTemplate;
private HashOperations<String, byte[], byte[]> hashOperations;
HashMapper<Object, byte[], byte[]> mapper = new ObjectHashMapper();
public StudyHashMapping(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
this.hashOperations = redisTemplate.opsForHash();
}
public void writeHash() {
String key = "1";
Person person = new Person();
person.setFirstname("Teddy");
person.setLastname("Bear");
// Convert an object to a map that can be used with Redis hashes.
Map<byte[], byte[]> mappedHash = mapper.toHash(person);
hashOperations.putAll(key, mappedHash);
}
public Person loadHash(String key) {
Map<byte[], byte[]> loadedHash = hashOperations.entries(key);
return (Person) mapper.fromHash(loadedHash);
}
}
10.9.2 Jackson2HashMapper
Jackson2HashMapper는 FasterXML Jackson을 사용하여 도메인 객체를 Redis 해시 매핑으로 제공합니다.
Jackson2HashMapper는 top-level properties을 해시 필드 이름으로 매핑하고 선택적으로 구조를 flatten 하게 만들 수 있습니다. 간단한 유형은 간단한 값으로 매핑됩니다. 복잡한 유형 (nested objects, collections, maps 등등)은 중첩된 JSON으로 표현됩니다.
flatten 하게 만들는 것은 모든 중첩된 속성에 대해 개별 해시 항목을 생성하고, 가능한 한 복잡한 유형을 단순한 유형으로 해결합니다.
public class StudyJackson2HashMapper {
private final RedisTemplate<String, Object> redisTemplate;
private final HashOperations<String, String, Object> hashOperations;
private Jackson2HashMapper mapper = new Jackson2HashMapper(true);
public StudyJackson2HashMapper(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
this.hashOperations = redisTemplate.opsForHash();
}
public void writeHash(String key, Person person) {
// Convert an object to a map that can be used with Redis hashes.
Map<String, Object> mappedHash = mapper.toHash(person);
hashOperations.putAll(key, mappedHash);
}
public com.example.springdataredis.jackson2hashmapper.Person loadHash(String key) {
Map<String, Object> loadedHash = hashOperations.entries(key);
return (Person) mapper.fromHash(loadedHash);
}
}
@Resource(name="redisTemplate")
10.4.4. Write to Master, Read from Replica