본문 바로가기
Engineering WIKI/Docs

Redis 뿌수기!

by wonos 2022. 12. 30.

Redis 는 무엇이며 언제 사용하는가?

Redis 는 무엇인가?

  • 시스템의 메모리를 사용하는 key, value 데이터 스토어입니다.
  • 디스크까지 접근해서 데이터를 가져오는 방식이 아니라 메모리를 통해 데이터를 가져오는 방식이기에 RDB 보다 접근 속도가 빠릅니다.
  • single 스레드로 동작하여 Atomic 을 보장합니다.

Redis 는 언제 사용하는가?

  • 디스크 I/O 가 빈번하게 발생할 때, 매번 디스크에 접근해 I/O 처리를 한다면 속도가 느릴 것입니다. 이를 해결하기 위해 Redis 를 이용해서 데이터를 캐싱해놓고 빈번하게 발생하는 요청을 처리하는 것입니다.
  • 또한, 이렇게 빈번하게 데이터를 처리하고 RDB 나 NoSQL 에 데이터를 업데이트 하도록 설계할 수도 있습니다.
  • 실제 사례를 들면, API 결과 값에 대해 캐싱을 하거나 세션 값을 캐싱하거나 빈번하게 요청되는 요청에 대해 캐싱하는 사례가 있습니다.

Redis 자료 구조

Hash

  • 하나의 키에 다양한 value 가 올 수 있습니다.
  • 예를 들면, 1234567890 이라는 키를 가지고 column1:value1 column2:value2 이런식으로 저장이 가능합니다
  • RDB 의 PK (key) 나머지 컬럼을 value 로 이해하면 됩니다. 일부를 가져올 수 있고, 전체를 다 가져올 수 있습니다.
    • Member 라는 테이블의 PK 는 member_id 이며, name:test, age:20 이 value 입니다.
    • Name 만 가져올 수도 있고, name, age 전부 가져올 수 있습니다.

List

  • 자바의 연결리스트 입니다. 데이터 삽입 시간복잡도는 O(1) 입니다. (맨 앞이나 맨 뒤에 넣기에)
  • 큐나 스택으로 사용할 수 있습니다.
  • 사용 예시
    • 사용자가 들어갔던 페이지를 저장하고 그것을 웹페이지에 보여주고 싶습니다. 보여줄 때는, 최근에 저장된 순으로 보여주고 싶습니다. (예를 들면, 쇼핑몰 에서 여러 상품 페이지를 클릭하고 내가 최근에 클릭한 페이지 순으로 보여주는 기능이라고 가정하겠습니다.)
    • 위와 같은 상황일 때, List 를 사용해서 데이터가 들어올 때마다 순차적으로 데이터를 쌓아주고 조회가 필요할 때는 순차적으로 보여주면 됩니다. (Stack 방식)
    • 데이터를 너무 많이 쌓기만 해서는 안되므로 특정 페이지 개수를 넘어서면 기존 오래된 페이지를 지우는 방법으로 계속해서 업데이트를 해야할 것입니다.

Set

  • Set 데이터는 순서 보장이 안되지만, 중복을 허용하지 않습니다.
  • 하나의 key 에는 여러 개의 Element 가 올 수 있습니다.
  • Set의 예를 들면, 123456(key) 가 있고, “test”,”test1”,”test2” 이런식으로 데이터를 저장합니다.

Sorted Set

  • Set 데이터에서 Value 에 가중치를 부여하여 정렬 기능이 있는 자료구조입니다.
    • 조금 더 상세하게 얘기하면 Key 하나에 여러 score 와 value 로 이루어졌습니다.
    • Score 를 기준으로 정렬이 이루어집니다.
  • 실제 업무 개발할 때, 정렬 기능이 필요할 경우 해당 자료구조를 사용하는 것에 대해 고려해볼 수 있지만 다른 자료 구조형으로 더 좋은 시간복잡도와 공간복잡도로 해결이 가능한지 비교해봐야 합니다.

Bit

  • 0과 1로 표현할 수 있는 자료구조입니다. (자바의 boolean 과 유사합니다)
  • 예를 들면, 10000(key), 0 (value ) / 10001(key), 1(value)
  • 0과 1을 통해 count 를 할 수도 있고, false, true 로 사용할 수 있습니다.

