개발 공부 기록

[SpringBoot] SpringBoot에서 Redis 사용하기(1) 본문

Spring/Development Log

[SpringBoot] SpringBoot에서 Redis 사용하기(1)

나만없서고냥이 2023. 12. 20. 00:20

✔️ Redis란?

출처 : https://en.wikipedia.org/wiki/Redis

Redis는 Remote Dictionary Server의 약자로, Key-Value 형태로 데이터를 관리하는 오픈 소스 기반의 In-Memory Data Strucutre Store입니다.

캐싱, 세션 관리, 데이터베이스 등의 용도로 사용되며, 데이터를 서버의 주 메모리에 저장하므로 빠른 읽기/쓰기 속도를 제공합니다.

 

Redis는 아래와 같은 다양한 자구조를 지원합니다. 

 

  1. Strings (문자열): 가장 기본적인 key-value 구조로서 간단한 문자열이나 이진 데이터를 안전하게 저장합니다.
  2. Lists (리스트): 순서가 있는 문자열 요소들의 모음이며, 삽입된 순서를 유지합니다. Linked List 형태로 구현되어 있어 리스트의 양 끝에 요소를 추가하거나 제거하는 작업이 빠르게 처리됩니다.
  3. Sets (집합): 유일한 값을 저장하는 자료구조로, 순서는 유지되지 않습니다. 중복된 값을 허용하지 않으며, 교집합, 합집합, 차집합 등의 집합 연산을 지원합니다.
  4. Sorted sets (정렬된 집합): Set과 비슷하지만 각 요소마다 score 값을 가집니다. 이 score를 기준으로 요소들을 정렬하여 저장합니다.
  5. Hashes (해시): 각 키마다 여러 필드와 값들을 저장하는 자료구조로, 내부적으로 key-value 구조를 하나 더 가집니다. 일반적으로 객체를 표현하는 데 사용됩니다.
  6. Bit arrays (비트 배열): 각 비트를 저장하여 비트 수준의 연산을 가능하게 합니다.
  7. HyperLogLogs (하이퍼로그로그): 매우 큰 데이터 집합의 고유 원소 개수를 근사적으로 계산하는 자료구조입니다. 주로 고유한 값을 추정하기 위해 사용됩니다.
  8. Streams (스트림): Redis 5.0에서 도입된 자료구조로, Log나 IoT 신호와 같이 지속적으로 발생하는 데이터를 처리하기 위한 자료구조입니다.

✔️ Redis의 영속성

Redis는 영속성을 보장하기 위해 데이터를 디스크에도 저장할 수 있습니다. 즉, 서버가 내려가도 디스크에 저장된 데이터를 읽어 메모리에 로딩할 수 있다는 의미입니다. Redis에서는 크게 두 가지 방식으로 디스크에 데이터를 저장합니다.

 

1. RDB(Redis DataBase) 방식

출처 : https://server-talk.tistory.com/489

RDB 방식은 주기적으로 혹은 특정 조건을 충족했을 때 Redis의 데이터 스냅샷을 생성하여 디스크에 저장하는 방식입니다. 스냅샷은 즉, 특정 시점의 메모리에 있는 데이터 상태를 의미하며, 서버 재시작 시에 사용됩니다.

 

🧐 RDB 방식을 사용하면 뭐가 좋나요?

  • RDB 방식은 특정 시점의 데이터 스냅샷을 저장하므로, 특히 백업에 효과적입니다. 스냅샷은 주기적으로 생성되기 때문에 이전 스냅샷에 대한 백업을 보존할 수 있으며 즉, 해당 시점의 데이터를 복원할 때 용이합니다. 또한 스냅샷 파일의 크기를 조정하여 디스크 공간을 효율적으로 관리할 수 있습니다.
  • fork()를 통해 간편한 영속화가 가능합니다. 부모 프로세스가 디스크 I/O를 직접 수행하지 않고, 자식 프로세스가 데이터를 저장하게 함으로써 Redis의 부모 프로세스는 클라이언트 요청을 처리하고 메모리에서의 작업을 계속할 수 있습니다. 동시에 자식 프로세스는 fork() 시점의 메모리 데이터를 디스크에 저장하는 작업을 담당하게 됩니다.

🧐 그렇다면 RDB 방식을 사용했을 경우에 단점은 무엇인가요?

  • RDB는 일정한 주기나 특정 조건에 따라 스냅샷을 생성합니다. 그러나 Redis가 멈추는 경우, Redis는 스냅샷을 생성하거나 디스크에 데이터를 저장하는 작업을 수행하지 못합니다. 따라서 Redis가 멈추는 순간부터 최근 스냅샷까지의 데이터는 스냅샷에 반영되지 않아 백업이 불가능해집니다.
  • 자식 프로세스를 이용해 디스크에 데이터를 저장하는 fork() 작업을 수행해야 할 경우, 데이터가 너무 크면 fork() 작업 시간이 길어져 Redis가 잠시 동안 응답하지 않을 수 있습니다. ( AOF 방식 또한 fork() 작업을 수행하긴 하지만, 그에 비해 RDB 방식은 주기적인 fork() 작업으로 인해 저장 작업 시간이 길어질 수 있습니다.)

 

2. AOF(Append On File) 방식

출처 : https://server-talk.tistory.com/489

AOF 방식은 Redis의 모든 write/update 연산을 log 파일에 기록하는 방식입니다. 이 log 파일은 명령어의 연속된 실행을 싥하며, 서버 재시작 시에 데이터를 복구하는 데에 사용됩니다. 

 

