Redis란? Spring boot 를 활용한 redis 사용

Kddongkyu·2023년 9월 15일
0

NoSQL이란 ?

기존 RDBMS 방식을 탈피한 데이터베이스를 의미한다. 기존의 관계형 DB가 가지고 있는 특성뿐만 아니라 다른 특성들을 부가적으로 지원한다. RDBMS가 가지고 있는 한계를 극복하기 위한 데이터 저장소의 새로운 형태로, 수평적 확장성을 가지고 있다. 문서, 그래프, 키 값, 인 메모리, 검색을 포함해 다양한 데이터 모델을 사용한다.

NoSQL 특징

  • DBMS와 달리 데이터간의 관계를 정의하지 않는다.
  • RDBMS에 비해 훨씬 더 대용량의 데이터를 저장할 수 있다.
  • 반정형화, 비정형화된 데이터에 적합하다.
  • 분산형 구조이고 확장성이 뛰어나다.
  • 고정되지 않은 테이블 스키마를 가진다.

Redis란?

Remote Dictionary Server의 약자로, 원격 Dictinary 자료구조 서버.
레디스는 세계에서 가장 인기있는 Key-Value Store 중 하나입니다.
Key로 올 수 있는 자료형은 기본적으로 String이지만, Value는 다양한 타입을 지원합니다.

자바 해쉬맵과의 비교

둘 다 key-value 기반이고, 메모리 베이스며, 원하는 value를 제가 원하는 표현방식으로 넣을 수 있습니다.

서버가 1대 있다는 가정에선 Redis의 장점이 크게 보이지 않지만, 분산 환경을 대입하면 장점이 보입니다.

  • 분산 환경에서의 장점
    유저 요청이 크게 늘어나 서버를 몇 대 증설하였지만, 동일한 해쉬맵 데이터를 참조해야할 상황이 있다고 가정합니다.
    이 때 원격 프로세스간에 동일한 해쉬맵 데이터를 참조해야 할 때, 분산환경에선 원격 프로세스간 데이터를 동기화 하기 어렵습니다. 이 때 별도의 레디스 서버를 구성하고, 해당 레디스에서 값을 꺼내 쓴다면 메모리 기반 데이터 구조의 빠른 응답성을 확보함과 동시에 데이터 불일치 문제를 해결할 수 있습니다.

  • DBMS로서의 장점
    또한 어플리케이션을 종료하면 휘발되어 사라져버리는 HashMap과 달리, Redis는 다양한 영속성(디스크에 백업) 옵션을 제공합니다.


Spring boot 에서 Redis 사용하기

레디스 공식 홈페이지 getting-started

Java 의 Redis Client

Java 의 Redis Client 는 크게 두 가지가 있다.

  • Jedis
  • Lettuce

Jedis 를 많이 사용했으나 여러 가지 단점 (멀티 쓰레드 불안정, Pool 한계 등등..) 과 Lettuce 의 장점 (Netty 기반이라 비동기 지원 가능) 때문에 Lettuce 의 사용이 많아지는 중입니다.

-> Spring Boot 2.0 부터 Jedis 가 기본 클라이언트에서 deprecated 되고 Lettuce 가 탑재되었습니다.

Spring Boot 에서 Redis 설정

Spring Boot 에서 Redis 를 사용하는 방법 두가지.

  • RedisRepository
  • RedisTemplate

공통 세팅

implementation 'org.springframework.boot:spring-boot-starter-data-redis'
  • build.gradle 에 spring-boot-starter-data-redis 추가하고 빌드.

spring:
  redis:
    host: localhost
    port: 6379
  • application.yaml 에 host 와 port 를 설정합니다.
  • localhost:6379 는 기본값이기 때문에 만약 Redis 를 localhost:6379 로 띄웠다면 따로 설정하지 않아도 연결이 됩니다.
  • 하지만 일반적으로 운영 서버에서는 별도의 Host 를 사용하기 때문에 값을 이렇게 별도의 값을 세팅하고 Configuration 에서 Bean 에 등록해줍니다.

@Configuration
public class RedisConfig {

    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private int port;

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory(host, port);
    }
}
  • Redis 사용을 위한 기본 Configuration.
  • application.yaml 에 설정한 값을 @Value 어노테이션으로 주입.

RedisRepository

Spring Data RedisRedis Repository 를 이용하면 간단하게 Domain EntityRedis Hash 로 만들 수 있습니다.
하지만, 트랜잭션을 지원하지 않기 때문에 만약 트랜잭션을 적용해야 한다면, RedisTemplate 을 사용해야 합니다.

