일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- design pattern
- react
- 컴퓨터구조
- MSA
- 알고리즘
- redis
- 자료구조
- Kafka
- Proxy
- Heap
- c언어
- Algorithm
- JPA
- MySQL
- OS
- JavaScript
- IT
- 자바
- Data Structure
- 네트워크
- 디자인 패턴
- 운영체제
- Java
- Spring
- mongoDB
- 백준
- 파이썬
- spring webflux
- Galera Cluster
- C
Archives
- Today
- Total
시냅스
Redis 공식문서 기반 정리 ( +Spring 에서의 용례 ) 본문
Redis
- in-memory 데이터 자료구조 저장소
- 아래와 같이 쓰일 수 있다.
- Database
- Cache
- Message broker
- Streaming engine
- Redis가 지원하는 자료구조
- String
- Hash
- List
- Set
- Sorted Set
- Bitmap
- Hyperloglog
- Geospatial Indexe
- Stream
- atomic 한 연산을 지원한다.
- Redis Model
- Transactions
- Pub/Sub
- Lua scripting
- Keys with a limited time-to-live
- LRU eviction of keys
- Automatic failover
- 특징
- Key-Value 구조의 비정형 데이터를 저장하고 관리하기 위한 오픈 소스 기반의 비관계형 데이터 베이스 관리 시스템이다.
- RAM에 저장하여 디스크 스캐닝이 필요없어 매우 빠르다.
- RAM은 휘발성으로 컴퓨터 전원이 꺼지면 모든 정보는 삭제된다.
- Snapshot을 통해 백업할 수 있다.
- AOF : 명령(쿼리)들을 저장해두고, 서버가 셧다운 후 재실행 되면 다시 실행한다.
- 싱글 스레드로 한 번에 하나의 명령만 수행한다.
- 다만 get, set 명령어의 경우 초당 처리속도를 10만개 까지 지원한다.
Transaction
- 명령의 그룹들을 single step으로 실행하게 한다.
- MULTI, EXEC, DISCARD, WATCH를 중심으로 한다.
- MULTI
- 트랜잭션 블록이 시작됨을 의미한다.
- EXCEC
- MULTI 이후 명령어들을 실핸한다.
- EXEC을 명령해야 내가 입력한 명령들이 실행된다.
- DISCARD
- 입력한 명령어들을 flush 한다.
- WATCH
- 트랜잭션의 조건부 실행을 감시할 지정된 키를 표시한다.
- MULTI
- 모든 트랜잭션 안의 커맨드들은 serialized 되고, 순차적으로 실행된다.
- 트랜잭션 중에 다른 트랜잭션이 실행될 수 없다.
- atomic 연산을 지원한다.
- 필자는 주로 redis에 객체/데이터를 넣어두고 RDBMS를 사용하지 않고 캐싱하는 용도로 자주 쓴다.
> MULTI
OK
> INCR foo
QUEUED
> INCR bar
QUEUED
> EXEC
1) (integer) 1
2) (integer) 1
Transaction의 Java에서의 용례
메서드 | 설명 |
opsForValue | String 을 Redis에 저장(Serialize), 반환(Deserialize) 하게 한다. |
opsForList | List 를 Redis에 저장(Serialize), 반환(Deserialize) 하게 한다. |
opsForSet | Set 을 Redis에 저장(Serialize), 반환(Deserialize) 하게 한다. |
opsForZSet | ZSet 을 Redis에 저장(Serialize), 반환(Deserialize) 하게 한다. |
opsForHash | Hash 를 Redis에 저장(Serialize), 반환(Deserialize) 하게 한다. |
- 아래는 String에서의 use case이다.
- 사전에 redisconfiguration을 정의해야 한다.
public class UserCaseRepository{
private final RedisTemplate<String, String> redisTemplate;
public void setToken(String key, String data) {
ValueOperations<String, String> values = redisTemplate.opsForValue();
values.set(key, data);
}
public void setToken(String key, String date, Duration duration) {
ValueOperations<String, String> values = redisTemplate.opsForValue();
values.set(key, date, duration); // duration을 통해 만료기간을 정한다.
}
public Optional<String> getToken(String key) {
ValueOperations<String, String> values = redisTemplate.opsForValue();
return Optional.of(values.get(key));
}
}
Pub/Sub
- 공식문서의 설명
- SUBSCRIBE, UNSUBSCRIBE 및 PUBSCRIBE는 보낸 사람(발행자)이 특정 수신자(구독자)에게 메시지를 보내도록 프로그래밍되지 않는 게시/구독 메시지 패러다임을 구현한다.
- 오히려, 게시된 메시지는 구독자가 있을 수 있는 것에 대한 지식 없이 채널로 특성화된다.
- 구독자는 하나 이상의 채널에 대해 관심을 표현하고 Publisher가 있는지에 대한 지식 없이 관심 있는 메시지만 수신한다.
- 게시자와 구독자의 이러한 분리는 더 큰 확장성과 더 동적인 네트워크 토폴로지를 가능하게 한다.
- 필자 각주
- 클라이언트가 특정 토픽을 Subscribe 하면 Publisher는 어떤 Subscriber가 있는지 고려하지 않고 구독하고 있는 모든 Subscriber에게 메세지를 보낸다.
- 이후 클라이언트는 Subscribe 로 메세지를 받을 때에 대한 행동을 정의할 수 있다.
- 참고로 Redis 입장에서 WAS는 client이다.
- 필자는 여러 WAS를 중개해야 하는 Event가 있을 때에 사용한다.
- WebSocket이나, SSE와 같은 것이 그렇다.
Pub/Sub의 Java에서의 용례
- 참고
- RedisAlarmPublisher
- RedisAlarmSubscriber
- CommentService : 53L : 58L
- 을 참고하시면 얼추 보실 수 있다.
@GetMapping("/subscribe")
public SseEmitter subscribe(@AuthenticationPrincipal UserPrincipal userPrincipal) {
ChannelTopic topic = new ChannelTopic("Emitter:UID" + userPrincipal.getId());
// topic을 통해 특정 topic에 대한 구독을 하고
// publish가 된다면, redisAlarmSubscriber의 onMessage가 실행된다.
redisMessageListener.addMessageListener(redisAlarmSubscriber, topic);
emitterRepository.putTopic(userPrincipal.getId(), topic);
return alarmService.connectAlarm(userPrincipal.getId());
}
- 어떤 컨트롤러에서 sse를 구독하면서 topic을 등록한다고 가정해보자.
- redisMessageListener를 통해서 subscriber와 topic을 등록하게 된다.
- subscriber 내부의 onMessage로 message를 받았을 때 행동을 정의하고, 구독할 topic을 정의한다.
@RequiredArgsConstructor
@Service
public class RedisAlarmSubscriber implements MessageListener {
private final ObjectMapper objectMapper;
private final RedisTemplate<String, String> redisTokenTemplate;
private final AlarmService alarmService;
@Override
public void onMessage(Message message, byte[] pattern) {
try {
String publishMessage = redisTokenTemplate.getStringSerializer().deserialize(message.getBody());
Long userId = objectMapper.readValue(publishMessage, long.class);
alarmService.send(0L, userId);
} catch (Exception e) {
log.error(e.getMessage());
}
}
}
- Subsriber는 위와 같다.
- message를 deserialize 하여 원하는 데이터의 형태로 사용할 수 있다.
- 이후 데이터를 활용하여 원하는 action을 취할 수 있다.
@RequiredArgsConstructor
@Service
public class RedisAlarmPublisher {
private final RedisTemplate<String, String> redisTokenTemplate;
public void publish(ChannelTopic topic, Long userId) {
redisTokenTemplate.convertAndSend(topic.getTopic(), userId.toString());
}
}
- Publisher는 비교적 간단한데
- 앞서 말했던 인자에 있는 topic을 통해 pushlish를 하게 되고, 원하는 message를 보낼 수 있다.
- 이 topic을 토대로 Subscriber가 메세지를 받을 수 있다.
참고로 위와 같은 일이 가능한 이유는...
- 서버 구조체에는 pubsub_channels 필드와 pubsub_patterns 필드가 있다.
- dictEntry 의 key field(robj channel)이 channel을 가리킨다(topic)
- 그리고 linked list로 client를 가지고 있다
- 따라서 publish 명령은 channel을 hash table에서 찾고 리스트에 저장되어 있는 클라이언트들에게 하나씩 메세지를 보낸다.
'데이터베이스 > Redis' 카테고리의 다른 글
Redis 설치부터 Cluster 구성까지 (0) | 2024.01.17 |
---|
Comments