Hae-Riri / today-alcohol

오늘한주 - 술 레시피 공유 커뮤니티 모바일 앱
0 stars 0 forks source link

Spring Data Redis #12

Open Hae-Riri opened 3 years ago

Hae-Riri commented 3 years ago

Spring Data Redis

Spring Data에서 지원하는 key-value 저장소로, 휘발성 멤캐시디(범용 분산 캐시 시스템)와 비슷하다.

  1. Pub/Sub 아키텍처 간단히
  2. Redis Pub/Sub
  3. Java Redis Client, Lettuce
  4. Spring Data Redis Pub/Sub

1. Pub/Sub

1.1 (HTTP) Request / Response vs Messaging

일반적인 시스템 통합 방법으로는 아래 방법들이 있다.

1.2 Messaging Pub/Sub vs Message Queuing

메시징은 포괄적인 단어이고, '메시징'을 구현하는 방법은 매우 다양하다. 그 방법들 중 Pub/Sub와 메시지 큐잉을 구분하자.

메시지 큐잉

메시지 큐잉 은 오직 한 수신자만 메시지를 수신한다. 주문을 처리하는 클라이언트가 있다면 3건의 주문을 순차적으로 받아서 처리한다. image

이때, 너무 처리시간이 느려서 채널에 메시지가 쌓일 수 있는데 그러면 심각한 병목 현상이 발생한다. 이를 해결하기 위해, 메시지들을 처리할 수 있는 클라이언트를 늘려주면 동시에 여러 메시지를 처리할 수 있다. image

클라이언트는 독립된 환경에서 실행되기 때문에 각각 '경쟁 소비자'가 되어서 각 메시지에 대한 처리 순서가 보장되지는 않는다. 만약 주문1이 잘 처리가 안된다면 서버는 다시 주문1을 보내게 되고, 이 이벤트는 주문2, 주문3 이후에 처리가 될 수도 있다. 따라서 이벤트에 대한 순서를 보장할 필요가 있다.

Pub/Sub

메시지 큐잉과 달리 수신자(클라이언트) 모두에게 동일한 메시지를 전송한다. image

1.3 Redis Pub/Sub

Redis Pub/Sub는 매우 심플한 Pub/Sub 기능을 제공하는데 Redis에서는 Pub/Sub 메시지를 별도로 저장하지 않고, 메시지를 한번 보내면 끝난다. 즉, 메시지 지속성이 없다. 메시지를 전송한 후 해당 메시지가 삭제되는데 Redis 어디에도 저장되지 않는다.

2. Redis Pub/Sub

2.1 Redis

Redis란 다음 특징을 갖는 data structure 다.

2.2 Redis Pub/Sub

Java의 Redis Client

Hae-Riri commented 3 years ago

Spring Data Redis Overview

Redis를 마치 JPA repository를 쓰듯 사용하도록 인터페이스를 제공하는 스프링 모듈

Redis

key-value 구조의 비정형 데이터를 저장, 관리하기 위한 비관계형 데이터베이스 관리 시스템으로, Memcached와 자주 비교되는 메모리 기반 저장소다.

Memcached vs Redis와 Collection

Memcached에서는 collection을 제공하지 않지만 Redis는 제공한다. Collection이 왜 중요할까?

개발의 난이도 : 친구 리스트를 key-value 형태로 저장해야 한다면?

결국, 외부 Collection을 잘 이용하는 것으로 여러가지 개발 시간을 단축시키고 문제를 줄여줄 수 있어서 중요하다.

컬렉션의 종류는 아래에 적었다.

Collection 주의 사항

Redis 운영 시 기억할 것

Redis 언제 쓰나?

Reactive Redis와 command들

reactive redis는 spring-data-redis 에 통합되어있지 않고 별도로 spring-boot-starter-data-redis-reactive를 추가해야 한다. junit 테스트도 처리할 수 없어서 별도로 io.projectreactor:reactor-test도 추가해야 한다.

1. String structure command

2. Lists structure command - 순서 있고 value 중복 허용

3. Hashes structure command - 순서 없고 key 중복허용안함, value 중복허용

  • Key 밑에 sub Key가 존재한다.
  • redis hash command 인 hset, hget, hlen, hdel에 대해.
  • Hmset
  • Hgetall : 해당 Key의 모든 subkey와 value 가져옴
  • reactive로 처리할 때 논블로킹으로 처리되고 원래도 순서가 보장되지 않는 타입이기 때문에 reactive로 인해 순서가 바뀌어 저장되어도 별 문제 없다.

    간단한 sql 문을 대체한다면?

  • insert into users(name, email) values ('ehl3288', 'ehl3288@naver.com')
  • hmset ehl3288 name ehl3288 email ehl3288@naver.com

4. Set structure command - 순서 없음, value 중복 허용 안함

  • 데이터가 있는지 없는지만 체크하는 용도임
  • redis set command인
    • sadd : value가 이미 key에 있으면 추가 안함
    • scard
    • smembers : 이 key에 대한 모든 value를 돌려줌
    • sismember : value가 존재하면 1, 없으면 0
  • 얘도 reactive로 하면 논블로킹 처리이고, 원래 순서가 보장되지 않는 타입이다. 그래서 reactive에서 별 문제 없다.

