티스토리 뷰
지난 글에 적었듯이 레디스는 다양한 자료형을 지원한다.
해서 조금씩 공부하면서 현재 진행 중인 프로젝트의 캐싱이나 채팅방, 인증번호 저장 등을
모조리 레디스를 사용하도록 교체할 꿈을 꾸고 있는데, 바로 적용하려니 기본 연산이 잘 들어오지 않아 힘들었다.
따라서 이번 글은 레디스에서 지원하는 자료형의 연산과 기본 명령어 등을 연습하려고 한다.
먼저 스프링부트 프로젝트를 하나 생성해서 내장 레디스를 사용하기 위한 의존성을 추가한다.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'it.ozimov:embedded-redis:0.7.2'
}
나머지 의존성은 필요에 따라 추가하면 되겠다.
계속해서 레디스 설정파일을 구성한다.
package com.example.redis;
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.repository.configuration.EnableRedisRepositories;
@Configuration
@EnableRedisRepositories
public class RedisConfig {
@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(RedisConnectionFactory connectionFactory) {
RedisTemplate<byte[], byte[]> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
return redisTemplate;
}
}
이어서 내장 레디스를 위한 설정 파일이다.
package com.example.redis;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import redis.embedded.RedisServer;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Profile("local")
@Configuration
public class EmbeddedRedisConfig {
@Value("${spring.redis.port}")
private int redisPort;
private RedisServer redisServer;
@PostConstruct
public void redisServer() {
redisServer = new RedisServer(redisPort);
redisServer.start();
}
@PreDestroy
public void stopRedis() {
if (redisServer != null) {
redisServer.stop();
}
}
}
"local"인 경우에만 내장 레디스를 사용하기로 했으니까 yml파일도 수정해 준다.
spring:
profiles:
active: local
redis:
lettuce:
pool:
max-active: 10
max-idle: 10
min-idle: 2
port: 6379
host: localhost
오늘 사용할 클래스는 StringRedisTemplate이다.
RedisTemplate을 상속받은 클래스이자 자료형의 대부분인 문자열을 효과적으로 직렬화하는 클래스라고 한다.
실제 사용할 땐 RedisTemplate을 직접 사용하기보단 StringRedisTemplate을 사용하는 것 같은데 그건 좀 더 보고..
하여간 코드를 뜯어보면 단순하게 생겼다.
package org.springframework.data.redis.core;
import org.springframework.data.redis.connection.DefaultStringRedisConnection;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.StringRedisConnection;
import org.springframework.data.redis.serializer.RedisSerializer;
public class StringRedisTemplate extends RedisTemplate<String, String> {
public StringRedisTemplate() {
setKeySerializer(RedisSerializer.string());
setValueSerializer(RedisSerializer.string());
setHashKeySerializer(RedisSerializer.string());
setHashValueSerializer(RedisSerializer.string());
}
public StringRedisTemplate(RedisConnectionFactory connectionFactory) {
this();
setConnectionFactory(connectionFactory);
afterPropertiesSet();
}
protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
return new DefaultStringRedisConnection(connection);
}
}
RedisSerializer라는 클래스를 이용해 직렬화를 하는 듯 보인다.
계속해서 실습에 사용할 컨트롤러 클래스를 작성하자.
package com.example.redis;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
public class RedisController {
private final StringRedisTemplate stringRedisTemplate;
private static String key = "gnidinger";
}
실습 하나마다 API 엔드포인트를 하나씩 따서 콘솔창에서 결과를 확인할 생각이다.
위 코드에서 key값은 그때그때 새로 정해주기도 귀찮고 지금은 별 의미도 없는 것 같아 전역변수로 설정해 주었다.
참고로 각 타입을 위한 메서드와 객체는 아래와 같다.
Method | Object |
opsForValue() | ValueOperations |
opsForList() | ListOperations |
opsForSet() | SetOperations |
opsForZSet() | ZSetOperations |
opsForHash() | HashOperations |
opsForStream() | StreamOperations |
opsForGeo() | GeoOperations |
opsForHyperLogLog() | HyperLogLogOperations |
opsForCluster() | ClusterOperations |
String
@GetMapping("/test")
public void testString() {
final ValueOperations<String, String> stringStringValueOperations = stringRedisTemplate.opsForValue();
stringStringValueOperations.set(key, "1");
final String result_1 = stringStringValueOperations.get(key);
System.out.println("result_1 = " + result_1);
stringStringValueOperations.increment(key);
final String result_2 = stringStringValueOperations.get(key);
System.out.println("result_2 = " + result_2);
}
result_1 = 1
result_2 = 2
먼저 가장 활용도가 높은 문자열이다.
위에 적었듯이 템플릿을 이용해 해당 메서드를 호출하면 위와 같은 객체가 반환된다.
재미있는 것은 set을 이용해 문자열 타입으로 숫자를 입력하면 increment() 메서드로 그 값을 1 증가시킬 수 있다는 점이다.
물론 반대 기능을 하는 메서드인 decrement() 역시 존재한다.
키를 이용해 값을 꺼내올 땐 get() 메서드가 사용된다.
List
@GetMapping("/test2")
public void testList() {
final ListOperations<String, String> listOperations = stringRedisTemplate.opsForList();
listOperations.rightPush(key, "H");
listOperations.rightPush(key, "E");
listOperations.rightPush(key, "L");
listOperations.rightPush(key, "L");
listOperations.rightPush(key, "O");
listOperations.rightPushAll(key, " ", "G", "N", "I", "D", "I", "N", "G", "E", "R");
final String char_1 = listOperations.index(key, 1);
System.out.println("char_1 = " + char_1);
final Long size = listOperations.size(key);
System.out.println("size = " + size);
final List<String> result = listOperations.range(key, 0, 14);
System.out.println("result = " + Arrays.toString(result.toArray()));
}
char_1 = E
size = 15
result = [H, E, L, L, O, , G, N, I, D, I, N, G, E, R]
다음으로 많이 쓰이는 리스트이다.
리스트의 오른쪽에 값을 추가하는 rightPush()와 왼쪽에 추가하는 leftPush()가 존재한다.
또한 index(K, V)를 이용해 리스트와 리스트의 인덱스를 지정해 값을 불러올 수 있다.
Set
@GetMapping("/test3")
public void testSet() {
final SetOperations<String, String> setOperations = stringRedisTemplate.opsForSet();
setOperations.add(key, "H");
setOperations.add(key, "E");
setOperations.add(key, "L");
setOperations.add(key, "L");
setOperations.add(key, "O");
Set<String> gnidinger = setOperations.members(key);
System.out.println("members = " + Arrays.toString(gnidinger.toArray()));
Long size = setOperations.size(key);
System.out.println("size = " + size);
gnidinger.forEach(System.out::println);
}
members = [L, H, O, E]
size = 4
L
H
O
E
다음으로 Set이다.
일반 컬렉션 프레임워크를 사용할 때와 큰 차이는 없다.
SortedSet
@GetMapping("/test4")
public void testZSet() {
ZSetOperations<String, String> zSetOperations = stringRedisTemplate.opsForZSet();
zSetOperations.add(key, "H", 2);
zSetOperations.add(key, "E", 5);
zSetOperations.add(key, "L", 10);
zSetOperations.add(key, "L", 20);
zSetOperations.add(key, "O", 30);
Set<String> range = zSetOperations.range(key, 0, 5);
System.out.println("range = " + Arrays.toString(range.toArray()));
Long size = zSetOperations.size(key);
System.out.println("size = " + size);
Set<String> scoreRange = zSetOperations.rangeByScore(key, 0, 13);
System.out.println("scoreRange = " + Arrays.toString(scoreRange.toArray()));
}
range = [H, E, L, O]
size = 4
scoreRange = [H, E]
Sorted Set, 혹은 ZSet은 내부 자료를 정렬해서 보관하고 있는 Set이라고 할 수 있다.
즉, 모든 값은 유일하며 정해진 기준에 의해 이진 탐색트리를 이용해 정렬까지 되어 있는 구조라 할 수 있다.
위 코드에서 값을 저장할 때 뒤에 숫자로 Score를 부여하며, 이 값의 크기에 따라 정렬이 결정된다.
혹은 키값을 이용해 저장된 값을 정렬해 출력하는 것도 가능하다.
Hash
@GetMapping("/test5")
public void testHash() {
HashOperations<String, Object, Object> hashOperations = stringRedisTemplate.opsForHash();
hashOperations.put(key, "Hello", "gnidinger");
hashOperations.put(key, "Hello2", "gnidinger2");
hashOperations.put(key, "Hello3", "gnidinger3");
Object hello = hashOperations.get(key, "Hello");
System.out.println("hello = " + hello);
Map<Object, Object> entries = hashOperations.entries(key);
System.out.println("entries = " + entries.get("Hello2"));
Long size = hashOperations.size(key);
System.out.println("size = " + size);
}
hello = gnidinger
entries = gnidinger2
size = 3
Hash는 말 그대로 키가 가리키는 값 안에 새로운 키-값 자료를 형성할 수 있는 구조이다.
무슨 소린지 잘 모르겠지만 아마도 채팅방 등과 같은 기능을 구현할 때 유용하게 쓰이는 듯하다.
키 값이 가리키는 채팅방 안에 또 채팅이라는 값들이 존재할 테니.
기본 연산은 간단한 편이다.
이런 식으로 레디스가 제공하는 기본 자료형과 그 연산에 대해 연습해 보았다.
조금은 친해졌을.. 지도?
다음번엔 (아마도) 레디스 캐싱을 프로젝트에 적용하는 법에 대해서 적어볼 생각이다.
채팅방에 당장 적용하고 싶지만 일단 쉬운 것부터.
'Development > Database' 카테고리의 다른 글
[Data]자바에도 있네, 데이터 분석 툴. Tablesaw (0) | 2023.02.17 |
---|---|
[ES]Elastic Search, Lucene, 그리고 (2) | 2023.02.15 |
[Redis]레디스(Redis), 스프링부트에 캐싱 적용 (0) | 2023.01.19 |
[Redis]캐시(Cache), 그리고 레디스(Redis) (2) | 2023.01.14 |
[데이터베이스]관계형 데이터베이스의 ERD (2) | 2022.08.07 |
[데이터베이스]SQL vs. NoSQL (0) | 2022.08.05 |
- Total
- Today
- Yesterday
- 동적계획법
- 스트림
- 리스트
- BOJ
- 알고리즘
- 면접 준비
- 유럽여행
- 여행
- Backjoon
- 자바
- spring
- Python
- 백준
- 지지
- 남미
- java
- 스프링
- 파이썬
- 야경
- 유럽
- 칼이사
- a6000
- 세계여행
- Algorithm
- 맛집
- 세계일주
- 세모
- RX100M5
- 중남미
- 기술면접
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |