<TIL> 100. Redis - 캐시(Cache)

YUJIN LEE·2023년 4월 17일
1

개발log

목록 보기
93/149

Redis(Remote Dictionary Server)

Remote(원격)에 위치하고 프로세스로 존재하는 In-Memory 기반의 Dictionary(Key-value) 구조 데이터 관리 Server 시스템.

Key-value 구조 데이터?

  • mysql 같은 관계형 데이터가 아닌 비 관계형 데이터 구조.
  • 관계형 데이터베이스처럼 쿼리 연산 지원X but, 데이터의 고속 읽기와 쓰기에 최적화.
  • Redis = NoSQL로 분류.

Redis는 인 메모리(In-Memory) 솔루션으로 분류.
다양한 데이터 구조체를 지원함으로써 DB, Cache, Message Queue, Shared Memory 용도로 사용될 수 있다.

일반 데이터베이스 같이 디스크(ssd)에 데이터를 쓰는 구조가 아니라 메모리에서 데이터를 처리하기 때문에 작업 속도가 빠르다.
-> 외부 저장 장치를 사용했다면 메모리와 외부 저장 장치와의 병목현상이 발생했겠지만, 메모리만 사용하기 때문에 데이터 저장 속도가 매우 빠름.

  • NoSQL
  • Not Only SQL의 약자로써 기존 관계형 데이터베이스(RDBMS)보다 더 융통성 있는 데이터 모델을 사용하고 데이터의 저장 및 검색을 위한 특화된 메커니즘을 제공하는 데이터 저장기술.
    NoSQL DB는 단순 검색 및 추가 작업에 있어서 매우 최적화된 키-값 저장 기법을 사용해 응답속도나 처리 효율 등에 있어서 매우 뛰어난 성능 보여줌.

Redis 특징

  1. NoSQL DBMS로 분류, In memory 기반의 Key-Value 구조를 가진 데이터관리시스템
  2. 메모리 기반이라 모든 데이터들을 메모리에 저장, 조회 매우 빠름.
  3. 메모리에 상주하며 서비스의 상황에 따라 DB로 사용될 수 있고, Cache로도 사용될 수 있다.
  4. Remote Data Storage로 여러 서버에서 같은 데이터를 공유하고 보고 싶을때 사용할 수 있다.
  5. 다양한 자료구조 지원.
  6. 쓰기 성능 증대를 위한 클라이언트 측 샤딩(Sharding) 지원.
    • Sharding: 같은 테이블 스키마를 가진 데이터(row)를 다수의 데이터베이스에 분산하여 저장하는 방법
  7. 메모리 기반이지만 Redis는 영속적인 데이터 보존(Persistence) 가능.(메모리는 원래 휘발성)
  8. 스냅샷 기능을 제공해 메모리 내용을 *.rdb 파일로 저장해 해당 시점으로 복구 가능
  9. 여러 프로세스에서 동시에 같은 key에 대한 갱신을 요청하는 경우, 데이터 부정합 방지 Atomic 처리 함수를 제공한다(원자성)
  10. Redis 기본적으로 1개의 싱글 쓰레드로 수행되기 때문에, 안정적인 인프라를 구축하기 위해서는 Replication(Master-Slave 구조) 필수.

Redis Replication Introduction

복제?

  • 레디스의 데이터를 거의 실시간으로 다른 레디스 노드에 복사하는 작업.
    따라서 서비스를 제공하던 첫 번째 레디스 노드가 다운되더라도, 데이터를 받은 두 번째 레디스 노드가 서비스를 계속 할 수있다.
    레디스에서는 첫 번째 노드를 마스터라고 하고 두 번째 노드를 복제(replica)라고 함.

  • 복제기능이 없다면?
    -> 레디스 인스턴스가 다운되었을 때, 데이터 복구 불가능
    => 이런 문제를 해결하고자 레디스는 복제 기능 제공.
    실 운영환경이라면 마스터-복제로 구성할 것을 권장.