Entity

@Getter
@RedisHash(value = "people", timeToLive = 30)
public class Person {

    @Id
    private String id;
    private String name;
    private Integer age;
    private LocalDateTime createdAt;

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
        this.createdAt = LocalDateTime.now();
    }
}
  • Redis 에 저장할 자료구조인 객체를 정의합니다.
  • 일반적인 객체 선언 후 @RedisHash 를 붙이면 됩니다.
    - value : Redis 의 keyspace 값으로 사용됩니다.
    - timeToLive : 만료시간을 seconds 단위로 설정할 수 있습니다. 기본값은 만료시간이 없는 -1L 입니다.
  • @Id 어노테이션이 붙은 필드가 Redis Key 값이 되며 null 로 세팅하면 랜덤값이 설정됩니다.
    - keyspace 와 합쳐져서 레디스에 저장된 최종 키 값은 keyspace:id 가 됩니다.

Repository

public interface PersonRedisRepository extends CrudRepository<Person, String> {
}
  • CrudRepository 를 상속받는 Repository 클래스 추가합니다.

Example

@SpringBootTest
public class RedisRepositoryTest {

    @Autowired
    private PersonRedisRepository repo;

    @Test
    void test() {
        Person person = new Person("Park", 20);

        // 저장
        repo.save(person);

        // `keyspace:id` 값을 가져옴
        repo.findById(person.getId());

        // Person Entity 의 @RedisHash 에 정의되어 있는 keyspace (people) 에 속한 키의 갯수를 구함
        repo.count();

        // 삭제
        repo.delete(person);
    }
}
  • JPA 와 동일하게 사용할 수 있습니다.
  • 여기서는 id 값을 따로 설정하지 않아서 랜덤한 키값이 들어갑니다.
  • 저장할때 save() 를 사용하고 값을 조회할 때 findById() 를 사용합니다.

RedisTemplate

  • RedisTemplate 을 사용하면 특정 Entity 뿐만 아니라 여러가지 원하는 타입을 넣을 수 있습니다.
  • template 을 선언한 후 원하는 타입에 맞는 Operations 을 꺼내서 사용합니다.

config 설정 추가

@Configuration
public class RedisConfig {

    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private int port;

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory(host, port);
    }

    @Bean
    public RedisTemplate<?, ?> redisTemplate() {
        RedisTemplate<?, ?> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory());
        return redisTemplate;
    }
}
  • RedisTemplate 에 LettuceConnectionFactory 을 적용해주기 위해 설정해줍니다.

Example

@SpringBootTest
public class RedisTemplateTest {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Test
    void testStrings() {
        // given
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
        String key = "stringKey";

        // when
        valueOperations.set(key, "hello");

        // then
        String value = valueOperations.get(key);
        assertThat(value).isEqualTo("hello");
    }


    @Test
    void testSet() {
        // given
        SetOperations<String, String> setOperations = redisTemplate.opsForSet();
        String key = "setKey";

        // when
        setOperations.add(key, "h", "e", "l", "l", "o");

        // then
        Set<String> members = setOperations.members(key);
        Long size = setOperations.size(key);

        assertThat(members).containsOnly("h", "e", "l", "o");
        assertThat(size).isEqualTo(4);
    }

    @Test
    void testHash() {
        // given
        HashOperations<String, Object, Object> hashOperations = redisTemplate.opsForHash();
        String key = "hashKey";

        // when
        hashOperations.put(key, "hello", "world");

        // then
        Object value = hashOperations.get(key, "hello");
        assertThat(value).isEqualTo("world");

        Map<Object, Object> entries = hashOperations.entries(key);
        assertThat(entries.keySet()).containsExactly("hello");
        assertThat(entries.values()).containsExactly("world");

        Long size = hashOperations.size(key);
        assertThat(size).isEqualTo(entries.size());
    }
}
  • 위에서부터 차례대로 Strings, Set, Hash 자료구조에 대한 Operations 입니다.
  • redisTemplate 을 주입받은 후에 원하는 Key, Value 타입에 맞게 Operations 을 선언해서 사용할 수 있습니다.
  • 가장 흔하게 사용되는 RedisTemplate<String, String> 을 지원하는 StringRedisTemplate 타입도 따로 있습니다.
profile
Step by step

0개의 댓글

관련 채용 정보