본문 바로가기
Web

다중 서버 환경에서 Session은 어떻게 공유하고 관리할까? - 4편(Redis vs Memcached)

by Hooligans 2020. 7. 8.

개요

 지난 시간 세션 저장소로 In-Memory Database를 사용하기로 하였습니다. 하지만, In-Memory Database에는 다양한 데이터베이스들이 존재합니다. 각각의 데이터베이스는 저장하는 데이터의 형태 및 특성에 따라 분류됩니다. 과연 세션 객체를 저장하기 위해서 어떤 데이터베이스가 적합할까요?

 

지금부터 어떤 데이터베이스에 세션을 저장하는 것이 적합할 지에 대해 하나하나 알아보도록 하겠습니다!

 

세션 객체는 어떤 형태로 구성되어 있을까?

 데이터베이스를 선정하기 전에 어떤 형태의 데이터가 저장되는지가 중요하다고 언급하였습니다. 그렇다면 우리가 저장해야 할 데이터인 세션 객체가 어떤 형태로 구성되어 있는지 알아야겠죠?

 

 우선, 세션 객체에 대해서 간단히 알아봅시다.

 

 세션 객체는 Key에 해당하는 SESSION ID와 이에 대응하는 Value로 구성됩니다. Value에는 세션 생성 시간, 마지막 접근 시간 및 User가 저장한 속성 등 이 Map 형태로 저장됩니다. (그림은 임의의 값으로 표현하였지만, 실제 저장되는 데이터 타입 및 값에 있어서 차이가 있을 수 있습니다.)

 

 세션 객체가 저장되는 방식과 유사하게 Key-Value 형태로 데이터를 저장하는 데이터베이스가 있습니다. 이를 Key-Value 데이터베이스 혹은 Key-Value Model NoSQL이라고 합니다.

 

 이러한 데이터베이스의 특징은 저장구조가 Key-Value 형태로 단순하다는 점입니다. 이로 인해 관계형 데이터베이스와 같이 복잡한 조회 연산을 지원하지 않지만, 단일 키 처리만을 지원하기 때문에 고속 읽기와 쓰기에 최적화된 경우가 많습니다. 특히, 세션에 저장되는 데이터의 경우에는 대부분 비교적 간단한 연산을 통해 처리를 할 수 있습니다. 이러한 점에서 Key-Value 형태의 데이터베이스가 세션 저장소로써 적합하다고 볼 수 있습니다.

 

 그중에서도 세션 저장소로 가장 많이 사용되는 것이 바로 Redis와 Memcached입니다!

redis(좌), memcached(우) logo

 엄밀히 말하면 Memcached는 캐시 솔루션으로써 전원이 꺼지게 되면 데이터가 사라지기 때문에 영속성을 보장할 수 없다는 점에서 데이터베이스 혹은 저장소는 아닙니다. 'Redis의 경우도 마찬가지 아닌가?'라고 생각하실 수 있으나, Redis는 AOF와 RDB 기능을 통해서 디스크에 데이터를 저장하여 영속성을 보장할 수 있습니다. 세션의 경우, 이전 포스팅에 언급했다시피 영속성이 크게 영향을 미치지 않기 때문에 해당 논제에 대해서는 더 이상 언급하지 않겠습니다.

 

과연 두 인 메모리 솔루션 중에서 어떤 것을 사용해야 할까요?

 

먼저, 두 솔루션의 차이점부터 분석해보겠습니다.

Redis와 Memcached 어떤 점이 다를까?

Memcached와 Redis는 In-Memory 기반으로 동작한다는 점과, Key-Value 형태로 데이터를 저장한다는 점에서 공통적인 특성을 지니고 있습니다. 이러한 특성은 두 저장소 모두 빠르게 데이터를 처리할 수 있다는 장점으로 이어집니다. 그렇다면 어떤 차이점을 가지고 있을까요?

 

 첫 번째는 failover(장애 극복 기능)입니다. Redis의 경우, Replication을 지원하기 때문에 서버 하나에 장애가 발생하더라도 복제된 Slave 서버를 Master로 승격시켜서 서비스를 중단 없이 운영할 수 있도록 합니다.

 

Memcached 구조

 하지만, Memcached의 경우에는 Replication을 지원하지 않습니다. 대신에 서비스 중단을 최소화하기 위해서 'Consistent Hashing'이라는 알고리즘을 통해 여러 대의 Memcached 서버에 데이터를 분산 저장하여 관리하게 됩니다. Consistent Hashing으로 인하여 서버 1대가 장애가 발생하더라도, 나머지 노드에 있는 데이터들은 재배치할 필요 없이 장애가 발생한 노드의 데이터들만 손실되기 때문에 나머지 노드들은 지속적으로 운영이 가능합니다.

 

 이러한 방식의 경우,  Memcached 서버의 숫자를 늘리게 되면 그 피해를 감소시킬 수 있습니다. 예를 들어, 3대 중 1대에 장애가 발생하면, 약 33%의 데이터를 손실하지만, 4대 중 1대에서 장애가 발생한다면, 25%의 데이터만 손실됩니다. 그러므로 노드가 많을수록 서비스 장애에 대한 피해는 줄어듭니다.

 

 Memcached가 Replication을 지원하지 않기 때문에 이렇게 손실을 감안하고 운영을 하는 경우도 있지만, 각 노드에 BackUp 서버를 배치하여 직접 failover를 구현하고, 가용성을 보장할 수 있습니다. BackUp 노드를 만드는 방법은 MySQL BinLog를 이용하거나, Primary-Secondary 서버 각 2대의 서버에 데이터를 쓰고 관리하는 방법 혹은 Repcached라는 패치를 통하여 Replication을 구현하는 등 다양한 방법이 존재합니다. 

 