** 마스터와 복제는 물리적으로 다른 머신에 두어야한다.
Redis Replication Introduction 복제 구성 설정 방법

Redis - 캐시(Cache)

Cache란?
한번 조회된 데이터를 미리 특정 공간에 저장해놓고, 똑같은 요청이 발생하게 되면 서버에게 다시 요청하지 말고 저장해놓은 데이터를 제공해서 빠르게 서비스를 제공해주는 것을 의미.

즉, 미리 결과를 저장하고 나중에 요청이 오면 그 요청에 대해서 DB 또는 API를 참조하지 않고 Cache를 접근하여 요청 처리 기법.

Redis Cache는 메모리 단(In-Memory)에 위치. 따라서 용량은 적지만 접근 속도 빠름.
다만 저장하려는 데이터 셋이 주어진 메모리 크기보다 크면 디스크를 쓰는 것이 올바른 선택

캐시의 구조 패턴

Look aside Cache 패턴

일반적인 캐시 정의를 그대로 구현한 구조.
look aside cache는 캐시를 한번 접근하여 데이터가 있는지 판단 후, 있다면 캐시의 데이터를 사용하고 없으면 실제 DB 또는 API 호출.

Look aside Cache 쿼리 순서

  1. 클라이언트에서 데이터 요청
  2. 서버에서 캐시에 데이터 존재 유무 확인
  3. 데이터가 있다면 캐시의 데이터 사용(빠른 조회)
  4. 데이터가 없다면 실제 DB 데이터에 접근
  5. 그리고 DB에서 가져 온 데이터를 캐시에 저장하고 클라이언트에 반환
  • Cache Miss: 메모리에 찾고자 하는 데이터가 없어서 디스크에 조회
  • Cache Hit: 메모리에 찾고자 하는 데이터가 있을 때

Write Back 패턴

주로 쓰기 작업이 많아, INSERT 쿼리를 일일이 날리지 않고 한꺼번에 배치 처리를 하기 위해 사용.

ex) 예매하기 서비스가 있을 때, 여러 고객이 동시에 예매하기 버튼을 누르면 DB에 갑작스럽게 엄청난 쓰기 요청이 몰리게 되면서 DB요청이 죽을 수 있다.
이때 write back 기반의 캐시를 사용하면 캐시 메모리에 데이터를 저장해놓고, 이후 DB 디스크에 업데이트 해주면 안전하게 쓰기 작업 이행할 수 있다.

즉, insert를 1개씩 500번이 아닌 500개를 한번에 삽입하는 동작이 훨신 빨라 write back 방식은 빠른속도로 서비스 가능.

but.. 단점
DB에서 디스크를 접근하는 횟수가 줄어, 성능 향상 기대할 순 있다.
그치만, DB에 데이터를 저장하기 전에 캐시 서버가 죽으면 데이터가 유실된다.

그래서 다시 재생가능한 데이터나, 극단적으로 heavy한 데이터에서 write back 방식을 많이 사용.
ex) 로그를 캐시에 저장하고 특정 시점에 DB에 한번에 저장하는 경우

write back 쿼리순서

  1. 우선 모든 데이터를 캐시에 저장
  2. 캐시의 데이터를 일정 주기마다 DB에 한꺼번에 저장(배치)
  3. 그리고나선 DB에 저장했으니 잔존 데이터를 캐시에서 제거.

Redis 활용하기 - 세션 스토어(Session Store)

서버 분산 처리 환경에서의 세션 불일치

보통 실무에서는 트래픽 부하 방지 위해 로드밸런서에 서버를 여러대 운영.
-> 서버를 여러대 운영하게 되면 클라이언트 세션이 서로 서버마다 달라 서비스 이용에 지장을 줄 수 있는 문제점 가짐.

서버 다중화 환경에서의 세션 불일치

