용로그
article thumbnail

이번 포스팅에서는 우리가 많이 사용하지만, 어쩌면 잘 모를 수 있는 레디스에 관련해서 기술하려고 한다. 레디스는 요즘 거의 모든 서버에서 사용하다시피 핫한 인 메모리 데이터베이스다. 참고로 2023년 1월 db-engines.com 통계자료에서 Redis가 key-value DBMS에서 1위를 차지하고 있다.

 

db-engines.com

많은 개발자들이 사용하는 만큼, 우리도 레디스가 뭔지 알아야하지 않겠는가!? 우선 레디스가 뭔지부터 간단하게 알아보자.

 

레디스(Redis)란?


레디스란 키(key)값(value)를 가진 NoSQL에 속하는 데이터베이스이다. 주로 비정형 데이터를 다루며 비관계형 데이터베이스다.

비정형 데이터? 식별 가능한 구조나 아키텍처가 없는 데이터

key, value 구조이기 때문에 쿼리를 사용할 필요가 없다. 레디스에서 지원하는 자료구조는 아래와 같다.

  • Strings : Vinary-safe한 기본적인 key-value 구조
  • Lists : String element의 모음, 순서는 삽입된 순서를 유지하며 기본적인 자료구로 Linked List를 사용
  • Sets : 유일한 값들의 모임인 자료구조, 순서는 유지되지 않음
  • Sorted sets : Sets 자료구조에 score라는 값을 추가로 두어 해당 값을 기준으로 순서를 유지
  • Hahses : 내부에 key-value 구조를 하나더 가지는 Reids 자료구조
  • Bit arrays(bitMaps) : bit array를 다를 수 있는 자료구조
  • HyperLogLogs : HyperLogLog는 집합의 원소의 개수를 추정하는 방법, Set 개선된 방법
  • Streams : Redis 5.0 에서 Log나 IoT 신호와 같이 지속적으로 빠르게 발생하는 데이터를 처리하기 위해서 도입된 자료구조

 

디스크에서 데이터를 저장 하는 것과는 달리 서버의 주 메모리에 저장하여 디스크에 액세스해야 할 필요를 없앰으로써 검색 시간으로 인한 지연을 방지하고 CPU 명령을 적게 사용하는 좀 더 간단한 알고리즘으로 데이터에 액세스 할 수 있다.

 

레디스에 데이터를 넣고 컴퓨터를 끄면 휘발되나요?


결론부터 말하자면, 그렇지 않다. Redis는 인 메모리 데이터베이스이기에 휘발성인 MM에 데이터를 저장한다. 그렇기 때문에 서버가 다운된다면 데이터의 유실이 일어날 수 있다.

 

이에 레디스는 데이터의 영속성을 보장하기 위해 디스크에도 데이터를 저장한다. 서버가 다운되어도 디스크에 저장해놓았던 데이터를 다시 메모리에 가져올 수 있다. 레디스가 메모리의 데이터를 디스크에 저장하는 방법은 아래와 같다.

 

  • RDB (Redis Data Base): RDB 지속성은 지정된 간격으로 데이터 세트의 특정 시점 스냅샷을 수행한다.
  • AOF (Append Only File): AOF 지속성은 서버에서 수신한 모든 쓰기 작업을 기록한다. 이러한 작업은 서버 시작 시 다시 재생되어 원본 데이터 세트를 재구성할 수 있다.
  • 지속성 없음 : 지속성을 완전히 비활성화할 수 있다. 캐싱할 때 가끔 사용된다.
  • RDB + AOF : 동일한 인스턴스에서 AOF와 RDB를 같이 사용할 수 있다.

RDB

RDB의 장점

  • RDB는 Redis 데이터의 매우 작은 단일 파일 특정 시점 표현이다. RDB 파일은 백업에 적합하다. Ex) 1시간마다 데이터 백업
  • DR(재해 복구)에 매우 유용하다
  • 레디스의 상위 프로세스가 디스크 I/O 등을 수행하지 않기 때문에 성능이 최대화된다.
  • 디스크에서 RDB는 다시 시작 및 장애 조치 후 부분 재동기화를 지원한다.

