[HG닷컴] #1 세션 저장소
[Infra] 대용량 트래픽 대응 - 3. 세션 저장소로 어떤 것을 사용할 것인가 (tistory.com)
[Infra] 대용량 트래픽 대응 - 3. 세션 저장소로 어떤 것을 사용할 것인가
앞서 Scale out을 서버 성능 향상 기법으로 선택했을 때 생기는 문제점인 데이터 불일치 문제에 대해서 세 가지의 기법을 알아보았고, 그 중 세션 저장소 분리 방법을 적용하기로 하였다. 그렇다면
hgcode.tistory.com
앞서 작성한 글에서 세션 저장소로 어떤 것을 사용할 것인지 알아본 바 있었다.
내가 진행한 프로젝트 역시 세션 저장소를 필요로 했다. 분산 처리 환경을 가정했을 때 세션 저장소의 분리 방법을 활용하기 위해서는 In-memory 방식의 데이터베이스 (빠른 요청 처리, 세션의 특징을 고려) 를 요구했고, 이 In-memory 방식의 데이터베이스 중 대표적인 것들인 Redis와 Memcached 중 하나를 선택하여야 했다.
왜 사람들은 Redis 혹은 Memcached의 적용을 선호하는가
사람들은 이 두 데이터베이스를 두고 어떤 것을 인메모리 데이터베이스로 택할 것인지 많이 고민한다. 이를 입증이라도 하는 듯 AWS나 Azure같은 서비스에서도 이를 위한 엔진으로 Redis와 Memcached를 지원하고 있다. 인메모리 데이터베이스의 종류는 다양한데 유독 사람들이 이 두 가지의 데이터베이스를 선호하는 이유는 무엇일까

Redis와 Memcached는 인메모리 데이터베이스이자 NoSQL의 한 종류이다. NoSQL은 데이터를 어떤 구조로 저장하는가에 따라 Key-Value, Document, Graph 등으로 나뉜다. 그 중에서도 Redis와 Memcached가 속한 Key-Value Store는 말 그대로 Key와 Value가 쌍으로 저장되는 데이터베이스이다. 단순한 구조를 갖고 있어 일반적인 SQL처럼 여러 테이블에 걸친 복잡한 연산을 수행할 수는 없지만, Key 하나로 데이터에 빠르게 접근 가능하기 때문에 일반적으로 다른 유형의 NoSQL보다 읽기/쓰기 연산을 빠르게 수행할 수 있다.
특히, 로그인에 필요한 세션 데이터의 경우 웹 어플리케이션의 대부분의 기능에 사용되는데도 주로 단순한 연산을 필요로하기 때문에 다른 데이터베이스에 비해 오버헤드가 적은 Key-Value 저장소가 세션 저장소로 많이 사용될 수밖에 없다.
Redis
Redis의 개념
Redis는 오픈 소스로써, 성능은 Memcached에 버금가면서 다양한 데이터 구조체를 지원하므로 DB, Cache, Message Queue, Shared Memory 용도로 사용될 있다.
Redis는 Remote Dictionary Server의 약자로 외부에서 사용 가능한 Key-Value 쌍의 해시 맵 형태의 서버라고 생각할 수 있다. 따라서 별도의 쿼리 없이 Key 값만을 통해 원하는 결과를 가져올 수 있다.

또한, 디스크에 데이터를 쓰는 구조가 아닌 메모리에서 데이터를 처리하기 때문에 작업 속도가 상당히 빠르다.
따라서, Redis를 한 줄로 정의하자면 고성능 Key-Value 저장소로써 String, List, Hash, Set, Sorted Set 등의 자료구조를 지원하는 NoSQL이다.
Redis의 특징
- 영속성을 지원하는 인메모리 데이터 저장소
- 다양한 자료 구조를 지원한다.
- 싱글 스레드 방식으로 인해 연산을 원자적으로 수행 가능하다.
- 읽기 성능 증대를 위한 서버 측 리플리케이션 지원
- 쓰기 성능 증대를 위한 클라이언트 측 샤딩 지원
- 다양한 서비스에서 사용되며 검증된 기술이다.
Redis의 영속성
Redis는 영속성을 보장하기 위해 데이터를 디스크에 저장할 수 있다. 서버가 내려가더라도 디스크에 저장된 데이터를 읽어서 메모리에 로딩한다. 데이터를 디스크에 저장하는 방식은 크게 두 가지가 있다.
- RDB(Snapshotting) 방식 : 순간적으로 메모리에 있는 내용 전체를 디스크에 옮겨 담는 방식
- AOF(Append On File) 방식 : Redis의 모든 write/update 연산 자체를 모두 log 파일에 기록하는 형태
Redis의 컬렉션

Key가 될 수 있는 데이터 구조체가 다양한 것을 알 수 있다. 이렇게 다양한 자료 구조를 지원하게 되면 개발의 편의성이 좋아지고 난이도가 낮아진다는 장점이 있다.
싱글 스레드를 사용하는 Redis
Redis는 싱글 스레드를 사용하므로 연산을 원자적으로 처리하여 Race Condition이 거의 발생하지 않는다.
Race Condition : 여러 process or 스레드가 같은 자원에 동시에 접근하는 경우로써 제대로 처리하지 못할 경우 자원의 불일치가 생길 수 있다.
OS에서 볼 수 있는 race condition은 멀티 프로세서 환경에서 여러 cpu가 같은 자원에 동시에 접근하는 경우가 있다.
예를 들어, 친구 리스트의 친구를 추가하는 연산을 시도해 보자. 아래와 같이 정상적인 상황에서는 유저 각각의 트랜잭션이 순서대로 잘 행해지고 있으므로 문제가 없다.

