반응형
기본
설명
- Spring Boot에서 Redis 연동하는 예제
- Java의 Redis Client는 Jedis, Lettuce가 있다.
- Jedis는 Deprecated되어 Spring Boot에서 별도 설정하지 않을 경우 기본 Client는 Lettuce를 사용한다.
- 별도 설정으로 Jedis 사용 가능하지만, Lettuce가 성능이 비교적 좋기 때문에 Lettuce를 많이 사용한다.
pom.xml 의존성 추가
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
application.properties
- Cluster와 연동시 설정
spring.redis.cluster.nodes=192.168.56.108:6379,192.168.56.109:6379,192.168.56.110:6379,192.168.56.108:6380,192.168.56.109:6380,192.168.56.110:6380
- Sentinel과 연동시 설정
spring.redis.sentinel.master=mymaster
spring.redis.sentinel.nodes=192.168.56.108:26379,192.168.56.109:26379,192.168.56.110:26379
- 단일 Redis 서버와 연동시 설정
spring.redis.host=192.168.56.21
spring.redis.port=6379
기본 예제
Test
@SpringBootTest
public class RedisOpsForTest {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@BeforeEach
public void setup() {
stringRedisTemplate.keys("*").forEach(key -> stringRedisTemplate.delete(key));
}
@Test
public void testString() {
stringRedisTemplate.opsForValue().set("test:string", "Hello World");
String result = stringRedisTemplate.opsForValue().get("test:string");
assertEquals("Hello World", result);
}
@Test
public void testList() {
stringRedisTemplate.opsForList().rightPush("test:list", "H");
stringRedisTemplate.opsForList().rightPush("test:list", "i");
List<String> result = stringRedisTemplate.opsForList().range("test:list", 0, -1);
assertEquals(List.of("H", "i"), result);
}
@Test
public void testSet() {
stringRedisTemplate.opsForSet().add("test:set", "H");
stringRedisTemplate.opsForSet().add("test:set", "i");
Set<String> result = stringRedisTemplate.opsForSet().members("test:set");
assertEquals(Set.of("H", "i"), result);
}
@Test
public void testSortedSet() {
stringRedisTemplate.opsForZSet().add("test:sorted:set", "H", 1);
stringRedisTemplate.opsForZSet().add("test:sorted:set", "i", 2);
stringRedisTemplate.opsForZSet().add("test:sorted:set", "!!", 3);
Set<String> result1 = stringRedisTemplate.opsForZSet().range("test:sorted:set", 0, -1);
Set<String> result2 = stringRedisTemplate.opsForZSet().rangeByScore("test:sorted:set", 2, 3);
assertEquals(Set.of("H", "i", "!!"), result1);
assertEquals(Set.of("i", "!!"), result2);
}
@Test
public void testHash() {
stringRedisTemplate.opsForHash().put("test:hash", "name", "john");
stringRedisTemplate.opsForHash().put("test:hash", "age", "25");
Set<Object> keys = stringRedisTemplate.opsForHash().keys("test:hash");
Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries("test:hash");
Object name = stringRedisTemplate.opsForHash().get("test:hash", "name");
Object age = stringRedisTemplate.opsForHash().get("test:hash", "age");
assertEquals(Set.of("name", "age"), keys);
assertEquals(Map.of("name", "john", "age", "25"), entries);
assertEquals("john", name);
assertEquals("25", age);
}
}
사용자 정의 Template 예제
RedisMessage
@NoArgsConstructor
@AllArgsConstructor
@Data
public class RedisMessage {
private String message1;
private String message2;
}
RedisConfig 클래스 추가
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new StringRedisSerializer());
return redisTemplate;
}
@Bean
public RedisTemplate<String, RedisMessage> redisMessageTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, RedisMessage> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(RedisMessage.class));
return redisTemplate;
}
}
데이터 추가 & 조회 예시
@RunWith(SpringRunner.class)
@SpringBootTest
public class RedisTest {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Autowired
private RedisTemplate<String, RedisMessage> redisMessageTemplate;
@Test
public void setText() {
redisTemplate.opsForValue().set("AAA", "안녕하세요");
String result = redisTemplate.opsForValue().get("AAA");
Assertions.assertEquals("안녕하세요", result);
}
@Test
public void setObject() {
redisMessageTemplate.opsForValue().set("BBB", new RedisMessage("BBB-1", "HelloWorld"));
RedisMessage result = redisMessageTemplate.opsForValue().get("BBB");
Assertions.assertEquals("BBB-1", result.getMessage1());
Assertions.assertEquals("HelloWorld", result.getMessage2());
}
}
@RedisHash 예제
설명
- Spring Data JPA처럼 객체를 기반으로 Redis 데이터를 관리하는 방법을 설명한다.
Student
@AllArgsConstructor
@NoArgsConstructor
@Data
@RedisHash(value = "student", timeToLive = 600L) // "student:{id}" 키로 저장, 600초의 expire 시간 지정
public class Student {
@Id
private String id;
private String name;
private Address address;
}
Address
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Address {
private String fullAddress;
private String zipCode;
}
StudentRepository
public interface StudentRepository extends CrudRepository<Student, String> {
}
Test
@SpringBootTest
public class RedisRepositoryTest {
@Autowired
private StudentRepository studentRepository;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@BeforeEach
public void setup() {
stringRedisTemplate.keys("*").forEach(key -> stringRedisTemplate.delete(key));
}
@Test
public void testRepository() {
Student student1 = new Student("1", "john", new Address("Seoul", "00000"));
Student student2 = new Student("2", "tyler", new Address("Incheon", "11111"));
studentRepository.save(student1);
studentRepository.save(student2);
assertEquals(student1, studentRepository.findById("1").get());
assertEquals(student2, studentRepository.findById("2").get());
}
}
Result
127.0.0.1:6379> keys *
1) "student:2"
2) "student"
3) "student:1"
127.0.0.1:6379> smembers student
1) "1"
2) "2"
127.0.0.1:6379> hgetall student:2
1) "_class"
2) "com.example.demo.Student"
3) "address.fullAddress"
4) "Incheon"
5) "address.zipCode"
6) "11111"
7) "id"
8) "2"
9) "name"
10) "tyler"
127.0.0.1:6379> ttl student:2
(integer) 576
@TimeToLive
설명
- @RedisHash 속성으로 timeToLive 값을 지정할 경우 각 데이터별로 만료 시간을 지정할 수 없다.
- 각 데이터별로 만료 시간을 지정할 수 있도록 필드를 두고 @TimeToLive 어노테이션을 달아 사용할 수 있다.
Student
@AllArgsConstructor
@NoArgsConstructor
@Data
@RedisHash(value = "student") // "student:{id}" 키로 저장
public class Student {
@Id
private String id;
private String name;
@TimeToLive(unit = TimeUnit.SECONDS)
private Long timeToLive;
}
StudentRepository
@Repository
public interface StudentRepository extends CrudRepository<Student, String> {
}
Test
@SpringBootTest
public class RedisRepositoryTest {
@Autowired
private StudentRepository studentRepository;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@BeforeEach
public void setup() {
stringRedisTemplate.keys("*").forEach(key -> stringRedisTemplate.delete(key));
}
@Test
public void testRepository() {
Student student1 = new Student("1", "john", 100L); // 100초
Student student2 = new Student("2", "tyler", 200L); // 200초
studentRepository.save(student1);
studentRepository.save(student2);
assertEquals(student1, studentRepository.findById("1").get());
assertEquals(student2, studentRepository.findById("2").get());
}
}
Result
127.0.0.1:6379> keys *
1) "student:2"
2) "student"
3) "student:1"
127.0.0.1:6379> ttl student:1
(integer) 96
127.0.0.1:6379> ttl student:2
(integer) 193
@Indexed
설명
- @RedisHash 사용시 필드에 인덱스를 걸어주는 어노테이션을 말한다.
- Repository에서 findByXXX로 조회하기 위해서 XXX 필드는 @Indexed 어노테이션으로 인덱싱 처리가 되어있어야 한다.
Student
@AllArgsConstructor
@NoArgsConstructor
@Data
@RedisHash(value = "student") // "student:{id}" 키로 저장
public class Student {
@Id
private String id;
@Indexed
private String name;
@Indexed
private int age;
}
StudentRepository
@Repository
public interface StudentRepository extends CrudRepository<Student, String> {
Student findByNameAndAge(String name, int age);
}
Test
@SpringBootTest
public class RedisRepositoryTest {
@Autowired
private StudentRepository studentRepository;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@BeforeEach
public void setup() {
stringRedisTemplate.keys("*").forEach(key -> stringRedisTemplate.delete(key));
}
@Test
public void testRepository() {
Student student1 = new Student("1", "john", 25);
Student student2 = new Student("2", "tyler", 30);
studentRepository.save(student1);
studentRepository.save(student2);
assertEquals(student1, studentRepository.findByNameAndAge("john", 25));
assertEquals(student2, studentRepository.findByNameAndAge("tyler", 30));
}
}
Result
127.0.0.1:6379> keys *
1) "student"
2) "student:1"
3) "student:age:30"
4) "student:age:25"
5) "student:2"
6) "student:name:john"
7) "student:name:tyler"
8) "student:2:idx"
9) "student:1:idx"
127.0.0.1:6379> smembers student:1:idx
1) "student:age:25"
2) "student:name:john"
127.0.0.1:6379> smembers student:name:john
1) "1"
127.0.0.1:6379> smembers student:age:25
1) "1"
127.0.0.1:6379> hgetall student:1
1) "_class"
2) "com.example.demo.Student"
3) "age"
4) "25"
5) "id"
6) "1"
7) "name"
8) "john"
기타
Operation 주입
- 스프링 컨테이너는 RedisTemplate 빈 객체를 참조하여 Operations를 주입해주는 기능을 제공한다.
- 참고
@SpringBootTest
public class OperationTest {
@Resource(name = "stringRedisTemplate")
private ValueOperations<String, String> valueOperations;
@Resource(name = "stringRedisTemplate")
private HashOperations<String, String, String> hashOperations;
@Test
public void testValueOperations() {
valueOperations.set("test:value", "Hello World");
assertEquals("Hello World", valueOperations.get("test:value"));
}
@Test
public void testHashOperations() {
hashOperations.put("test:hash", "key1", "value1");
assertEquals("value1", hashOperations.get("test:hash", "key1"));
}
}
참고
반응형
'Development > Redis' 카테고리의 다른 글
[Redis] Sentinel (0) | 2020.12.30 |
---|---|
[Redis] Master-Slave Replication (0) | 2020.12.30 |
[Redis] 명령어 (0) | 2019.03.11 |
[Redis] Cluster (0) | 2019.03.09 |
[Redis] 설치 (0) | 2019.03.09 |