Redis를 사용해보자! (1) (feat. 비관계형 데이터 베이스란?)

서은경·2023년 3월 24일
0

DB

목록 보기
2/2

토이 프로젝트에서 레디스를 써봤는데 공부하면서 캐싱 전략 외에도 많은 상황에서 사용된다는 걸 알았다. 잊지 않기 위해 포스팅~!

비관계형 데이터 베이스란 ?

NoSQL이라고도 부르며 키-값 저장소, 그래프 저장소, 컬럼 저장소, 문서 저장소로 나뉜다.
전통적인 관계형 데이터베이스 보다 덜 제한적인 일관성 모델을 이용하는 데이터의 저장 및 검색을 위한 매커니즘을 제공한다.

비관계형 데이터 베이스 사용이 바람직한 경우

  • 아주 낮은 응답 지연시간(legacy)이 요구됨
  • 다루는 데이터가 비정형이라 관계형이 아님
  • 데이터를 직렬화하거나 역직렬화할 수 있기만 하면 됨
  • 아주 많은 양의 데이터를 저장할 필요가 있음

NoSQL - Redis

Redis는 Remote Dictionary Server의 약자로 In-Memory 기반의 Key-Value 기반의 NoSql DBMS이다.
다양한 자료구조(String, Hash, Set, List, Sort등)를 저장할 수 있는 기능이 있으며 Cache 방식을 사용함으로써 DB Read의 부하를 줄여주어 많은 요청이 몰렸을 때 성능이 향상될 수 있다

In-Memory 란? 
데이터들이 디스크에 저장되는 다른 DBMS와 달리 Memory(Ram)에 저장된다.
일반적으로 디스크를 읽는 속도보다 메모리를 읽는 속도가 빠르기 때문에 데이터를 읽거나 쓰는 과정에서 속도가 훨씬 빠르다.

Cache 란?
데이터를 미리 저장해놓고 요청에 따라 필요한 데이터를 바로 보여준다.

적용해보자!

  • 로컬에 redis 설치 (homebrew 사용)
  • gradle 에 의존성 추가

dependencies {
	... 생략
    api 'org.springframework.boot:spring-boot-starter-data-redis'
}

멀티 모듈을 사용하여 구성했기 때문에 의존주입을 받는 모듈에서도 사용할 수 있도록 api 메서드를 사용하여 추가해줬다.

  • 설정파일에 host와 port 설정
spring:
  redis:
    lettuce:
      pool:
        max-active: 10
        max-idle: 10
        min-idle: 2
    port: 6379
    host: 127.0.0.1
    password: 'tjdrud123!'
  • RedisConfig 파일 생성
package com.sek.ottfind.api;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

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

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

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

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory(redisHost, redisPort);
    }

	/*
    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        redisStandaloneConfiguration.setHostName(redisHost);
        redisStandaloneConfiguration.setPort(Integer.parseInt(redisPort));
        redisStandaloneConfiguration.setPassword(redisPassword);
        LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(redisStandaloneConfiguration);
        return lettuceConnectionFactory;
    }
	*/
    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory());
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        return redisTemplate;
    }
}

setKeySerializer, setValueSerializer 설정해주는 이유는 RedisTemplate를 사용할 때 Spring - Redis 간 데이터 직렬화, 역직렬화 시 사용하는 방식이 Jdk 직렬화 방식이기 때문이다. 동작에는 문제가 없지만 redis-cli을 통해 직접 데이터를 보려고 할 때 알아볼 수 없는 형태로 출력되기 때문에 적용한다.

String 자료구조를 사용하였기 때문에 opsForValue를 사용했다.
RedisTemplate 클래스를 보면 이렇게 여러 자료구조를 사용하는 걸 알 수 있다. list는 list를 쉽게 직렬화/역직렬화 해주는 인터페이스, set은 set, hash는 hash.. 등등 적절하게 사용한다.
(밑에 소스는 리턴값을 긁어온것! 메서드는 opsForSet() 형식)


	private final ValueOperations<K, V> valueOps = new DefaultValueOperations<>(this);
	private final ListOperations<K, V> listOps = new DefaultListOperations<>(this);
	private final SetOperations<K, V> setOps = new DefaultSetOperations<>(this);
	private final StreamOperations<K, ?, ?> streamOps = new DefaultStreamOperations<>(this,
			ObjectHashMapper.getSharedInstance());
	private final ZSetOperations<K, V> zSetOps = new DefaultZSetOperations<>(this);
	private final GeoOperations<K, V> geoOps = new DefaultGeoOperations<>(this);
	private final HyperLogLogOperations<K, V> hllOps = new DefaultHyperLogLogOperations<>(this);
	private final ClusterOperations<K, V> clusterOps = new DefaultClusterOperations<>(this);

참고로 호스트와 포트만 설정해줄거라면 redisConnectionFactory는 뭘 써도 상관없다.
LettuceConnectionFactory 소스를 까봤을 때 동일하게 작동하기 때문! (그냥 생성자 차이..)

public LettuceConnectionFactory(String host, int port) {
		this(new RedisStandaloneConfiguration(host, port), new MutableLettuceClientConfiguration());
	}
  • 테스트를 위한 RedisController 생성
package com.myproject.demo.application.controller;

import com.myproject.demo.domain.dto.code.ResultCodeEnum;
import com.myproject.demo.domain.dto.response.CommonResponseDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RedisController {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @GetMapping("/redisTest")
    public CommonResponseDto addRedisKey() {
        ValueOperations<String, String> op = redisTemplate.opsForValue();
        op.set("1", "test1");
        op.set("2", "test2");
        op.set("3", "test3");
        return new CommonResponseDto(ResultCodeEnum.SUCCESS);
    }

    @GetMapping("/redisTest/{key}")
    public CommonResponseDto getRedisKey(@PathVariable String key) {
        ValueOperations<String, String> op = redisTemplate.opsForValue();
        String value = op.get(key);
        return new CommonResponseDto(ResultCodeEnum.SUCCESS, value);
    }

}

redisTemplate를 통해 키-값 을 세팅하면 키를 호출했을 때 값을 정상적으로 출력하는 것을 볼 수 있다.

다음편엔 ZSET을 이용해 인기메뉴 상위 3개를 가져오는 로직에 적용한 것을 포스팅하겠다!

0개의 댓글