• 왜 Redis를 정리하게 됐는지
  • 다른 DB와의 차이점
  • Redis data types
  • Redis 관련 용어설명
  • 기업의 Redis 적용사례들
  • Redis 설치하고 SpringBoot와 연동하기

 

왜 Redis를 정리하게 됐는지

 

Redis는 처음 SpringSession 연동과 관련해서 지마켓 기술블로그를 통해 더 관심을 갖게 되었다. 그래서 직접 설치 및 다음 토이프로젝트에 적용하는 것을 목표로 독학하기 위해 블로그에 글을 정리해 보았다.

 

Redis는 db-engines에서 볼 수 있듯이, (2023년 1월 기준) 6위 인기도를 보여주고 있다.

역시 탑 랭크는 관계형 db가 차지하고 있는데 사진에서도 알 수 있듯이 Redis는 key-value 값의 인메모리 db에 속한다. In-Memory 구조의 데이터베이스는 NoSQL로 다양한 자료구조를 지원하기 때문에 우리가 JAVA 등의 언어에서 LIST, HASHMAP 등의 구조를 사용하는 것처럼 데이터베이스를 구조화된 형태로 저장, 사용할 수 있다는 점이 유리하다.

단일 서버에서는 큰 이점이 없을 수 있지만, 여러 서버로 가동되는 프로그램의 경우, 동일한 데이터베이스 Redis를 공유하므로서 데이터 안전성을 확보한다는 것은 큰 이점이다. 자세한 사용 사례는 아래 목차를 참고하면 된다.

 


다른 DB와의 차이점

 

관계형 데이터베이스(MySQL, MSSQL)는 데이터를 디스크에 저장하지만 Redis와 같은 In-Memory 형식은 Memory(RAM)에 데이터가 저장된다. 즉, 데이터를 읽거나 저장하는 속도가 더 빠르다.

Cache 방식을 사용하기 때문에 관계형 데이터베이스처럼 디스크에서 데이터를 조회해서 가져올 필요가 없다. 또한 Expire 기능이 있어 각 데이터가 메모리에 저장되는 유효기한을 정해서 사용할 수 있다. 그리고 다양한 데이터 타입을 제공하기 때문에 개발 편의성을 높여준다.

 


Redis data types

 

위에 Redis에 대해 설명하면서 잠깐 언급됐지만, 자료구조가 다양하다는 장점은 제일 크게 느껴졌다. 아래 사진처럼 Core data type은 String, Lists, Sets, Hashes, Sorted sets, Hashes, Sorted sets, Streams, Geospatial indexes, Bitmaps, Bitfields, HyperLogLog 가 있다.

여기서 대부분은 자료구조는 자바에서 봤던 것과 유사했고, 몇 가지 자료구조에 대해서만 찾아봤다.

Grospatial index는 이름에서도 알 수 있듯이 지리적 위치에 대한 데이터를 저장하기 좋은 구조다. 위도와 경도를 저장하는 타입이고 각 위치의 지리적 거리 관계를 파악하기 유용하다.

Hyperloglogs는 집합의 원소의 개수를 추정하는 방법으로 추가된 데이터구조다. 이전 LogLogCounting 알고리즘을 발전시킨 것으로 데이터를 저장하기 않기 때문에 작은 메모리로 빠른 계산이 가능해진다. 주로 매우 큰 데이터를 오차 1% 이하의 근사치로 구할 때 사용된다. 원소를 추가하는 PFADD, 원소의 수를 계산하는 PFCOUNT, 둘 이상의 원소를 한 원소로 합치는 PFMERGE라는 명령어가 사용된다.

PFADD members 123
PFADD members 500

PFCOUNT members
2

Redis 관련 용어설명

 

Redis Cluster

분산 서버를 갖는 형태로 최소 3개의 node를 가진다. 여러 개의 Master 서버와 한 Master 서버 당 한 개 이상의 Slave를 두는 Master-Slave 형태로 구성이 가능하다.

 

Lettuce vs. Jedis

자바에서 Redis를 사용하기 쉽게 도와주는 라이브러리들이다. 블로그에서 Lettuce가 TPS/CPU/Connection 전 분야 우위에 있는 것을 비교해서 보여주고 있다. 자원 절약과 업데이트 등 여러 면에서 Lettuce 사용을 추천하고 있다. (Jedis 보다 Lettuce를 쓰자 참조)

 


기업의 Redis 적용사례들

 

유저 목록을 Redis Bitmap 구조로 저장하여 메모리 절약하기

메모리 절약을 위해 Redis Set 구조 대신에 Bitmap 구조를 이용한 내용이다. 본문에 따르면 리멤버에서는 설문에 해당하는 사람들의 ID를 Set으로 Redis에 저장하고 설문 이외에는 사용하지 않고 있었다. 메모리 소비가 Bitmap을 사용하므로 확실히 줄어드는 것을 볼 수 있었다. 하지만 데이터의 양이 작고 오프셋이 크다면 Set의 메모리 소비가 더 작을 수 있다는 점에 유의하자.

 