이렇듯 Redis와 Memcached의 경우, 각자의 방법으로 failover를 지원합니다. 다만 Redis의 경우 Replication을 솔루션 자체에서 지원을 하기 때문에 보다 더 쉽게 failover 기능을 구현할 수 있다는 장점이 있습니다.

 

 두 번째는 응답속도의 균일성에서 차이가 발생할 수 있습니다. Redis의 경우, 대규모 트래픽이 발생하면 Memcached에 비해 응답속도의 안정성이 떨어질 수 있습니다. 이유는 Redis는 메모리 할당을 하는 데 있어서 jemalloc 알고리즘을 사용하여 매번 malloc과 free를 통하여 메모리를 할당하기 때문입니다. 이는 메모리 단편화 문제를 발생시키고, 이러한 단편화 문제로 인하여 응답속도가 느려질 수 있기 때문입니다.

반면, Memcached의 경우 메모리 할당에 있어서 slab allocator를 사용하기 때문에 내부적으로 메모리 할당을 다시 하지 않고 관리할 수 있습니다. slab allocator에 대한 상세한 내용은 아래 링크를 확인하시면 더 세부적인 동작을 참고하실 수 있습니다.

 

http://jake.dothome.co.kr/slub/

 

 

Slab Memory Allocator -1- (구조)

Slab Memory Allocator 슬랩(Slab, Slub, Slob) object는 커널이 사용하는 정규 메모리 할당의 최소 단위이다. 커널은 다음과 같이 3가지 중 하나를 선택하여 빌드되어 사용된다. 서로에 대한 차이��

jake.dothome.co.kr

 

 이러한 결과, 응답속도가 일정한가에 대해서는 Memcached가 조금 더 좋은 성능을 보이는 듯합니다. 이 또한 극단적인 예시이기 때문에 Redis의 응답속도는 절대적으로 불안하다고 생각하시면 안 됩니다! "대규모 트래픽이 들어왔을 때, Memcached보다 응답속도 안정성이 떨어질 수 있다." 정도로 이해하시면 될 것 같습니다.

 

 세 번째는 Redis는 싱글 스레드로 동작하고, Memcached는 멀티 스레드로 동작을 한다는 점입니다. 

 

