Remote(원격)에 위치하고 프로세스로 존재하는 In-Memory 기반의 Dictionary(Key-value) 구조 데이터 관리 Server 시스템.
Key-value 구조 데이터?
Redis는 인 메모리(In-Memory) 솔루션으로 분류.
다양한 데이터 구조체를 지원함으로써 DB, Cache, Message Queue, Shared Memory 용도로 사용될 수 있다.
일반 데이터베이스 같이 디스크(ssd)에 데이터를 쓰는 구조가 아니라 메모리에서 데이터를 처리하기 때문에 작업 속도가 빠르다.
-> 외부 저장 장치를 사용했다면 메모리와 외부 저장 장치와의 병목현상이 발생했겠지만, 메모리만 사용하기 때문에 데이터 저장 속도가 매우 빠름.
복제?
레디스의 데이터를 거의 실시간으로 다른 레디스 노드에 복사하는 작업.
따라서 서비스를 제공하던 첫 번째 레디스 노드가 다운되더라도, 데이터를 받은 두 번째 레디스 노드가 서비스를 계속 할 수있다.
레디스에서는 첫 번째 노드를 마스터라고 하고 두 번째 노드를 복제(replica)라고 함.
복제기능이 없다면?
-> 레디스 인스턴스가 다운되었을 때, 데이터 복구 불가능
=> 이런 문제를 해결하고자 레디스는 복제 기능 제공.
실 운영환경이라면 마스터-복제로 구성할 것을 권장.
** 마스터와 복제는 물리적으로 다른 머신에 두어야한다.
Redis Replication Introduction 복제 구성 설정 방법
Cache란?
한번 조회된 데이터를 미리 특정 공간에 저장해놓고, 똑같은 요청이 발생하게 되면 서버에게 다시 요청하지 말고 저장해놓은 데이터를 제공해서 빠르게 서비스를 제공해주는 것을 의미.
즉, 미리 결과를 저장하고 나중에 요청이 오면 그 요청에 대해서 DB 또는 API를 참조하지 않고 Cache를 접근하여 요청 처리 기법.
Redis Cache는 메모리 단(In-Memory)에 위치. 따라서 용량은 적지만 접근 속도 빠름.
다만 저장하려는 데이터 셋이 주어진 메모리 크기보다 크면 디스크를 쓰는 것이 올바른 선택
일반적인 캐시 정의를 그대로 구현한 구조.
look aside cache는 캐시를 한번 접근하여 데이터가 있는지 판단 후, 있다면 캐시의 데이터를 사용하고 없으면 실제 DB 또는 API 호출.
주로 쓰기 작업이 많아, INSERT 쿼리를 일일이 날리지 않고 한꺼번에 배치 처리를 하기 위해 사용.
ex) 예매하기 서비스가 있을 때, 여러 고객이 동시에 예매하기 버튼을 누르면 DB에 갑작스럽게 엄청난 쓰기 요청이 몰리게 되면서 DB요청이 죽을 수 있다.
이때 write back 기반의 캐시를 사용하면 캐시 메모리에 데이터를 저장해놓고, 이후 DB 디스크에 업데이트 해주면 안전하게 쓰기 작업 이행할 수 있다.
즉, insert를 1개씩 500번이 아닌 500개를 한번에 삽입하는 동작이 훨신 빨라 write back 방식은 빠른속도로 서비스 가능.
but.. 단점
DB에서 디스크를 접근하는 횟수가 줄어, 성능 향상 기대할 순 있다.
그치만, DB에 데이터를 저장하기 전에 캐시 서버가 죽으면 데이터가 유실된다.
그래서 다시 재생가능한 데이터나, 극단적으로 heavy한 데이터에서 write back 방식을 많이 사용.
ex) 로그를 캐시에 저장하고 특정 시점에 DB에 한번에 저장하는 경우
보통 실무에서는 트래픽 부하 방지 위해 로드밸런서에 서버를 여러대 운영.
-> 서버를 여러대 운영하게 되면 클라이언트 세션이 서로 서버마다 달라 서비스 이용에 지장을 줄 수 있는 문제점 가짐.
단일 서버 환경에서는 session을 통한 로그인을 구현 시 session 불일치 문제를 신경쓰지 않아도 됨.
but, 서비스가 커짐에 따라 한대의 서버로 운영하는 것이 불가능해졌다고 가정시 서버 업그레이드 방식이 두가지 있다.
scale-up
서버 자체 성능을 늘려 부하를 견딜 수 있게 하는 방식.
but, 여전히 서버 한 대에 모든 트래픽 집중되어 만일 서버 장애가 생길 시 서버 복구될때까지 서비스 중단해야하는 상황 발생할 수 있다.
scale-out
서버를 여러대로 늘려, 각 서버에 로드밸런싱으로 트래픽을 분산시킴.
그래서 서버 한대에 장애가 생겨도 다른 서버는 살아있어 서비스 문제가 생기지 않는다.
but, 서비스 이용에 발생하는 커다란 문제점이 생길 수 있는데, 데이터 정합성.
-> 세션 불일치 문제.
왜냐하면 여러 대의 서버가 각각 세션 저장소를 독립적으로 갖고 있어 데이터 불일치 문제가 발생할 수 있다.
** 로드밸런싱?
여러 대의 서버를 두고 트래픽을 분산처리하여 서버의 로드율 증가, 부하량, 속도저하 등을 해결해주는 서비스.
** 데이터 정합성(data consistency)?
어떤 데이터들의 값이 서로 일치함.
** 세션 객체의 형태?
세션 객체는 Key에 해당하는 SESSION ID와 이에 대응하는 Value로 구성.
Value에는 세션 생성 시간, 마지막 접근 시간 및 User가 저장한 속성 등이 Map형태로 저장
로드밸런서가 랜덤으로 아무 서버에게 요청을 보내서 발생하는 문제는,
만일 클라이언트의 요청이 어느 한 서버에 도달해 세션 데이터가 생겼다면,
앞으로 이 서버는 해당 클라이언트만의 요청/응답만 처리하도록 고정시키면 됨.
1. 클라이언트는 서버에게 처음 요청을 전달한다.
그러면 로드밸런서는 서버들 중 하나에게 요청을 보내 처리한다.
2. 서버에서 클라이언트에 응답을 보낼 때, Set Cookie:SERVERID=서버1 이런 형태로 정보를 쿠키에 담아 보낸다.
-> 담당한 서버 정보를 클라이언트의 쿠키에 저장하는 것.
3. 클라이언트가 다시 서버에 요청을 보낼 때 Cookie:SERVERID=서버1을 함께 보냄.
그러면 로드밸런서가 우선적으로 요청해 쿠키 정보가 있는지부터 확인, 쿠키 정보를 확인했다면 해당 요청은 해당 쿠키가 생성되어 있는 서버로 보내짐.
4. 만약 존재하지 않는 쿠키면 로드밸런서의 알고리즘에 의해 선정된 다른 서버에 쿠키가 생성되어 다음에 똑같은 요청이 오면 같은 경로로 맵핑시켜 줄 수 있도록 한다.
=> 동일한 사용자가 세션이 있는 해당 서버에 계속 요청을 보낼 수 있게,
지속적으로 서버 정보가 쿠키를 통해 응답에 삽입되어 보내지게 되어,
클라이언트와 서버가 서로 연결을 유지할 수 있는 것.
=> Sticky sesssion에서는 사용자와 세션 정보를 갖고 있는 서버를 1:1로 매핑해주어 세션 불일치를 해결하지만, 문제가 발생하면 Scale out의 장점인 트래픽 분산과 가용성을 제대로 활용하지 못하게 되는 경우가 발생할 수 있게 된다.
앞서 sticky 세션은 각 서버에 세션을 저장해 놨더니 세션 불일치는 해결이 되었지만,
도리어 성능이 안좋아진다는 결과를 낳음.
-> 세션 정보를 각 서버마다 저장하는게 아닌 세션 데이터를 복사해 서버들에게 전파
Session Clustering은 서버들은 하나의 클러스터로 묶어 관리하고, 클러스터 내의 서버들이 세션을 공유할 수 있도록 하는 방식.
WAS마다 session clustering을 지원하는 방식이 조금 다름.
그 중 가장 대표적인 WAS인 톰캣(Tomcat)에서 Session Clustering을 어떻게 구현??
특정 서버에 생성된 세션을 클러스터를 이루는 모든 서버에 세션을 복제하기 때문에,
클라이언트의 요청을 한 곳으로 지정하지 않아도 되고 다른 서버로 요청을 보내더라도 같은 세션을 유지할 수 있다.
만일 이용하고 있는 서버에 장애가 발생해도 다른 서버에서 세션을 유지하고 있어 클라이언트는 동일한 서비스 환경을 제공받을 수 있다.
하지만 All-to-All 방식에서는 모든 서버가 전체 세션 데이터를 유지하고있어,
다른 서버에서 세션을 찾기 위한 추가적인 네트워크 I/O가 발생하진 않지만, 그만큼 많은 메모리가 필요하다는 단점.
세션 전파 작업 중 모든 서버에 세션이 전파되기까지의 시간차로인한 세션 불일치 문제와 같은 예상치 못한 문제가 발생할 가능성이 존재.
Primary 서버와 Secondary(Backup)서버에만 전체 세션을 복제 후 저장하되,
나머지 이외의 서버들에는 세션의 Key에 해당하는 JSESSIONID(톰캣 컨테이너에서 세션을 유지하기 위해 발급하는 키)만 복제, 저장함으로써 메모리 절약할 수 있는 방식.
하지만, Primary, Secondary 서버를 제외한 다른 서버에 세션 정보를 요청 시, 다른 온전한 세션 정보를 얻기 위해서는 Primary, Secondary에 다시 요청을 보내야 한다는, 세션 복제를 위한 과정이 수행되는 문제점 존재.
Primary-Secondary Session Replication 방식은
비교적 대규모 클러스터 환경에서 적합한 방식.
서버 세팅의 어려움
이 방식은 scale out 관점에서 새로운 서버가 하나 뜰 때마다 기존에 존재하던 WAS에 새로운 서버의 IP/Port를 입력해서 클러스터링 해줘야하는 불편함이 있다.
추가 메모리 비용
서버마다 동일한 세션 정보를 가지고 있어야 해서, 서버가 확장될수록 복제해야 할 세션 데이터가 늘어나고 이는 추가적인 오버헤드로 이어진다.
Tomcat을 예로 들었을 때, 모든 데이터를 각각의 Tomcat 노드에게 전달해야 하고, 배포하는 노드가 아닐 경우에도 복사를 진행해 불필요하게 메모리를 차지한다.
즉, 효율적인 메모리 관리가 이루어지지 않는다.
네트워크 트래픽 증가
데이터 변경이 발생할 때 마다 세션을 전파(복사)하는 작업이 일어나 네트워크 요청 트래픽이 증가.
서버가 늘어날수록 이 트래픽은 더 심해진다.
또한, 복사 뿐만 아니라 서버가 늘어남에 따라 세션을 저장하고 찾아오는 과정에서 추가적인 트래픽이 발생할 수도 있어 확장에 한계가 있다.
시차로 인한 세션 불일치 발생
세션 전파 작업 중 모든 서버에 세션이 전파되기까지의 시간차로 인한 세션 불일치 문제와 같은 예상치 못한 문제 발생 가능성 존재.
=> 이처럼 세션 클러스터링은 sticky 세션의 문제점인 특정 서버에만 트래픽이 몰리는 문제 해결가능.
but, 세션 클러스터링이나 sticky 세션이나 서버가 세션이라는 상태(데이터)를 가진다는 것은 변함 없다는 특징이 있다.
"서버가 상태(데이터)를 가진다"라는 의미는 Scale out방식으로 확장했을 때 서버가 가지고 있는 데이터를 확장하는 서버에도 똑같이 맞춰줘야 한다는 뜻이다. ->오버헤드로 이어짐.
==> 세션 클러스터링은 정합성 이슈를 해결할 수 있지만, 성능적인 한계가 존재함.
위의 두 방식의 단점을 보완해 다중 서버에서 세션 공유하는 방식.
별도의 세션 저장소를 외부에서 생성하고 각 서버들이 가져와 사용.
세션 스토리지는 기존의 서버 내 세션 저장소를 이용하지 않고, 로컬 서버에서 분리해 별도의 세션 저장소를 두고 서버들이 이를 공유함으로써 세션 불일치 해결.
따라서 새로운 서버를 추가하더라도 추가한 서버에만 세션 저장소 정보를 명시해주기만하면 되어 기존 서버의 수정이 발생하지 않는다는 장점.
그래서 세션을 저장 시 세션을 복제해 다른 서버들에 보낼 필요가 없어 WAS들끼리 불필요한 네트워크 통신 과정을 진행하지 않아도 되어 성능면에서도 유리.
또한, 한 서버에 장애가 발생하더라도 세션은 이와 독립되어 별도로 존재해서 세션을 활용한 서비스에 영향을 미치지 않는다.
Disk Database(MySql, Oracle)
세션 데이터를 디스크에 저장하는 것.
전원이 공급이 안되도 디스크에는 정보를 잃지 않고 잘 유지 가능.
하지만 속도가 느리다는 단점..
In-Memory(Redis / Memchached)
데이터를 메모리에 저장하는 방식,
I/O 속도가 디스크에 비교해서 매우 빠름.
하지만 전원이 공급되지 않으면 기억하고 있는 데이터를 모두 잃어버림.
문제가 생기면 모든 서버 장애
세션 클러스터 같은 경우 하나의 서버에 장애가 생겨도 나머지 서버에 미리 복제해둬 문제는 없다.
그러나 세션을 저장하고 있는 Session storage 자체에 장애가 발생 시 모든 세션을 잃어버려 세션을 사용하는 모든 서버에 영향이 끼치는 위험 존재.
그래서 이러한 문제를 보완하기 위해 동일한 세션 저장소를 하나 더 구성하는 방법(마스터-슬레이브 복제)으로 해당 문제 해결할 수 있다.
성능적인 마이너스
별도의 Session storage로부터 세션을 불러와야 해 추가적인 네트워크 I/O가 발생.
세션을 외부에서 가져와 사용해, 로컬 메모리에 저장해 사용하는 것보다 성능적인 면에서 떨어질 수 밖에 없다.
별도의 세션 스토리지를 구성해 세션의 정합성 문제를 해결하려고 할 때,
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는 싱글 스레드 기반.
그래서 한 번에 딱 하나의 명령어 실행해
긴 처리시간이 필요한 명령어를 쓰면 불리하고, 요청 건을 처리하기 전까지 다른 서비스 요청을 받아들일 수 없고 서버가 다운되는 현상 발생.
따라서 전체 데이터를 다루는 시간복잡도를 가진 O(N) 명령어 keys, flush, getall 은 주의해서 사용!!!
메모리를 할당 받고 해제하는 과정에서 부분부분 빈 공간이 생겨 낭비 발생.
이 현상이 계속되면 실제 physical 메모리가 커져 프로세스가 죽는 현상 발생.
또는 쓰기 연산이 copy on write 방식으로 동작해 최대 메모리를 2배 이상까지 사용 가능.
그래서 redis를 사용시 메모리를 적당히 여유있게 사용하는게 좋다.