[ReSeller Project] 세션 스토리지로 어떤 것이 더 적합한가 ❓ - Redis VS Memcached

홍정완·2022년 9월 3일
0

ReSeller Project

목록 보기
7/15
post-thumbnail

세션 스토리지로 어떤 것이 더 적합한가 ❓ - Redis VS Memcached


지난 시간에 포스팅했던 Scale out 확장 구조에서 Session 불일치 문제를 어떻게 다뤄야 할까 🤔에서 별도의 세션 스토리지를 구성하기로 했다.


이번 시간에는 세션 스토리지로 적합한 데이터베이스는 무엇일지 고민해 보자.



Session


웹 애플리케이션에서 세션은 사용자의 상태 정보를 유지하는데 주로 사용된다.
특징으로 영구적으로 유지되지 않고, 만료 시간이 지나거나 브라우저가 종료되면 삭제되도록 고안되어 있다.


그 이유는 세션이 서버에 저장되기 때문이다. 사용자가 많고 세션에 저장되는 데이터가 많아질수록 서버 리소스를 많이 차지하고 이로 인한 부하가 매우 커지게 된다. 또한 세션을 오랫동안 유지하게 되면 사용자의 권한을 탈취할 수 있는 위험이 증가하므로 위와 같은 관리가 필요하다. 이러한 세션은 영구적으로 저장되는 데이터가 아니다.



In-memory DB vs Disk based DB


데이터베이스의 데이터가 어느 공간에 저장이 되는가에 따라서 In-memory DBDisk based DB로 분류된다. 전자는 데이터를 메모리에 저장하여 관리하고, 후자는 데이터를 디스크에 저장하여 관리한다.


Disk based DB

먼저 Disk based DB의 접근 과정을 알아보자.

Disk based DB라고 해서 조회한 정보를 디스크에서 직접 찾는 것은 아니다.
디스크에 저장된 데이터를 메모리에 임시 저장소(버퍼) 공간을 마련하여 데이터를 페이지(블록) 단위로 읽어온다. 따라서 데이터를 조회할 때 바로 디스크에서 찾는 것이 아닌 버퍼에서 먼저 찾아보고, 해당 데이터가 버퍼에 존재하지 않을 경우 디스크에 있는 페이지(블록)을 다시 읽어오는 방식이다.


이러한 방식을 사용하는 이유는 기본적으로 메모리 I/O디스크 I/O에 비해 굉장히 속도가 빠르기 때문이다. 그래서 디스크에 가기 전에 읽고자 하는 데이터를 먼저 버퍼에서 찾아보고, 찾지 못할 때만 디스크에서 찾는다.

즉, 필요한 데이터만 메모리로 로딩시켜 처리하는 것이다.

기존 데이터베이스 시스템에서는 성능을 향상시키기 위해 자주 사용하는 데이터를 버퍼에 저장하는 등 다양한 방식으로 디스크 I/O는 최소화하고 버퍼를 효율적으로 활용하는 것에 초점을 맞춘다.

그러나 처리 능력을 강화해도 DB 연산 처리 시간 외에 디스크에서 데이터를 찾을 때 발생하는 대기 시간, 디스크에서 버퍼로의 데이터 전송 시간 등이 발생하기 때문에 방대한 I/O 작업을 처리하는 경우 결국 병목 현상이 생기게 된다.


이러한 이유로 세션이 존재하는지 확인하는 작업을 여러 번 반복해야 하는 서비스에서 세션 스토리지로 Disk based DB를 사용하는 것은 성능적인 측면에서 올바른 선택이 아니라고 생각한다.



In-memory DB

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


In-momery DB는 주 저장소로 휘발성 메모리인 RAM을 사용하기 때문에 기본적으로 영속성을 보장하지 않는다. 예상치 못한 에러나 오류로 인해 프로세스가 종료된다면 데이터가 모두 유실될 수도 있다.


그렇다면, session을 저장하는 저장소로는 적합할까 ❓

세션은 HTTP의 비연결 지향과 상태 없음(stateless)과 같은 특성을 보완하기 위해 사용하는 것이다.
세션에는 주로 로그인한 사용자의 정보를 저장하는데, 이 정보는 영원히 저장되어야 하는 정보가 아니다. 사용자가 로그아웃을 하거나 개발자가 설정해놓은 세션의 timeout에 따라서 세션이 제거된다.

세션에 저장되는 데이터의 위와 같은 특성 덕분에 데이터 유실로 인해 발생하는 피해가 다른 데이터에 비해 적다. 극단적인 예로 세션 저장소의 데이터가 유실된다면 사용자는 재 로그인만 진행하면 된다.
따라서 세션 스토리지로 In-memory DB의 사용이 적절하다고 생각한다.



In-memory DB 중 어떤 DB를 저장소로 사용해야 할까?


In-memory DB에는 다양한 데이터베이스들이 존재한다.
그중에서도 세션 저장소로 가장 많이 사용하는RedisMemcached에 대해서 알아보자.


세션 스토리지를 사용할 때 RedisMemcached를 자주 언급되는 이유는 두 개 모두 key-value 형태로 메모리에 데이터를 저장하는 방식을 사용하고 있기 때문이다. 저장 구조가 Key-Value 형태로 단순하기 MySQL과 같은 RDBMS처럼 복잡한 연산은 지원하지 않지만 세션의 데이터를 읽고 쓰는 작업과 같이 단순한 작업에 있어서는 빠른 처리가 가능하다.


Redis와 Memcached의 차이점


첫 번째Replication의 지원 여부이다.