결론

  • Redis 는 여러 가지의 자료구조를 제공하고 있으며, 용도에 맞는 것을 사용하면 됩니다.
  • Spring data redis 의 경우 opsForList, opsForSet 등의 메소드를 통해 Redis 의 자료구조를 사용할 수 있도록 제공하고 있습니다.

 

Redis 상세 정리 (명령어, 싱글 스레드, 영속성)

Redis 명령어

  • Redis 는 해시 기반의 key-value 형식의 저장방식을 제공하면서 키 등록/ 조회에 O(1) 시간복잡도를 보장합니다.
  • Get / Set : O(1)
  • SADD, LSET, HSET, HGET —> O(1)

싱글 스레드 기반의 Redis

  • Redis 4.0 부터 4개의 스레드로 동작합니다. 메인스레드 1개, 시스템 명령들을 처리하는 thread 3개
  • 메인스레드에서 사용자 명령어를 처리하기에 싱글 스레드로 동작한다고 이해하면 됩니다.
  • 싱글 스레드로 동작하고 해시 기반의 get/set 을 지원하기에 Redis 의 주요 기능들이 O(1) 시간복잡도로 빠르게 처리되며, 데이터를 일관성있게 유지할 수 있습니다.

싱글 스레드 기반의 Redis 주의사항

  • 주의해야할 점은 redis 는 single thread 기반이기에 시간이 오래 걸리는 명령어를 사용할 경우 뒤에 있는 명령어들은 기다려야하므로 성능에 영향을 줄 수 있습니다.
  • 예를 들면, SMEMBERS 는 한 번에 모든 member 를 조회하기에 오랜 시간이 걸릴 수 있습니다. 이에 대한 방안으로 sscan key cursor 를 사용할 수 있습니다.
  • sscan key cursor 는 데이터를 조회할 때, 일부 데이터만 조회하는 명령어입니다.

Redis 영속성 (Persistent)

  • RDB 와 AOF 를 지원합니다. RDB 와 AOF 를 같이 쓰면서 영속성을 유지할 수 있습니다.

RDB

  • Redis 의 memory 를 snapshot 한 데이터입니다. 주의해야할 점은 Redis 는 Single thread 이고, snapshot 이 동작할 때, 모든 명령어 수행이 멈춥니다.
  • 동기식 save 는 실제 운영 환경에서 문제가 있기에, BGSAVE (백그라운드 세이브) 를 지원합니다.
  • 기존 프로세스를 fork 해서 snapshot 을 진행하기에 명령어 멈춤이 없습니다. 단, RDB 기능을 사용할 때는 메모리가 현재 사용량의 2배가 될 수 있음을 알고 있어야합니다. (여유 메모리 확보 필요)

AOF

  • redis 명령어들을 파일에 append 합니다.

Redis 기능

Pub/Sub

  • 메시지를 받으면 subscriber 에게 메시지 전달 가능합니다.
    • 단, Message Queue 와는 다름. Message Queue 의 경우 메시지를 받았다는 ack 가 와야 메시지 큐에서 비워지는데 Redis 는 그냥 전달합니다.

cache aside pattern

  • redis 와는 관계가 없지만 캐시 패턴을 간단히 정리해봤습니다.
  • cache 를 사용하는 패턴 중에 하나로서 다음과 같은 플로우로 동작합니다.
    1. Application 에서 캐시 저장소를 조회합니다. 데이터가 없습니다.
    2. 데이터베이스를 조회합니다. 데이터를 가져와 캐시에 저장하고 Client 에 응답해줍니다.
  • 이 방법을 사용할 때, 주의할 점은 초기에 요청이 많이 들어올 때, key 를 못찾는 경우가 존재할 수 있습니다.
    • 기동할 때, 자주 쓰이는 키를 미리 등록해두면 해결이 가능합니다.

 

Redis 상세 정리 (key, 클러스터, 센티넬)

Redis Key

  • 레디스의 키는 문자열을 사용합니다. 허용되는 최대 키 크기는 512MB 입니다.
  • object:id 의 형태 권장합니다.
    • member:1
    • member:2