'Redis가 싱글 쓰레드로 설계되었기 때문에 병목현상이 발생하지 않을까?'라는 생각을 하실 수 있습니다. Redis 공식 홈페이지(https://redis.io/topics/faq)에도 이에 대한 FAQ를 진행하여 게시해놓은 걸 보면, 많은 사람들이 이 점에 대해 의문을 갖는 것을 알 수 있었습니다.

 

 이에 대한 답변을 정리해보자면 우선, Redis는 데이터가 메모리에서 저장되고, 관리되기 때문에 CPU가 Redis에서 병목현상을 발생시키는 빈도가 적다고 명시되어 있습니다. 이에 따라 Redis가 제시한 벤치마크의 경우, 평균적으로 Linux 환경에서 Redis는 파이프라인을 사용하면 초당 약 100만 건의 요청을 전달 가능하다고 제시하였습니다. 그러므로 응용 프로그램이 O(N) 혹은 O(log(N)) 정도의 연산을 한다면 CPU에 큰 부담을 주는 것은 아니라고 명시하였습니다. 

 

 그럼에도 불구하고 CPU를 더 많이 사용하기 위해서는 Replication과 Sharding을 활용하여 Redis의 인스턴스를 증가시키고, 각 인스턴스로 데이터를 분산시켜서 사용하면 가능합니다.

 

 얼핏 보면, 'Memcached가 멀티스레드로 동작하기 때문에 훨씬 좋은 성능을 내는 것 아닌가?'라는 생각을 하실 수 있습니다. 그러나 실제 수행 속도에 있어서는 큰 차이를 느낄 수 없습니다.

 

 왜냐하면, Memcached는 명령어를 파싱 하는 것까지는 멀티 스레드 형태로 수행되지만, 실제 데이터에 접근하는 연산들의 경우에는 Golbal cache lock으로 인하여 서버 전체의 캐시에 대해 잠금 상태가 되기 때문입니다. 이로 인해 Memcached는 멀티 스레드로 구성되었음에도 불구하고, 데이터 연산에 있어서는 싱글 스레드처럼 동기화되어서 동작하게 됩니다.

 

 여기까지 보았을 때, Redis와 Memcached 각각 다른 방식으로 캐시를 처리하고 있음을 알 수 있습니다. 두 솔루션 모두 장점과 단점을 지니고 있지만, 위에서 보았듯이 사용할 수 없을 정도로 치명적인 단점을 갖는 솔루션은 없습니다.

 

 그렇다면 결정적으로 이 두 솔루션의 Performance를 비교한 결과를 통해 어떤 솔루션을 사용하는 것이 나을지 알아보겠습니다.

 

Redis vs Memcached 실제로 어떤 솔루션이 빠를까?

 해당 실험의 경우, 아래 논문을 통해 확인한 결과입니다. 각 서버의 환경에 따라 결과가 달라질 수 있으므로 이 결과가 절대적인 것은 아니니, 참고용으로만 확인하시길 바랍니다.

 

https://www.sciencedirect.com/science/article/pii/S1319157816300453

 

A performance evaluation of in-memory databases

The popularity of NoSQL databases has increased due to the need of (1) processing vast amount of data faster than the relational database management s…

www.sciencedirect.com

 

 결과를 표를 통해 각각 요약해 보겠습니다.

 

 

쓰기 성능 비교
읽기 성능 비교

 

해당 표에서 볼 수 있듯이 Memcached의 경우 Write 연산에 있어서, Redis 보다 좋은 성능을 보임을 알 수 있습니다. 하지만, Read 연산에 있어서는 Redis가 더 좋은 성능을 보입니다.

 

 이를 비교해보았을 때, Redis가 세션을 저장하는 데 더 적합하다는 점을 알 수 있습니다. 세션 관련 작업의 경우, 쓰기 연산보다는 읽기 연산이 압도적으로 높은 작업이기 때문에 읽기 성능이 더 좋은 Redis를 사용하는 것이 더 효율적임을 알 수 있습니다. 

 

쓰기 연산에 따른 메모리 사용량
읽기 연산에 따른 메모리 사용량

 마지막으로 Redis가 Read/Write에 있어서 메모리를 더 효율적으로 사용하는 것을 볼 수 있습니다.

 

 위 결과를 종합한 결과, 쓰기 성능에서는 Memcached가 앞서지만 메모리 사용 효율과 처리속도를 모두 고려한다면 Redis가 상대적으로 더 나은 선택임을 알 수 있습니다.

 

이번 프로젝트에서 Redis를 사용한 결정적인 이유는?

 지금까지 Redis와 Memcached 중 세션을 저장하고 관리하기 위해서 어떤 솔루션이 적합할 지에 대해 알아보았습니다. 필자는 위의 내용들을 고려하였을 때, Redis를 이번 프로젝트에서 사용하기로 하였습니다. 하지만 두 솔루션 모두 장, 단점을 가지고 있으므로 제작하는 프로그램에 맞추어서 선택하면 될 것이라고 생각됩니다.

 

 물론 Memcached와 비교해서 Redis가 갖는 가장 큰 장점은 기본적으로는 Key-Value 저장소이지만 Value에 List, Set, Sorted Set 등 다양한 자료구조를 지원한다는 점입니다. 하지만, 이러한 점은 캐시 솔루션으로써 비교할 때 유효하다고 판단하였고, 이는 세션을 위한 솔루션으로써는 큰 메리트로 느껴지지 않아 위의 장단점 비교에서 제거하였습니다. 캐시 솔루션을 고민하시는 분은 이 점을 꼭 고려해보시길 바랍니다!

 

 위에서 언급한 Redis의 장점들뿐만 아니라 이번 프로젝트에서 Redis를 사용한 결정적인 이유는 스프링에서 Redis API를 지원한다는 점이었습니다. AGORA 프로젝트는 Spring 프레임워크를 사용하여 개발 중이기 때문에 Spring-Data-Redis 의존성만 빌드한다면, 쉽게 Redis의 기능을 사용할 수 있다는 장점이 존재하였습니다.

 

 이러한 이유로 세션 저장소로 Redis를 선택하였고, 최종적으로는 다중 서버 환경에서도 세션을 효율적으로 관리할 수 있게 되었습니다.

 

정리해보기

1. 세션 객체는 Key-Value 형태로 저장되기 때문에, 인 메모리 솔루션 중에서도 Redis와 Memcached와 같은 Key-Value Model 솔루션을 사용하는 것이 유리합니다.

 

2. Redis와 Memcached는 각각의 장, 단점을 지니고 있고, 성능적인 측면에서는 Memcached는 쓰기 성능, Redis는 읽기 성능 및 메모리 사용 효율에 있어서 각각 우세함을 보입니다.

 

3. Redis는 스프링에서 API를 지원하므로 쉽게 사용할 수 있습니다.

 

프로젝트 참고

 

f-lab-edu/sns-project

Contribute to f-lab-edu/sns-project development by creating an account on GitHub.

github.com

 

참고