그러나, 동시에 친구 리스트에 B, C를 추가한다면 어떨까?

두 트랜잭션이 동일한 최종 상태인 A를 자신의 메모리로 읽어 들이고, 그 상태에서 각자 B 또는 C를 추가하게되면 최종 상태가 (A, B) 혹은 (A, C)가 된다. (A, B) 혹은 (A, C)라고 한 이유는 컨텍스트 스위칭에 따라 두 트랜잭션 중 누가 먼저 끝날 지 예측할 수 없기 때문이다. 물론 이러한 Race Condition을 해결하기 위해 격리 수준 등 여러 가지 기법이 있지만, Redis는 싱글 스레드를 사용하므로 하나의 트랜잭션은 하나의 명령만 실행할 수 있으므로 다수의 Race Condition을 해결할 수 있다. (모든이 아닌 다수라고 한 이유는 더블 클릭 이슈는 싱글 스레드만으로 해결 못함)
Memcached
Memcached의 개념
Memcached는 무료로 사용할 수 있는 오픈 소스이며, 분산 메모리 캐싱 시스템이다. DB의 부하를 줄여 동적 웹 애플리케이션 속도 개선을 위해 사용하며, DB나 API 호출 또는 렌더링 등으로부터 받아오는 결과 데이터를 Key-Value 형태로 메모리에 저장한다.
장점
시스템의 사용되지 않는 일부 메모리를 활용할 수 있어 남는 자원을 효율적으로 사용하여 성능을 향상시킬 수 있다.

초창기의 캐시 시스템은 [그림 1]과 같이 각 노드가 완전히 독립적으로 운영되어 데이터를 조회하거나 저장 시 어느 서버를 이용할지 관리해야 하고, 용량의 제한으로 인해 비생산적이며 자원 낭비적인 시스템으로 구성되었다.
Memcached는 이러한 제약 사항을 해결하기 위해 [그림 2]와 같이 consistent hash 알고리즘을 사용하여 물리적인 별도의 캐시 서버를 로직 상 하나의 서버로 보고 사용할 수 있도록 하였다. 즉, 개발자는 서버가 몇 대든 상관없이 한 개의 객체만을 활용하여 저장 및 조회할 수 있으므로 능률적이고 대용량의 캐시 시스템을 갖게 되는 것이다. 또한, 기업의 입장에서는 오래된 저사양 서버의 남는 메모리를 활용할 수 있기 때문에 비용 면에서 효율적일 수 있다.
Consistent Hashing이란 데이터를 노드의 수에 따라 균등하게 분배하여 저장하는 것이다. 보통의 Hashing 알고리즘은 서버에 장애 발생으로 인해 서버의 수가 달라졌을 때 모든 데이터들을 달라진 서버의 수에 맞게 재분배해야 한다. 이는 대량의 캐시 데이터 유실로 이어질 수가 있다. 그러나 Consistent Hashing 알고리즘은 장애가 발생한 서버의 데이터만 유실되고 나머지 서버들은 데이터를 그대로 유지한 채 운영할 수 있다. 또한 서버의 수를 늘린다면 서버 한 대 당 저장하는 데이터가 줄어들기 때문에 피해를 줄일 수 있다.
단점
인 메모리 기반의 시스템이므로 재부팅 시 데이터가 소멸하고, 이로 인해 영구적인 저장용 시스템으로 활용할 수 없다는 문제가 있다. 만약 영구 저장이 필요하다면 해당 데이터를 DB에 저장해 두고, 재부팅 시 DB로부터 데이터를 받아야 한다.
어떤 것을 선택하는 것이 좋은가
Redis와 Memcached의 성능 차이는 미미하다. 두 가지 모두 각자 장단점이 있기 때문에 단순히 이게 무조건 더 좋다라고
할 수 없으며 자신이 개발하고 있는 상황을 고려하여 더 적합한 인메모리 데이터베이스를 선택해야 한다. 나는 다음과 같은 이유로 Redis를 인메모리 데이터베이스로 선택했다.
- Redis가 Memcached에 비해 다양한 기능을 제공한다.
- 애초에 Redis는 Memcached의 단점을 개선하여 만들어진 데이터베이스이다. 그렇기 때문에 Memcached에 비해 어플리케이션 운영에서 유용하게 쓰일 수 있는 편리한 기능들을 많이 제공한다. 또한, 만일 어플리케이션의 서비스가 중단되었을 경우 복구가 비교적 쉬운 Redis가 시스템 상 더 유리하다고 판단하였다.
- Spring Boot에서 Redis를 위한 API를 지원한다.
- 현재 내가 진행하는 프로젝트는 Spring Boot 프레임워크 위에서 개발하고 있다. Spring Boot에서는 Redis API를 지원하기 때문에 Memcached를 사용하는 것보다 Redis를 사용하는 것이 편리하게 개발할 수 있고 이에 따른 유지 보수성도 좋아질 것으로 생각한다.
따라서 Redis를 세션 스토리지로 선택하여 트래픽이 늘어나더라도 쉽게 서버 확장이 가능하면서도 세션을 효율적으로 처리할 수 있는 분산 처리 환경을 구성할 수 있다.