단일 서버 환경에서는 session을 통한 로그인을 구현 시 session 불일치 문제를 신경쓰지 않아도 됨.
but, 서비스가 커짐에 따라 한대의 서버로 운영하는 것이 불가능해졌다고 가정시 서버 업그레이드 방식이 두가지 있다.

  1. scale-up
    서버 자체 성능을 늘려 부하를 견딜 수 있게 하는 방식.
    but, 여전히 서버 한 대에 모든 트래픽 집중되어 만일 서버 장애가 생길 시 서버 복구될때까지 서비스 중단해야하는 상황 발생할 수 있다.

  2. scale-out
    서버를 여러대로 늘려, 각 서버에 로드밸런싱으로 트래픽을 분산시킴.
    그래서 서버 한대에 장애가 생겨도 다른 서버는 살아있어 서비스 문제가 생기지 않는다.
    but, 서비스 이용에 발생하는 커다란 문제점이 생길 수 있는데, 데이터 정합성.
    -> 세션 불일치 문제.
    왜냐하면 여러 대의 서버가 각각 세션 저장소를 독립적으로 갖고 있어 데이터 불일치 문제가 발생할 수 있다.

** 로드밸런싱?
여러 대의 서버를 두고 트래픽을 분산처리하여 서버의 로드율 증가, 부하량, 속도저하 등을 해결해주는 서비스.

** 데이터 정합성(data consistency)?
어떤 데이터들의 값이 서로 일치함.

** 세션 객체의 형태?
세션 객체는 Key에 해당하는 SESSION ID와 이에 대응하는 Value로 구성.
Value에는 세션 생성 시간, 마지막 접근 시간 및 User가 저장한 속성 등이 Map형태로 저장

세션 불일치 해결방법

Sticky Session 방식 - 고정된 세션

로드밸런서가 랜덤으로 아무 서버에게 요청을 보내서 발생하는 문제는,
만일 클라이언트의 요청이 어느 한 서버에 도달해 세션 데이터가 생겼다면,
앞으로 이 서버는 해당 클라이언트만의 요청/응답만 처리하도록 고정시키면 됨.

1. 클라이언트는 서버에게 처음 요청을 전달한다.
그러면 로드밸런서는 서버들 중 하나에게 요청을 보내 처리한다.
2. 서버에서 클라이언트에 응답을 보낼 때, Set Cookie:SERVERID=서버1 이런 형태로 정보를 쿠키에 담아 보낸다.
-> 담당한 서버 정보를 클라이언트의 쿠키에 저장하는 것.
3. 클라이언트가 다시 서버에 요청을 보낼 때 Cookie:SERVERID=서버1을 함께 보냄.
그러면 로드밸런서가 우선적으로 요청해 쿠키 정보가 있는지부터 확인, 쿠키 정보를 확인했다면 해당 요청은 해당 쿠키가 생성되어 있는 서버로 보내짐.
4. 만약 존재하지 않는 쿠키면 로드밸런서의 알고리즘에 의해 선정된 다른 서버에 쿠키가 생성되어 다음에 똑같은 요청이 오면 같은 경로로 맵핑시켜 줄 수 있도록 한다.

=> 동일한 사용자가 세션이 있는 해당 서버에 계속 요청을 보낼 수 있게,
지속적으로 서버 정보가 쿠키를 통해 응답에 삽입되어 보내지게 되어,
클라이언트와 서버가 서로 연결을 유지할 수 있는 것.

  • 대표적으로 AWS ELB(Elastic Load Balancer)는 Sticky session을 활용한 서비스 제공

Sticky Session 문제점

  • 특정 서버에 트래픽이 집중되는 문제
  • 고정된 세션을 사용한다는 말은 사용자가 접속해야 하는 서버가 고정되어 있어 하나의 서버에 트래픽이 집중될 수 있다는 위험성을 가짐.
    즉, 본래 목적인 로드 밸런싱이 제대로 이뤄지지 않을 수 있다.
  • 세션 정보의 유실
  • 서비스 중 만일 하나의 서버에 장애가 발생하게 되면, 해당 서버를 사용하는 사용자들은 세션 정보를 모두 잃게 된다.
    이렇게 되면 다른 서버에서 세션 인증을 다시 해야 하는 문제가 생기게 된다.