RDB의 단점

  • 레디스가 오랜 시간 동안 비정상적으로 작동을 멈춘 경우 최근 데이터의 백업이 불가능하다. 일반적으로 5분마다 RDB 스냅샷을 생성하긴 하지만 Redis가 어떤 이유로든 비정상적으로 작동을 멈춘다면, 그에 대한 방안을 마련해야 한다.
  • RDB는 상위 프로세스를 사용하지 않는 만큼 하위 프로세스를 굴린다. 따라서 하위 프로세스는 디스크에 데이터를 유지하기 위해 잦은 디스크 I/O가 일어난다. fork(디스크 I/O)는 데이터 세트가 큰 경우 시간이 많이 소요될 수 있으며, 데이터 세트가 매우 크고 CPU 성능이 좋지 않은 경우 클라이언트 서비스가 다운될 수 있다.

AOF

AOF의 장점

  • AOF는 다른 fsync 정책을 가질 수 있기 때문에 RDB보다 내구성이 높다. fsync는 백그라운드 스레드를 사용하여 수행되며 기본 스레드는 fsync가 진행 중이지 않을 때 쓰기(write) 작업을 계속 진행한다.
  • redis-check-aof가 지원되어 로그 파일을 쉽게 수정할 수 있다.
  • 레디스는 AOF가 너무 커지면 백그라운드에서 자동으로 다시 작성할 수 있다.
fsync? 리눅스 커널에서 파일의 내용을 스토리지 디바이스에 동기화하는 동작이다.

AOF의 단점

  • AOF 파일은 일반적으로 동일한 데이터 세트에 대한 동등한 RDB 파일보다 용량이 크다.
  • AOF는 정확한 fsync 정책에 따라 RDB보다 느릴 수 있다. 일반적으로 fsync를 매초 로 설정해도 성능은 여전히 ​​매우 높으며, fsync를 비활성화하면 높은 트래픽에서도 RDB만큼 빨라질 수 있다.

그래서 뭘 사용해?


레디스가 디스크에 데이터를 동기화하는 방식이 이렇게나 많은데, 우리는 뭘 사용해야 할까? 보통은 AOF 기법을 사용한다고 한다. RDB보다 높은 품질의 데이터 백업이 가능할뿐더러, 성능 또한 RDB보다 많이 뒤처지는 게 아니기도 하다.

 

하지만 레디스 공식 문서에서는 AOF만 사용하는 사람들이 많지만, 때때로 RDB 스냅샷을 갖는 것이 데이터베이스 백업을 수행하고 더 빠른 재시작을 할 수 있다고 한다.

 

그냥 AOF를 default로 하되, RDB를 옵션으로 사용하라는 말인 것 같다. 그냥 RDB + AOF 사용하자.

레디스 적용 사례


우리가 무엇을 개발하고 어떤 상황일 때 레디스 도입을 고려할 수 있을까?

한 번만 가능한 기능

좋아요 또는 구독 기능에서 가장 중요한 포인트는 한 사람당 단 한 번씩만 가능하다는 것이다. 자신이 사용하는 데이터베이스 자체에서도 유니크 제약조건을 설정해서 구현할 수 있지만, 이런 작업 특성상 빈번한 insert, update가 일어나는 작업이기에 성능 저하가 일어날 수 있다.

 

Redis의 SET 자료구조를 활용할 수 있다. Sets의 간단한 옵션은 아래와 같다.

  • SADD - Sets 타입의 key value 추가한다. ( 다중 추가 가능  Space 구분 )
  • SCARD - Sets에 저장되어있는 요소들의 길이를 반환한다.
  • SMEMBERS - 해당되는 key값 안의 요소들을 출력한다.
  • SISMEMBER - 해당 요소가 Sets안에 있다면 1 없다면 0을 반환한다.
  • SMOVE - 해당 요소를 다른 key값으로 이동시킨다.
  • SPOP - 요소에 저장된 값들중 해당 갯수만큼 랜덤으로 POP.
  • SRANDMEMBER - 요소에 저장된 값들중 지정된 count만큼 랜덤 반환한다. ( count가 없으면 1개 반환)
  • SREM - 저장된 요소중에서 일치하는 요소가 있다면 삭제후 1반환 삭제될요소가 없다면 0을 반환한다.

 