Redis는 서버에 장애가 발생했을 시, Replication을 솔루션 자체에서 지원하기 때문에 Replication을 지원하지 않는 Memcached 보다 더 쉽게 서버의 데이터를 복구할 수 있다.

마스터 / 슬레이브 Replication을 설정하면 마스터로부터 전체 데이터베이스의 초기 복사본을 받게 된다. 마스터에서 쓰기 작업이 수행되면 Snapshot과 쓰기 명령 로그를 마스터와 연결된 모든 슬레이브로 전송하고 슬레이브는 기존의 데이터를 지우고 전송된 덤프 파일을 로드한다. 이와 같은 복제 프로세스를 통해 마스터와 슬레이브 간 데이터를 실시간으로 동기화할 수 있다. 문제가 발생하여 이에 대한 처리를 해야 하거나 혹은 읽기 전용 서버를 두어야 하는 등의 상황에서 마스터 / 슬레이브 Replication을 설정하면 읽기 성능과 가용성을 향상시킬 수 있다.


반면에 Memcached가 사용하는 Consistent Hashing 모델에 따라 캐시 데이터를 저장하기 때문에 분산 서버에서는 데이터 손실을 최소화할 수 있다. Consistent Hashing이란 데이터를 노드의 수에 따라 균등하게 분배하여 저장하는 것이다. 보통의 Hashing 알고리즘은 서버에 장애 발생으로 인해 서버의 수가 달라졌을 때 모든 데이터들을 달라진 서버의 수에 맞게 재분배해야 한다. 이는 대량의 캐시 데이터 유실로 이어질 수가 있다. 그러나 Consistent Hashing 알고리즘은 장애가 발생한 서버의 데이터만 유실되고 나머지 서버들은 데이터를 그대로 유지한 채 운영할 수 있다. 또한 서버의 수를 늘린다면 서버 한 대당 저장하는 데이터가 줄어들기 때문에 피해를 줄일 수 있다.


💡 Replication

  • 두 개 이상의 DBMS 시스템을 Mater / Slave로 나눠서 동일한 데이터를 저장하는 방식

두 번째로 응답 속도에서 차이를 보인다.

Redis의 경우 대규모 트래픽으로 인해 많은 데이터가 한 번에 업데이트되면 Memcached에 비해서 불안정한 속도를 보인다. 이것은 Redis와 Memcached의 메모리 할당 구조가 다르기 때문이다.

Redis는 jemalloc이라는 알고리즘을 사용하기 때문에 매번 malloc과 free를 통해서 메모리 할당이 이루어진다. 반면에 Memcached는 slab 할당자를 이용하여 내부적으로는 메모리를 재할당을 하지 않고 관리하는 형태를 취한다. 따라서 Redis는 메모리 파편화가 발생하기 때문에 이러한 할당 비용으로 인해 응답 속도가 느려지는 것이다. (매우 드물게 발생)


세 번째RedisMemcached에 비해 다양한 자료구조를 지원한다.

String만 지원하는 Memchached에 비해 Redis는 String, Set, Sorted Set, List, Hash와 같은 다양한 자료구조를 지원한다.


마지막으로 Redis는 싱글 스레드로 수행되고 Memcached는 멀티 스레드로 수행된다.

싱글 스레드로 수행되는 Redis는 atomic을 보장하고, race condition(경쟁)을 회피할 수 있지만 long-time 명령 수행 시 다른 명령어들을 처리할 수 없는 상태가 되기 때문에 매우 비효율 적으로 동작하게 된다. 따라서 long-time 명령의 수행들을 피해야 한다.

반면에 Memcached멀티 스레드를 지원하기 때문에 scale-up을 통한 서버 확장에 용이하다.
또한 Memcached가 멀티 스레드를 지원하긴 하지만 수행 시간적인 측면에서 Redis와 큰 차이가 나지 않는다. 그 이유는 Memcached의 경우 파싱 과정은 멀티 스레드 형태로 동작하지만 실제 데이터에 접근하는 연산 과정에서 Global cache lock으로 인해 싱글 스레드처럼 동작하기 때문이다.


추가적으로, 위에서 In-memory DB는 기본적으로 영속성을 보장하지 않는다고 설명했는데,
RedisSnapshotAOF라는 방법을 통해 영속성을 보장한다.

Snapshot은 사용자가 설정한 특정 시간 조건을 만족할 때 메모리상에 저장되어 있는 모든 데이터에 대해서 복사본을 만들어 저장되는 파일이다. Snapshot에 저장된 Key-Value들을 읽어 메모리에 저장하는 방식으로 복구 작업이 진행된다.

반면 AOF 파일은 Redis가 수신한 명령 중 메모리 내부 데이터에 수정이 이루어진 명령이 기록된 파일이다. AOF 파일에 기록된 로그를 순차적으로 읽으면서 초기화된 데이터베이스에 명령어를 재실행하는 방법으로 복구 작업이 수행된다. 이러한 옵션들은 서버 오류로 인해 강제로 종료되더라도 데이터 손실 없이 다시 복구시킬 수 있다. 그러나 디스크에 기록하는 연산을 추가로 수행해야 하기 때문에 성능이 저하되는 요인이 될 수 있다.



어떤 세션 스토리지를 선택할까 ❓


아래의 기준으로 Redis를 세션 저장소로 선택하게 되었다.


  • Spring Boot에서 Redis를 위한 API를 지원한다.

  • Replication을 이용한 서버 복구 기능이 있는가.



참고

profile
습관이 전부다.

0개의 댓글