예전에 레디스에 대해서 재미있게 다룬 내용을 보았다. 그 당시에는 레디스에 대한 그렇게까지 자세힌 이해도는 없었던 것 같다. 하지만 워낙 글쓴이의 필력이 대단해서 어느새 마지막 문단을 읽고 있었던 것 같다.
- [June] 레디스의 50가지 그림자 : https://papercut.blog/235
레디스(Redis)의 50가지 그림자
터미널에 비친 로그에 좌절해서 얼굴을 찌푸렸다. 이 몹쓸 캐시는 얌전히 데이터를 가지고 있을 생각을 하지 않았다. 원래 캐시는 데이터를 유지할 의무가 없었지만, 몹쓸 개발자였던 나는 일관
papercut.blog
예전에는 그냥 레디스라고 하면 그냥 캐시 할 때 쓰는 거 아닌가? 정도였다. 그 외에 레디스에 대한 것이라면 특징 정도가 있겠다. 예를 들면 인메모리 데이터 그리드라는 것, 혹은 키-값 모델을 사용한다는 점 그리고 하나 더 하자면 마스터-슬레이브 구조로 클러스터링을 할 수 있다는 점이 전부였다.
하지만 직접 레디스를 여러 번 써보면서 한계점에 봉착하곤 했다. maxmemory를 설정하지 않는다면 스왑 메모리까지 침범해 버려 속도가 엄청난 속도로 저하된다거나 혹은 상수의 시간복잡도를 제공하는 redis에 몹쓸 짓을 해버려 성능이 제대로 나오지 않는 등의 문제였다.
그러자 문득 본질적으로 redis란 무엇일까? 라는 생각이 들었다. 나는 왜 어느 순간부터 속도가 너무 느리다면 어떻게 해야 되나요.라는 질문에 redis를 쓰세요라는 말만 하게 되었을까. 그리고 정작 redis에 대해 깊은 이해를 시도해보려고 했던 적이 있었는가.
이번 포스팅에서는 redis에 대해서 간단하게 실행하고 다루는 방법에 대해서 알아보려고 한다. 그 후에 다양한 내용을 포스팅하며 redis에 대해서 깊게 살펴보고자 한다.
본론
먼저 Redis(레디스)는 정확히 말하면 인메모리 데이터 그리드라고 부른다. 메모리를 사용하며, 그리드의 형태이기 때문에 분산 환경에서도 손쉽게 확장할 수 있다는 것이다. In-Memory(인메모리)라는 것은 말 그대로 메모리에 데이터를 저장하고 가져오는 역할을 한다고 볼 수 있다.
그렇다면 데이터베이스와는 어떤 차이가 있을까? 보통의 데이터베이스는 메모리를 사용하기는 하지만 대부분의 데이터를 디스크에 저장한다. 우리가 아는 SSD나 HDD를 말한다. 그렇기 때문에 서버에서 데이터를 끌어와야 하는 경우 데이터베이스보다 인메모리 데이터 그리드인 레디스의 쪽이 더 빠르다.
어느 정도 알고있는 내용이지만 정확히 얼마나 빠르지?에 대해서 궁금하기 때문에 조사와 함께 figure를 그려왔다. 컴퓨터는 CPU라고 부르는 중앙 처리 장치에 의해서 작동된다. 위로 가면 갈수록 CPU에 가까워지기 때문에 빠르게 데이터를 왔다 갔다 움직일 수 있게 된다. 위로 올라갈수록 속도와 가격은 올라가며, 밑으로 내려갈수록 크기는 커진다.
왼쪽에 쓰인 설명에 따르면, 크기에 따르면 위로 갈수록 넘볼 수 없을 수준으로 작아진다. 바이트의 단위 자체가 달라지지만 그에 대한 반대효과로 속도도 넘볼 수 없을 정도로 빨라진다.
위와 같은 그림을 생각해봤을 때, disk에서 데이터를 끌어오는 것과 memory에서 데이터를 끌어오는 것의 속도 차이를 상상할 수 있겠는가? 정말 말도 안 되게 빨라질 것이다. 데이터베이스는 속도적인 측면에서 아쉽지만, 기가바이트 정도의 데이터를 다루는 레디스와는 다르게 말도 안 되는 양의 데이터를 저장할 수 있다. 레디스는 정반대의 특징을 가진다. 이러한 점을 고려하여 적절한 스토리지를 가져가는 것이 중요하겠다.
레디스의 특징
앞서 말했던 것에서 레디스의 특징은 모두 파생된다. 먼저 인메모리라는 특징이 있다. 상술한 내용대로 데이터베이스에 비해 엄청난 속도를 보인다.
다음으로는 key-value 스토어라는 점이다. 레디스는 키-값 형태로 데이터를 저장하는데, 이를 이용해서 List, Set, Sorted Set, Hash 등 다양한 자료구조로 사용할 수 있다. 이 외에도 기본적인 자료구조도 다 가능하다. List의 자료구조를 이용해서 queue로 만들어서 썼던 기억이 있다.
마지막으로 레디스는 데이터베이스가 아니다. 넓게 보면 데이터를 저장하니까 데이터베이스는 맞다.라고 본다면 맞지만 우리가 아는 RDB와 NoSQL과는 확실히 다르다. 레디스는 SQL을 사용하지 않기 때문에 이 NoSQL 계열로 분류가 되기는 하지만 인메모리 데이터 그리드이다. 이렇게 그리드(grid)로써 정의되기 때문에 레디스는 마스터-슬레이브 복제를 통해서 클러스터를 손쉽게 구축할 수 있다는 특징이 있다.
레디스의 실행
다음은 레디스를 직접 사용해보는 과정에 대해서 다뤄보자. 환경의 편의를 위해 redis는 docker로 띄울 예정이다. docker가 없다면 다음의 레퍼런스를 통해서 직접 설치하자.
- [redis.io] installation guide : https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/
Install Redis Stack
Install Redis Stack on Linux, macOS, and Windows
redis.io
설정해야 할 것이 꽤나 많고, 라이선스가 바뀌면서 더욱 복잡해졌기 때문에 docker를 통해서 레디스를 띄우는 걸로 진행해 보자.
docker run -d redis:7.4.1
이제, 해당 레디스가 떠있는 컨테이너를 찾아야 한다. 다음의 명령어를 통해서 CONTAINER ID를 찾아야 한다.
docker ps
그 후에는 확인된 컨테이너의 id를 통해서 직접 컨테이너에 접속해 보자.
docker exec -it 0e458f595a42 /bin/bash
위 명령어를 입력하면 redis가 실행되고 있는 컨테이너에 접근할 수 있다! 해당 컨테이너에 접근한 후 redis-cli 명령어를 통해서 레디스의 클라이언트에 접근할 수 있다.
redis-cli
자, 이제 일단 위 과정을 통해서 두 개의 redis-cli를 띄어놓자. 그 후 하나의 redis-cli에서 monitor 명령어를 입력한다.
monitor
그리고 터미널을 분할하면 위와 같이 모니터링할 수 있는 쉘(오른쪽) 그리고 레디스를 테스트할 수 있는 쉘(왼쪽)이 있다. 이 정도의 세팅이 끝났다면 간단하게 레디스 명령어를 통해서 사용하는 방법을 알아보자.
레디스 사용법
위 내용대로의 세팅이 되었다면, 레디스에 데이터가 어떻게 들어가는지 손쉽게 모니터링할 수 있다. 모니터링은 기본 중의 기본! 간단하게 카운트의 값을 10으로 세팅하고 카운팅 해보자.
위와 같이 SET key value 명령어를 통해 값을 세팅하고, INCR key, DECR key를 통해서 값을 증가시키고 감소시킬 수 있다.
위 명령어는 가장 기본적인 테스트였고, DEL(삭제), EXPIRE/TTL(만료 기간), SCAN(탐색), HGET/HSET(해시 이용), LPUSH/RPUSH/LPOP/RPOP(List로 사용) 등이 있다. 명령어에 대해서는 키워드만 써놓았다. 자세한 활용법이 필요하면 언제든 ChatGPT를 이용하자.
명령어에 대해서 가장 중요한 것은 각종 레디스 명령어의 시간 복잡도이다. 레디스 공식 문서에 따르면 각각 명령어에 대한 시간복잡도를 제공하고 있다. 레디스는 키-값 베이스의 스토리지이기 때문에, 대부분의 작업이 O(1)의 시간복잡도를 제공한다. O(n)의 시간복잡도를 가지는 명령어를 사용할 때에는 주의가 필요하다. 다음의 레퍼런스에서 자세한 정보를 확인할 수 있다.
- [redis.io] commands : https://redis.io/docs/latest/commands/
Commands
redis.io
데이터 구조 크기에 의존하는 연산
Redis에서는 문자열뿐만 아니라 List, Set, Hash 등의 자료구조를 지원한다. 이러한 자료구조를 LLEN이나 LRANGE, SMEMBERS(특정 원소가 자료구조 안에 있는지 확인) 등의 명령어를 쓰는 경우에는 시간복잡도가 증가할 수 있기 때문에 유의해서 사용해야 한다.
정렬 및 탐색
SORT 명령은 키에 대해서 정렬을 수행하는데, 정렬이기 때문에 O(N)의 시간복잡도가 나올 수 있다. 이러한 명령어는 유의해서 사용해야 한다.
또한 탐색에 대해서는 keys *를 사용해 볼 수도 있고, scan 명령어를 사용해 볼 수도 있다. 이에 대해서는 각각의 장단점이 존재하기 때문에 어떤 명령어를 사용하더라도 자세히 살펴보면서 신중하게 시간복잡도를 측정하는 것이 중요하다.
레디스와 서버의 연결(스프링부트)
이러한 레디스를 앞서 보았던 것처럼 직접 붙어서 쓰지는 않는다. 직접 붙는 경우는 모니터링이나 데이터를 직접 확인하는 정도이고, 나머지는 전부 서버에서 레디스 인스턴스를 띄워서 서버 쪽에서 레디스에 데이터를 나르는 작업을 한다. 대부분 프로그래밍 언어 진영의 프레임워크에서는 이러한 레디스 라이브러리가 다 지원되어 있다.
스프링부트를 예를 들어 소개하면 아래와 같이 설정할 수 있다.
# build.gradle에 의존성 추가
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
}
# 설정 파일 ( application.properties 또는 application.yml 에 redis 서버 정보 지정)
spring:
redis:
host: localhost
port: 6379
참고로 레디스는 6379 포트를 사용한다.
마지막으로 스프링부트를 사용한다면, @Bean을 통해서 이러한 redis를 싱글톤 인스턴스로 만드는 설정을 해줄 수 있다. 대부분의 프레임워크에서 하나의 레디스 인스턴스를 만들어서 싱글톤 패턴을 통해 redis를 사용한다.
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.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory();
}
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new StringRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
위 RedisConfig를 통해서 싱글톤 패턴으로 만들어진 redis 인스턴스를 아래와 같이 간단하게 사용할 수 있다.
@Service
public class RedisExampleService {
private final StringRedisTemplate redisTemplate;
public RedisExampleService(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
public void saveValue(String key, String value) {
redisTemplate.opsForValue().set(key, value);
}
public String getValue(String key) {
return redisTemplate.opsForValue().get(key);
}
}
여기까지 했다면 간단하게 redis를 어떻게 사용하는지 정도는 익힐 수 있을 것이다.
마치며
최근에 geeknews에서 레디스의 라이선스 문제가 발생했다는 것들을 꽤나 자주 접했는데, 최근에 레디스의 공식 사이트를 들어가니 로고부터 많이 바뀌었다. 예전 레디스의 로고가 더욱 맘에 들었다.
그리고 이러한 레디스의 라이센스 정책에 대한 대처로 다른 키-값 스토어가 생겨나고 있다. Valkey라고 하는 것이다. ( 아마 value key를 합친 듯? 밸키? ) 나중에 기회가 된다면 이 툴에 대해서 다뤄보고 싶다.
참고
- [redis.io] commands : https://redis.io/docs/latest/commands/
- [June] 레디스의 50가지 그림자 : https://papercut.blog/235
- [dockerhub] redis : https://hub.docker.com/_/redis
감사합니다.
'Database' 카테고리의 다른 글
Redis에 데이터를 무한정 넣을 수 있다고? ( maxmemory ) (1) | 2024.12.16 |
---|