반응형

기본

설명

  • 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 주입

@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

+ Recent posts