센티넬

  • 센티넬이란 master/slave 가 정상 동작하는지 모니터링 하며, 문제가 생겼을 경우 조치해주는 모듈
    • 마스터가 문제가 있으면 slave 를 master 로 설정
  • ping 을 통해 노드들의 장애를 판단하며, 여러 대의 Sentinel 에 의해 판단을 합니다.

Redis 클러스터

  • 클러스터란 여러 개 서버를 묶어서 하나의 시스템처럼 동작하게 하는 방식입니다.

Redis 클러스터 특징

  • 데이터 셋을 여러 노드로 자동 분할하는 기능을 제공합니다.
    • Hash slot 을 통해 여러 노드에 데이터를 분배 가능합니다.
    • 노드가 추가되거나 노드가 삭제 되도 다운타임 없이 가능합니다.
    • 고가용성을 제공합니다.
      • 전체 노드 중 일부에 장애가 발생했을 때, 그에 대한 방안이 있습니다. (Master/Slave 교체 등)

Redis 클러스터 성능 측정

  • redis-benchmark 사용해서 성능 측정 가능합니다.
  • redis-benchmark 를 사용할 수 없을 경우 ngrinder, application, server (redis 실행중) 를 두고 ngrinder 에서 호출해서 임계점 파악을 할 수 있습니다.

Redis 를 메인 스토리지로 사용할 수 있을까?

  • 데이터가 적다면 사용할 수 있으나 트래픽이 점점 늘어난다면 서버를 늘려야하고, 샤딩에 대한 문제도 처리해야합니다.
  • 서버를 늘릴 때, 용량을 늘리는 것과 memory 를 늘리는 것은 비용차이가 많이 납니다. 그렇기에 데이터가 많이 늘어나는 상황에서 Redis 메인 스토리지는 사용하기 힘듭니다.

 

Redis 상세 정리 (트랜잭션과 Lock)

redis 트랜잭션과 Lock

  • redis 는 key,value NoSQL 이지만 transaction, lock 관련 기능을 제공하고 있습니다.

redis 트랜잭션 사용방법 (MULTI)

  • MULTI 명령어를 사용한 후, 입력된 명령어가 QUEUE 에 쌓이며, EXEC 명령어를 사용할 때, QUEUE 에 들어있는 명령어가 한 번에 수행됩니다.
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set member1 testName
QUEUED
127.0.0.1:6379(TX)> set member2 testName2
QUEUED
127.0.0.1:6379(TX)> EXEC
1) OK
2) OK
127.0.0.1:6379> GET member1
"testName"
  • 만약 QUEUE 에 들어간 명령어 중 잘못된 명령어가 있다면 이전 실행된 명령어들은 Redis 서버에 반영됩니다.

redis Lock (WATCH)

  • WATCH 명령어를 사용해서 특정 Key 에 Lock 을 걸면 Lock 이 걸리게 됩니다.
    • RDB 처럼 Lock 을 계속 잡는게 아니라 트랜잭션이 시작된 상황에서 값 변경을 1번으로 제한하는 기능입니다.
  • 아래 케이스는 터미널 2개를 띄워놓고 값 변경에 실패한 케이스입니다.
    1. terminal 1 에서 ‘lockTest’ 라는 key 에 대해서 lock 을 걸었습니다.
    2. terminal 2 에서 ‘lockTest’ 의 값을 변경했습니다.
    3. terminal 1 에서 트랜잭션을 열고 ‘lockTest’ 값을 변경해서 실행했습니다.
    4. terminal 1 에서 에러가 발생했으며, lockTest 는 값이 1로 조회됩니다.
# terminal 1, terminal 2 로 구분했습니다.
terminal 1 > watch lockTest
OK

terminal 2> set lockTest 1

terminal 1> MULTI
OK
terminal 1> set lockTest 2
QUEUED
terminal 1> EXEC
(error) EXECABORT Transaction discarded because of previous errors.

terminal 1> get lockTest
"1"

결론

  • 트랜잭션을 사용하여 데이터를 묶어야하는 요건이 있을 때는 MULTI 를 사용하며, 특정 데이터에 대해 Lock 을 걸어야 하는 요건이 있으면 WATCH 를 사용합니다.