=> Sticky sesssion에서는 사용자와 세션 정보를 갖고 있는 서버를 1:1로 매핑해주어 세션 불일치를 해결하지만, 문제가 발생하면 Scale out의 장점인 트래픽 분산과 가용성을 제대로 활용하지 못하게 되는 경우가 발생할 수 있게 된다.


Session Clustering 방식

앞서 sticky 세션은 각 서버에 세션을 저장해 놨더니 세션 불일치는 해결이 되었지만,
도리어 성능이 안좋아진다는 결과를 낳음.
-> 세션 정보를 각 서버마다 저장하는게 아닌 세션 데이터를 복사해 서버들에게 전파

Session Clustering은 서버들은 하나의 클러스터로 묶어 관리하고, 클러스터 내의 서버들이 세션을 공유할 수 있도록 하는 방식.
WAS마다 session clustering을 지원하는 방식이 조금 다름.
그 중 가장 대표적인 WAS인 톰캣(Tomcat)에서 Session Clustering을 어떻게 구현??

Tomcat Session Clustering

  1. All-to-ALl Session Replication
    하나의 세션 클러스터 내에서 데이터가 변경되면 변경된 사항이 다른 모든 서버로 복제되는 방식.
    톰캣에서 제공하는 DeltaManager 클래스를 통해 구현.

특정 서버에 생성된 세션을 클러스터를 이루는 모든 서버에 세션을 복제하기 때문에,
클라이언트의 요청을 한 곳으로 지정하지 않아도 되고 다른 서버로 요청을 보내더라도 같은 세션을 유지할 수 있다.

만일 이용하고 있는 서버에 장애가 발생해도 다른 서버에서 세션을 유지하고 있어 클라이언트는 동일한 서비스 환경을 제공받을 수 있다.

하지만 All-to-All 방식에서는 모든 서버가 전체 세션 데이터를 유지하고있어,
다른 서버에서 세션을 찾기 위한 추가적인 네트워크 I/O가 발생하진 않지만, 그만큼 많은 메모리가 필요하다는 단점.
세션 전파 작업 중 모든 서버에 세션이 전파되기까지의 시간차로인한 세션 불일치 문제와 같은 예상치 못한 문제가 발생할 가능성이 존재.

  • 톰캣 공식 문서에서도 All-to-All 방식은 소규모 클러스터 환경(노드가 4개 미만)에서 좋고, 이보다 큰 클러스터 환경에서는 추천X.
    DeltaManager를 사용한 방식은 애플리케이션이 배포되지 않은 노드에도 복제를 시도하기 때문에 불필요한 트래픽을 서버에 발생시키는 문제 준재.
    Tomcat에서는 이러한 문제를 해결하기 위해 BackupManager를 이용한 방법 존재.
  1. Primary-Secondary Session Replication

    Primary-Secondary 방식은 Primary 서버의 세션 데이터를 Secondary(Backup) 서버에만 전체 복제하여 저장하는 방식.
    BackupManager 클래스를 통해 이 방식 제공.

Primary 서버와 Secondary(Backup)서버에만 전체 세션을 복제 후 저장하되,
나머지 이외의 서버들에는 세션의 Key에 해당하는 JSESSIONID(톰캣 컨테이너에서 세션을 유지하기 위해 발급하는 키)만 복제, 저장함으로써 메모리 절약할 수 있는 방식.

하지만, Primary, Secondary 서버를 제외한 다른 서버에 세션 정보를 요청 시, 다른 온전한 세션 정보를 얻기 위해서는 Primary, Secondary에 다시 요청을 보내야 한다는, 세션 복제를 위한 과정이 수행되는 문제점 존재.

Primary-Secondary Session Replication 방식은
비교적 대규모 클러스터 환경에서 적합한 방식.