5. SortedSet structure command - 순서 있음, value 중복 허용 안함

  • 랭킹에 따라 순서가 바뀌길 바랄 때 사용함
  • redis sortedset command인
    • zadd : value가 이미 key에 있으면 해당 score로 변경
    • zcard
    • zrange : 해당 Index 범위의 값을 모두 돌려줌.
    • zrank
  • 원래는 순서가 보장되는 타입이기 때문에 reactive를 통해 저장할 때 순서가 바뀌면서 문제가 될 것만 같다. 그러나 저장시 입력 받는 score 값으로 정렬할 경우, 입력 순서는 문제없다. 주의사항

    SortedSet의 score는 double 타입이기 때문에 값이 정확하지 않을 수 있다. 컴퓨터에서는 실수가 표현할 수 없는 정수값들이 존재하니까. Long 정수형 말고 string으로 보내야 함.

    간단한 sql 문을 대체한다면? with 정렬

  • select * from rank order by score limit 50, 20;
  • zrange rank 50, 70
  • select * from rank order by score desc limit 50, 20;
  • zrevrange rank 50, 70 (거꾸로정렬)

cache와 구조

아주 추상적으로 웹 서비스 구조를 생각하면 [client - web server - db]가 되는데, db안에는 많은 데이터가 있고 이때 주로 디스크에 내용이 저장되게 된다. 사실 DB도 내부에 자기만의 캐시가 있긴 한데, 여러 접근이 생기면 기존 캐시를 없애고 디스크에서 새로 읽어와야 한다. 이때 디스크 접근을 할 때마다 속도가 느려질 수 있다.

  1. Look aside Cache 캐시봤다가 없으면 캐시보기 image
  2. Write Back 데이터를 많이 쓰는 작업일 경우 캐시에 먼저 저장했다가 특정 시점마다 DB에 저장함. insert를 500번 날리는 것보다 500개를 모아서 한번 insert하는 게 훨씬 효율적이다.
    • 얘의 단점은 캐시에 먼저 저장하니까, 문제가 생기면 데이터가 없어져버릴 수도 있다. 리부팅하면 끝장남
    • 로그를 DB에 저장하는 경우에 많이 쓰는 구조다. image

Java의 Redis Client

  • Jedis

    예전부터 쓰여 온 java 표준 redis client

  • Lettuce

    SpringBoot2.0부터 기본 client다. (지금 user-api 는 spring boot 2.3이다. fully non-blocking redis client built with netty providing 리액티브, 비동기와 동기 데이터 접근

Hae-Riri commented 3 years ago

오늘 한 일

과제 진행

  1. 코드 리뷰 검토(?)와 수정
    • UserRequest로 id, seq, idNo를 모두 받아서 한번에 처리하는 부분은 request만 보고 api 역할을 파악하기 어려우니 API를 분리하기로 함.
    • 대신 delete의 경우 dvd-batch에서 필요한 로직만 추가하기
    • 헤더 정보를 파싱하는 부분은 request에 파싱할 필드가 같이 있어서 request로 넣어야할 필드가 뭔지 파악하기 어려움. -> 구조를 어떻게 잡을지에 대해 고민했음.
    • 현재의 해결책 : 구조를 아래와 같이 변경하여 빈 request임을 알 수 있게 하기.
      rpc get (Empty) returns (UserResponse) {}
      message Empty{
      NaverUser headers = 1;
      }
    • 헤더를 넘기는 방법으로 request가 아닌 다른 방안을 사용하면 해결될텐데 찾는 데 어려움을 겼었다. sendHeaders, context 활용 등을 봤으나 get에서 접근하는 법을 찾지 못함ㅠ
  2. 캐시 적용을 위한 reactive redis overview

내일 할 일

  • 추가 코드 리뷰 검토, 반영
  • 캐시 진행

공부한 내용

  • redis, reactive redis의 기본 내용을 훓음.
Hae-Riri commented 3 years ago

과제 목표

  • user-api 구현
  • 1차 과제 발표

금주 이슈 사항

  • header를 request가 아닌 다른 방식으로 전달할 수 있을까. header랑 request를 분리하고 싶다....
    • REST API 에서 request는 request로, header는 @RequestHeader로 접근하듯 grpc에서도 이 둘을 분리하고 싶다.
    • 현재는 interceptor에서 header에 접근하고 이걸 api 메소드로 넘기기 위해 request를 수정한다. (header를 그 안에 추가해서 넘긴다.)
    • 문제점) request를 통해 header를 파싱했더니 request 필드안에 header필드가 같이 들어가서 난잡해짐. 구조를 변경하긴 했으나 여전히 헤더에서 파싱될 필드가 request에 노출되어 있기 때문에, api를 사용하는 입장에서 request를 제대로 파악하기 어려울 것 같다.
    • 해결방안) request에 넣지 않고 다른 방법으로 header를 전달하면 된다. -> 틈틈히 시도할 것 같다..
      • sendHeaders 시도 : interceptor 구현은 가능하나 grpcService에서 받는 방법을 찾지 못함.
      • FromIncomingContext 시도 : golang 코드에서 발견한건데 이 메소드를 찾지 못함. 어디서 나온 건지 모르겠음.;;

주요 진행 사항

  • 1차 과제 발표
  • json to proto 해결
  • user-api 인터페이스의 전반적인 구현 (캐시, 코드리뷰반영 등의 작업 더 필요)

향후 일정

  • 8/9 ~ 8/11 : 코드 리뷰 반영, 캐시 적용
  • 8/11 ~ : nstore-bach의 dvd_customer 로직 제거, tvstore-batch에서 웹툰 api호출