🧐 AOF 방식을 사용하면 뭐가 좋나요?

  • AOF 방식은 모든 write/update 연산에 대한 로그를 파싱하기 쉬운 형태인 text 형식으로 보관합니다. AOF 파일을 보면 어떤 명령어가 언제 수행되었는지 알 수 있으며, 각 명령어가 어떤 데이터를 변경했는지 파악하기 쉽기 때문에 데이터의 변경 이력을 살펴보거나 손쉽게 복구할 수 있습니다.
  • AOF 방식은 데이터 내구성을 보장하기 위해 fsync 비활성화, 매초 fsync(default), 매 쿼리 fsync와 같은 다양한 fsync(flush synchronization) 정책을 제공합니다. ( fsync는 데이터를 디스크에 동기적으로 쓰는 작업을 말합니다.) fsync 작업은 백그라운드에서 수행되기 때문에(약 1초 가량의 쓰기 정보만 유실) Redis의 작업이 블로킹되지 않고 계속 진행될 수 있습니다. 높은 내구성이 요구되는 경우엔 데이터를 디스크에 더 자주 쓰거나, 반대로 성능을 우선시하는 경우엔 디스크 쓰기를 최소화할 수 있습니다.

🧐 그렇다면 AOF 방식을 사용했을 경우에 단점은 무엇인가요?

  • AOF 파일은 모든 write/update 명령어, 즉 변경 내역을 계속 기록하기 때문에, RDB 파일에 비해 크기가 큽니다.
  • fsync 정책이 엄격할 경우, RDB에 비해 쓰기 부하가 있을 수 있습니다.

✔️ 어느 방식을 사용해야 하나?

구글링을 해본 결과 AOF 방식을 기본으로 선택하고, fsync 설정을 적절히 조정하여 데이터의 내구성을 높인다고 합니다.

 

그러나, AOF와 함께 RDB를 추가적인 백업 수단으로 사용하는 경우도 있습니다. RDB는 주기적으로 스냅샷을 저장하기에, 빠른 재시작을 위한 데이터베이스 백업에 이러한 RDB 스냅샷을 저장하여 바로 복구하는 방식으로 활용될 수 있습니다.

 


✔️ 어떤 경우에 Redis를 사용하면 좋을까?

📌 실시간 통지 및 알림

Redis의 Pub/Sub 기능을 사용하여 실시간 통지 및 알림을 구현할 수 있습니다.

 

1. 사용자가 애플리케이션의 특정 이벤트에 대한 구독(Subscribe)을 신청합니다.

2. 특정 구독 채널에 사용자를 등록하여 해당 이벤트를 수신할 수 있도록 합니다.

3. 이벤트가 발생하면, 해당 이벤트를 Redis의 특정 채널에 발행합니다.

4. 사용자가 구독한 채널에 새로운 이벤트가 발생했을 때, Redis는 해당 이벤트를 구독하고 있는 사용자에게 실시간으로 전달합니다.

 

전체적으로 위와 같은 과정을 거쳐 실시간 통지 및 알림 기능을 구현할 수 있습니다. 이를 통해 채팅 메시지, 실시간 업데이트, 푸시 알림 등 다양한 실시간 알림 기능을 구현할 수 있습니다.

 

📌 좋아요 혹은 구독 기능

좋아요나 구독과 같이 사용자 당 한 번만 가능한 기능을 구현할 때도 Redis를 사용할 수 있습니다. 

 

1. Redis의 SET을 활용하여 only-one-time:{yyyyMMdd}와 같은 패턴으로 key를 생성합니다. 예를 들어, only-one-time:20231219과 같이 날짜를 key로 사용합니다.

2. 특정 사용자의 ID를 SET의 member(value)로 추가합니다. 예를 들어, SADD only-one-time:20231219 {User ID}와 같이 특정 사용자의 ID를 날짜에 해당하는 SET에 추가합니다.

3. 사용자가 좋아요 혹은 구독과 같은 이벤트에 참여하려 할 때, SISMEMBER only-one-time:20231218 {User ID}을 통해 해당 날짜의 SET에 사용자 ID가 이미 존재하는지 확인합니다.

4. 만약 존재한다면, 해당 사용자는 이미 참여한 것으로 처리합니다.

 

📌 데이터에 유효 시간을 부여

Redis를 통해 특정 데이터의 유효 시간을 제한할 수 있습니다. 예를 들어, 회원가입시 이메일 인증 코드의 유효시간을 설정하는 상황을 가정해봅시다.

 

1. 회원가입시 이메일 인증 코드를 생성하여 Redis에 저장합니다. 이때, auth-code:{User ID}와 같은 패턴으로 key를 생성하여 해당 사용자의 인증 코드를 저장합니다.

2. NX 옵션을 사용하여 이미 해당 키가 존재하지 않을 때만 값을 저장합니다. 이를 통해 기존 인증 코드를 덮어쓰지 않게 합니다.

3. EXPIRE 옵션을 사용하여 해당 데이터의 TTL(Time To Live)을 설정함으로써 데이터가 만료되는 시간을 지정할 수 있습니다. 예를 들어, SET auth-code:{User ID} 1234 NX EXPIRE 180와 같이 유효 시간을 180초(3분)으로 설정합니다.

4. 사용자가 이메일 인증을 시도할 때, Redis의 GET 명령어를 사용하여 해당 키에 저장된 인증 코드를 확인합니다. 값이 존재하면 유효한 인증 코드입니다.

5. 설정된 시간(여기서는 180초) 이후에 Redis는 해당 키와 연결된 데이터를 자동으로 삭제합니다. 이를 통해 인증 코드의 유효 시간을 제한할 수 있습니다.

 


다음 포스팅에서는 이러한 Redis를 SpringBoot에서 직접 사용해보겠습니다.


References