Session Clustering 문제점

  1. 서버 세팅의 어려움
    이 방식은 scale out 관점에서 새로운 서버가 하나 뜰 때마다 기존에 존재하던 WAS에 새로운 서버의 IP/Port를 입력해서 클러스터링 해줘야하는 불편함이 있다.

  2. 추가 메모리 비용
    서버마다 동일한 세션 정보를 가지고 있어야 해서, 서버가 확장될수록 복제해야 할 세션 데이터가 늘어나고 이는 추가적인 오버헤드로 이어진다.
    Tomcat을 예로 들었을 때, 모든 데이터를 각각의 Tomcat 노드에게 전달해야 하고, 배포하는 노드가 아닐 경우에도 복사를 진행해 불필요하게 메모리를 차지한다.
    즉, 효율적인 메모리 관리가 이루어지지 않는다.

  3. 네트워크 트래픽 증가
    데이터 변경이 발생할 때 마다 세션을 전파(복사)하는 작업이 일어나 네트워크 요청 트래픽이 증가.
    서버가 늘어날수록 이 트래픽은 더 심해진다.
    또한, 복사 뿐만 아니라 서버가 늘어남에 따라 세션을 저장하고 찾아오는 과정에서 추가적인 트래픽이 발생할 수도 있어 확장에 한계가 있다.

  4. 시차로 인한 세션 불일치 발생
    세션 전파 작업 중 모든 서버에 세션이 전파되기까지의 시간차로 인한 세션 불일치 문제와 같은 예상치 못한 문제 발생 가능성 존재.

=> 이처럼 세션 클러스터링은 sticky 세션의 문제점인 특정 서버에만 트래픽이 몰리는 문제 해결가능.
but, 세션 클러스터링이나 sticky 세션이나 서버가 세션이라는 상태(데이터)를 가진다는 것은 변함 없다는 특징이 있다.
"서버가 상태(데이터)를 가진다"라는 의미는 Scale out방식으로 확장했을 때 서버가 가지고 있는 데이터를 확장하는 서버에도 똑같이 맞춰줘야 한다는 뜻이다. ->오버헤드로 이어짐.

==> 세션 클러스터링은 정합성 이슈를 해결할 수 있지만, 성능적인 한계가 존재함.


Session Storage 방식

위의 두 방식의 단점을 보완해 다중 서버에서 세션 공유하는 방식.
별도의 세션 저장소를 외부에서 생성하고 각 서버들이 가져와 사용.

세션 스토리지는 기존의 서버 내 세션 저장소를 이용하지 않고, 로컬 서버에서 분리해 별도의 세션 저장소를 두고 서버들이 이를 공유함으로써 세션 불일치 해결.

따라서 새로운 서버를 추가하더라도 추가한 서버에만 세션 저장소 정보를 명시해주기만하면 되어 기존 서버의 수정이 발생하지 않는다는 장점.

그래서 세션을 저장 시 세션을 복제해 다른 서버들에 보낼 필요가 없어 WAS들끼리 불필요한 네트워크 통신 과정을 진행하지 않아도 되어 성능면에서도 유리.
또한, 한 서버에 장애가 발생하더라도 세션은 이와 독립되어 별도로 존재해서 세션을 활용한 서비스에 영향을 미치지 않는다.

Session Storage 종류

  1. Disk Database(MySql, Oracle)
    세션 데이터를 디스크에 저장하는 것.
    전원이 공급이 안되도 디스크에는 정보를 잃지 않고 잘 유지 가능.
    하지만 속도가 느리다는 단점..

  2. In-Memory(Redis / Memchached)
    데이터를 메모리에 저장하는 방식,
    I/O 속도가 디스크에 비교해서 매우 빠름.
    하지만 전원이 공급되지 않으면 기억하고 있는 데이터를 모두 잃어버림.