only-one-time:{yyyyMMdd}와 같은 패턴으로 key를 사용하는 SET에, User ID를 member로 사용하면

유저가 특정 날짜에 이벤트에 참여했는지를 알 수 있다.

redis:6379> SADD only-one-time:20230118(key) user_1(value)
(integer) 1 # SET에 새로 추가된 user인 경우 1 반환
redis:6379> SADD only-one-time:20230118 user_1
(integer) 0 # SET에 이미 존재하는 user인 경우 0 반환

 

데이터에 유효 시간 부여

예를 들어 회원가입시 이메일 인증이 필요할 때, 이메일 인증 코드의 유효시간을 3분으로 제한하는 기능 등이다.

 

Redis의 SET 커맨드의 option인 NX, EXPIRE를 활용한다. 시간 관련 옵션은 아래와 같다.

  • EX - 지정된 만료 시간을 초 단위로 설정한다.
  • PX - 지정된 만료 시간을 밀리초 단위로 설정한다.
  • EXAT - 키가 만료되는 지정된 Unix 시간을 초 단위로 설정한다.
  • PXAT - 키가 만료되는 지정된 Unix 시간을 밀리초 단위로 설정한다.
  • NX - 아직 존재하지 않는 경우에만 키를 설정한다.
  • XX - 이미 존재하는 경우에만 키를 설정한다.
  • KEEPTTL - 키와 연결된 TTL(Time to Live)을 유지한다.
  • GET - 키에 저장된 이전 문자열을 반환하거나 키가 존재하지 않으면 nil을 반환한다. SET키에 저장된 값이 문자열이 아닌 경우 오류가 반환되고 중단된다.

auth-code:{User ID}와 같은 패턴으로 key를 사용하고, NX(Key가 존재하지 않으면 저장)와 EXPIRE(TTL 기능)를 사용하면

특정 시간 내에 유저가 인증 문자를 받았는지 확인할 수 있다.

redis:6379> SADD auth-code:user_1(key) {AUTH-CODE}(value) EX 180
redis:6379> get auth-code:user_1
유효시간이 만료되지 않았을 때 : return {AUTH-CODE}
유효시간이 만료되었을 때 : none

이 역시 커맨드 실행 시 리턴되는 값을 통해 하나의 커맨드를 통해 시간 내에 인증 문자를 받았는지 아닌지를 확인할 수 있다.

 

이런 기능 말고도 레디스에 특화된 여러가지 기능들이 많이 있다.(실시간 변동 순위,  최근 조회한 데이터, PUB/SUB 지원으로 고성능 채팅방 구현..)

 

정리


이번 포스팅에서는 레디스가 무엇인지, 어떤 것이 장점이고 휘발성 메모리의 단점을 어떻게 보완했는지, 그리고 레디스가 사용될 수 있는 기능과 간단한 사용법들을 알아보았다.

 

다음 포스팅에서는 스프링 부트에서 직접 레디스를 사용해보자.

 

public class Ref {	
    final String SET = "https://redis.io/commands/set/";
    final String 레디스(Redis) 란? = "https://dev-chan2.tistory.com/5";
    final String [Redis] 데이터 타입 Sets = "https://jhleed.tistory.com/128";
    final String [redis] AOF (append-only-file) = "https://knight76.tistory.com/entry/redis-AOF-appendonlyfile";
    final String [Redis] Redis 자료구조 알아보기 = "https://sabarada.tistory.com/134";
    final String All-About-Backend = "https://github.com/Backend-Study-friends/All-About-Backend/blob/whitebear05-database-redis/database/redis/introduce.md";
}
profile

용로그

@용로그

벨덩보단 용덩 github.com/wonyongChoi05