선물하기 시스템의 상품 재고는 어떻게 관리되어질까?

배민에서는 전체 실시간 재고와 인당 실시간 재고를 관리하도록 Key 값을 정해서 Set 자료구조를 사용하고 있었습니다. 재고사용량 차감에 대한 잘못된 구매번호의 이벤트가 발행되어도 재고사용량 차감에 영향을 미치지 않도록 비동기 방식을 채택한 것을 볼 수 있다. 하지만 Redis만으로는 휘발성 데이터로 데이터 유실이 일어날 수 있으므로, 재고 사용량 데이터를 RDB에 싱크해서 관리하고 있다는 것을 알 수 있다.

 


Redis 설치하고 SpringBoot와 연동하기

 

먼저 설치를 위해 Redis-x64-3.0.504.msi를 다운로드 받아서 설치한다. (Window 기준) 경로 설정이나 포트 설정 외에 별다른게 없어서 캡쳐는 진행하지 않았다. 모두 Next를 해서 Install 진행하면 간단하게 Redis 설치가 종료된다.

Redis를 기본 경로로 했을 경우의 설치된 위치는 C:\Program Files\Redis이다. 여기서 Redis 폴더는 아래와 같은데 블록된 파일을 위주로 처음 보면 될 것 같다. 

 

redis-server.exe를 눌러 서버를 가동한다.

redis-cil.exe가 우리가 Redis를 사용할 때 선택해서 명령어를 입력하는 cmd 창이라고 보면 될 것 같다.

redis.windows.conf는 포트, bind 설정, 로그 설정 등 설정을 할 수 있는 파일이다. 주석된 것을 풀고 저장한 뒤 재시작해서 사용하면 된다.

 

 

Redis가 제대로 설치됐는지 확인해보기 위해 몇 가지 명령어를 입력하고 값이 저장되는지 확인해봤다. 간단하게 값을 set, get해서 데이터가 잘 확인되는 것을 알 수 있다.

 

 

그럼 이제 Spring boot에 연동해서 Redis를 사용해보자. 나는 Lettuce를 사용해서 Redis를 연결했다. 연동테스트를 위한 파일과 코드는 다음과 같다.

 

build.gradle

설정부분에서 dependecies는 이번 연동 테스트를 위해서 필요 없지만 추후 프로젝트를 위해 미리 설정한 값이 있어 주석처리해놓았다.

plugins {
	id 'java'
	id 'org.springframework.boot' version '2.4.4'
	id 'io.spring.dependency-management' version '1.1.0'
}

group = 'test.guide'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	//implementation 'org.springframework.boot:spring-boot-starter-security'
	implementation 'org.springframework.boot:spring-boot-starter-data-redis'
	//implementation 'org.springframework.session:spring-session-core'
	implementation 'org.springframework.session:spring-session-data-redis'
	implementation 'io.lettuce:lettuce-core:6.2.2.RELEASE'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
	useJUnitPlatform()
}

 

application.properties

properties 설정부에서 로컬에서 default 포트를 사용할 경우 동일하게 설정하면 된다.

spring.redis.host=localhost
spring.redis.port=6379

 

SessionConfig.java

config 설정을 해서 @Value로 properties 값을 가져오고, redisTemplate 설정을 해줬다. 여기서는 StringRedisTemplate를 사용해서 간단히 key, value를 대입해보려고 한다.

package test.guide.springSession.config;

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.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;

@Configuration
@EnableRedisRepositories
public class SessionConfig extends AbstractHttpSessionApplicationInitializer {
    @Value("${spring.redis.host}")
    private String redisHost;

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

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

    @Bean
    public RedisTemplate<?,?> redisTemplate(){
        RedisTemplate<byte[], byte[]> redisTemplate=new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory());
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        return redisTemplate;
    }

    @Bean
    public StringRedisTemplate stringRedisTemplate(){
        StringRedisTemplate stringRedisTemplate=new StringRedisTemplate();
        stringRedisTemplate.setKeySerializer(new StringRedisSerializer());
        stringRedisTemplate.setValueSerializer(new StringRedisSerializer());
        stringRedisTemplate.setConnectionFactory(redisConnectionFactory());
        return stringRedisTemplate;
    }
}

 

SpringSessionApplicationTest.java

test를 위한 코드를 작성했다. 아래 사진처럼 잘 연동되는 것을 확인할 수 있다. 설치 후 확인을 위해 redis-cil.exe로 데이터를 넣은 key 값, "os"의 value인 window까지 출력되는 것을 확인할 수 있다.

package test.guide.springSession;


import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;

@SpringBootTest
class SpringSessionApplicationTests {
	@Autowired
	private StringRedisTemplate redisTemplate;
	private final String KEY="keyword";

	@Test
	public void test(){
		String keyword="Redis_Test";

		ValueOperations<String, String> rt = redisTemplate.opsForValue();
		rt.set(KEY, keyword);

		System.out.println(rt.get(KEY));
		System.out.println(rt.get("os"));

	}

}

 

댓글