Session Storage 문제점

  1. 문제가 생기면 모든 서버 장애
    세션 클러스터 같은 경우 하나의 서버에 장애가 생겨도 나머지 서버에 미리 복제해둬 문제는 없다.
    그러나 세션을 저장하고 있는 Session storage 자체에 장애가 발생 시 모든 세션을 잃어버려 세션을 사용하는 모든 서버에 영향이 끼치는 위험 존재.
    그래서 이러한 문제를 보완하기 위해 동일한 세션 저장소를 하나 더 구성하는 방법(마스터-슬레이브 복제)으로 해당 문제 해결할 수 있다.

  2. 성능적인 마이너스
    별도의 Session storage로부터 세션을 불러와야 해 추가적인 네트워크 I/O가 발생.
    세션을 외부에서 가져와 사용해, 로컬 메모리에 저장해 사용하는 것보다 성능적인 면에서 떨어질 수 밖에 없다.

In-memory DB vs Disk based DB

별도의 세션 스토리지를 구성해 세션의 정합성 문제를 해결하려고 할 때,
Session storage를 이용하는 방법이 가장 이상적.

Disk based DB는 디스크에서 데이터를 찾아 페이지 단위로 버퍼를 전송하는 시간이 발생해 여러 I/O 작업 처리에 있어 병목현상 발생.
이런 이유로 세션이 존재하는지 확인하는 작업을 여러번 반복해야 하는 서비스에서 세션 스토리지로 Disk based DB르 사용하는 것은 성능적인 측면에서 올바른 선택이 아니라고 생각.

반면, In-memory DB는 애초에 모든 데이터를 메모리에 저장하기 때문에 Disk I/O 작업이 발생할 일이 없다.
따라서 디스크 기반의 DB에서 발생하는 병목현상 피할 수 있다.

세션은 HTTP의 비연결 지향과 상태없음(stateless)과 같은 특성을 보완하기 위해 사용하는 것.
세션에는 주로 로그인한 사용자의 정보를 저장하는데, 이 정보는 영원히 저장되어야 하는 정보가 아니다.
거기다 세션에 데이터 유실로 인해 발생하는 피해가 다른 데이터에 비해 적다.
극단적인 예로 세션 저장소의 데이터가 유실된다면 사용자는 재 로그인만 진행하면 된다.
그래서 세션 스토리지로 In-memory DB의 사용 적절..

또한 몇몇 In-memory DB는 세션 스토리지 서버에 문제가 생겨 데이터가 유실되는 것을 방지하기 위해 동일한 데이터를 또 다른 세션 스토리지에 복사하는 마스터-슬레이브 복제방법을 사용하거나 'Consistent Hashing'알고리즘을 사용해 가용성을 보장한다.

Redis의 문제점

시간복잡도

Redis는 싱글 스레드 기반.
그래서 한 번에 딱 하나의 명령어 실행해
긴 처리시간이 필요한 명령어를 쓰면 불리하고, 요청 건을 처리하기 전까지 다른 서비스 요청을 받아들일 수 없고 서버가 다운되는 현상 발생.
따라서 전체 데이터를 다루는 시간복잡도를 가진 O(N) 명령어 keys, flush, getall 은 주의해서 사용!!!

메모리 파편화

메모리를 할당 받고 해제하는 과정에서 부분부분 빈 공간이 생겨 낭비 발생.
이 현상이 계속되면 실제 physical 메모리가 커져 프로세스가 죽는 현상 발생.
또는 쓰기 연산이 copy on write 방식으로 동작해 최대 메모리를 2배 이상까지 사용 가능.
그래서 redis를 사용시 메모리를 적당히 여유있게 사용하는게 좋다.

https://inpa.tistory.com/entry/REDIS-%F0%9F%93%9A-%EC%BA%90%EC%8B%9CCache-%EC%84%A4%EA%B3%84-%EC%A0%84%EB%9E%B5-%EC%A7%80%EC%B9%A8-%EC%B4%9D%EC%A0%95%EB%A6%AC

https://inpa.tistory.com/entry/REDIS-%F0%9F%93%9A-%EC%BA%90%EC%8B%9CCache-%EC%84%A4%EA%B3%84-%EC%A0%84%EB%9E%B5-%EC%A7%80%EC%B9%A8-%EC%B4%9D%EC%A0%95%EB%A6%AC

profile
인정받는 개발자가 되고싶습니다.

0개의 댓글