Redis(Remote Dictionary Server)란?
Redis는 Remote(원격)에 위치하고 프로세스로 존재하는 In-Memory 기반의 Dictionary(key-value) 구조 데이터 관리 Server 시스템이다.
Redis는 mysql 같은 관계형 데이터가 아닌 비 관계형 구조로서 데이터를 그저 '키-값' 형태로 단순하게 저장한다.
그래서 관계형 데이터베이스와 같이 쿼리 연산을 지원하지 않지만, 대신 데이터의 고속 읽기와 쓰기에 최적화되어 있다.
그래서 Redis는 일종의 NoSQL로 분류되기도 한다. 또한 Redis는 인 메모리(In-Mememory) 솔루션으로도 분류되기도 하는데, 다양한 데이터 구조체를 지원함으로써 DB, Cache, Message Queue, Shared Memory 용도로 사용될 수 있다.
일반 데이터베이스와 같이 디스크(SSD)에 데이터를 쓰는 구조가 아니라 메모리(DRAM)에서 데이터를 처리하기 때문에 작업 속도가 상당히 빠르다.
Redis는 Java, Python, PHP, C, C++, C#, JavaScript, Node.js, Ruby, R, Go를 비롯한 정말 많은 프로그래밍 언어 프레임워크에 대한 API를 폭넓게 지원한다.
Redis의 특징
- NoSql DBMS(비관계형 데이터베이스)로 분류되며 In-memory 기반의 Key-Value 구조를 가진 데이터 관리 시스템
- 메모리 기반이라 모든 데이터들을 메모리에 저장하고 조회에 매우 빠르다.
- 메모리에 상주하면서 서비스의 상황에 따라 데이터베이스로 사용될 수 있으며, Cache로도 사용될 수 있다.
- Remote Data Storage로 여러 서버에서 같은 데이터를 공유하고 보고 싶을 때 사용할 수 있다.
- 다양한 자료구조를 지원한다. (Strings, Set, Sorted-Set, Hashes, List ...)
- 쓰기 성능 증대를 위한 클라이언트 측 샤딩(Sharding)을 지원한다.
- Sharding : 같은 테이블 스키마를 가진 데이터(row)를 다수의 데이터베이스에 분산하여 저장하는 방법
- 메모리 기반이지만 Redis는 영속적인 데이터 보존(Persistence)이 가능하다. (메모리는 원래 휘발성)
- 스냅샷 기능을 제공해 메모리 내용을 *.rdb 파일로 저장하여 해당 시점으로 복구할 수 있다.
- 여러 프로세스에서 동시에 같은 key에 대한 갱신을 요청하는 경우, 데이터 부정합 방지 Atomic 처리 함수를 제공한다. (원자성)
- Redis는 기본적으로 1개의 싱글 쓰레드로 수행되기 때문에, 안정적인 인프라를 구축하기 위해서는 Replication(Mater-Slave 구조) 필수이다. (http://redisgate.kr/redis/configuration/replication.php)
Redis 활용 - 캐시(Cache)
캐시(Cache)란 한번 조회한 데이터를 특정 공간에 저장해 놓고, 똑같은 요청이 발생하게 되면 서버에게 다시 요청하지 말고 저장해 놓은 데이터를 제공해서 빠르게 서비스를 제공해 주는 것을 의미한다.
즉, 미리 결과를 저장하고 나중에 요청이 오면 그 요청에 대해서 DB 또는 API를 참조하지 않고 Cache를 접근하여 요청을 처리한다.
서비스를 처음 운영할 때는 WEB-WAS-DB 정도로 작게 인프라를 구축하는데, 사용자가 늘어나면 DB에 무리가 가기 시작한다.
DB는 데이터를 물리 디스크에 직접 쓰기 때문에 서버에 문제가 발생해도 데이터가 손실되지는 않지만, 매 트랜잭션마다 디스크에 접근해야 하므로 부하가 많아지면 성능이 떨어진다.
그래서 사용자가 늘어나면 DB를 Scale-in 또는 Scale-out 하는 방식 외에도 캐시 서버를 검토하게 된다.
캐시의 구조 패턴
Look aside Cache 패턴
일반적인 캐시 정의를 그대로 구현한 구조이다.
캐시를 한번 접근하여 데이터가 있는지 판단한 후 있다면 캐시의 데이터를 사용하고 없다면 실제 DB 또는 API를 호출한다.
쿼리 순서
- 클라이언트에서 데이터 요청
- 서버에서 캐시에 데이터 존재 유무 확인
- 데이터가 있다면 캐시의 데이터 사용 (빠른 조회)
- 데이터가 없다면 실제 DB 데이터에 접근
- 그리고 DB에서 가져온 데이터를 캐시에 저장하고 클라이언트에 반환
Write Back 패턴
write back은 주로 쓰기 작업이 굉장히 많아서, INSERT 쿼리를 일일이 날리지 않고 한꺼번에 배치 처리를 하기 위해 사용한다.
예를 들어 영어 듣기 평가를 온라인으로 진행하는 서비스가 있을 때, 여러 학생이 동시에 제출 버튼을 누르면서 DB에 갑작스럽게 엄청난 쓰기 요청이 몰리게 되면 DB 서버가 죽을 수도 있다.
이때 write back 기반의 캐시를 사용하면 캐시 메모리에 데이터를 저장해 놓고, 이후 DB 디스크에 업데이트해 주면 안전하게 쓰기 작업을 이행할 수 있는 것이다.
즉, insert를 1개씩 500번 수행하는 것보다 500개를 한 번에 삽입하는 동작이 훨씬 빠르기 때문에 write back 방식은 빠른 속도로 서비스가 가능하다.
하지만, DB에서 디스크를 접근하는 횟수가 줄어들기 때문에 성능 향상을 기대할 수 있지만, DB에 데이터를 저장하기 전에 캐시 서버가 죽으면 데이터가 유실된다는 문제점이 있다.
그래서 다시 재생 가능한 데이터나, 극단적으로 heavy 한 데이터에서 write back 방식을 많이 사용한다.
예를 들면 로그를 캐시에 저장하고 특정 시점에 DB에 한번에 저장하는 경우가 있다.
쿼리 순서
- 우선 모든 데이터를 캐시에 저장
- 캐시의 데이터를 일정 주기마다 DB에 한꺼번에 저장 (배치)
- 그리고 나선 DB에 저장했으니 잔존 데이터를 캐시에서 제거
Redis 캐시의 활용
Redis는 특히 Remote Dictionary로서 RDBMS의 캐시 솔루션으로 사용 용도가 굉장히 높다.
RDBMS에서 SELECT 쿼리문을 날려 특정 데이터들을 FETCH 했을 때, RDBMS의 구조성 DISK에서 데이터를 꺼내오는 데 Memory에서 읽어 들이는 것보다 천배 가량 더 느리다.
따라서 Redis와 같은 유연한 자료구조를 가지는 인메모리 Key-value 솔루션을 사용하여 DB 부하의 Read 연산의 부하를 분산시키는 데 적용한다.
또 하드 한 작업 같은 경우 쿼리문이 길고 복잡해 기본적으로 데이터베이스를 조회하는 시간이 오래 걸리는데, 만일 이 쿼리가 자주 사용되는 경우라면 해당 쿼리가 전체 서비스 속도의 병목이 될 수 있다.
그럴 때는 쿼리 결과 자체를 Redis로 캐싱을 해두고, 쿼리의 결과가 바뀔 수 있는 이벤트가 발생할 때마다 캐시에 적재를 새로 한다면 전체 서비스 속도를 향상할 수도 있다.
그래서 캐싱이 필요할 때 많이 사용되는데 즉시 메시지를 주고받아야 될 때나, 장바구니의 삭제와 같은 경우에 많이 사용하는 편이다.
또한 RAM은 휘발성인데 그럼 실행 중인 Redis를 끄면 데이터가 전부 날아간다고 생각이 들게 되는데, Redis는 in-memory이지만 persistent on-disk 데이터베이스 이기도 하다.
Redis는 특정한 때에 현재까지의 in-memory 상태를 disk에 저장해 두었다가 Redis를 다시 시작했을 때 disk에 저장해 두었던 dump 파일들을 load 하기 때문에 데이터의 손실 발생을 방지할 수도 있다.
Redis 활용 - 세션 스토어(Session Store)
서버 분산 처리 환경에서의 세션 불일치
보통 실무에서는 트래픽 부하를 방지하기 위해 로드밸런서에 서버를 여러 대 운영한다.
그러나 서버를 여러대를 운영하게 되면 클라이언트의 세션이 서로 서버마다 달라 서비스 이용에 지장을 줄 수 있다는 문제점을 가지게 된다.
우리가 웹 서비스를 이용할 때 비인가 사용자와 인가된 사용자 모두 접근할 수 있는 요청도 존재하고, 인가된 사용자만이 접근할 수 있는 요청도 존재할 것이다.
이렇게 인가된 사용자만 접근할 수 있는 요청을 처리할 때마다 매번 세션 저장소에서 해당 로그인 세션이 존재하는지 확인하는 작업을 진행해야 하기 때문에 성능에 악영향을 주지 않도록 빠르게 세션 정보를 찾아서 제공해야 한다.
이러한 이유로 세션이 존재하는지 확인하는 작업을 여러 번 반복해야 하는 서비스에서 세션 스토리지로 Disk Based DB를 사용하는 것은 성능적인 측면에서 올바른 선택이 아니다.
세션에는 주로 로그인한 사용자의 정보를 저장하는데, 이 정보는 영원히 저장되어야 하는 정보가 아니다.
거기다 세션에 데이터 유실로 발생하는 피해가 다른 데이터에 비해 적다. 극단 적인 예로 세션 저장소의 데이터가 유실된다면 사용자는 재 로그인만 진행하면 된다.
Redis의 문제점 (주의해야 할 점)
시간 복잡도
레디스는 자바스크립트와 같이 싱글 쓰레드 기반으로 돌아간다.
그래서 한 번에 딱 하나의 명령어만 실행하기 때문에, 긴 처리시간이 필요한 명령어를 쓰면 불리하고 요청 건을 처리하기 전까지 다른 서비스 요청을 받아들일 수 없고 서버가 다운되는 현상이 일어날 수 있다.
따라서 전체 데이터를 다루는 시간복잡도를 가진 O(N) 명령어 keys
, flush
, getall
는 주의해서 사용할 필요가 있다.
메모리 파편화
메모리를 할당받고 해제하는 과정에서 아래 그림과 같이 부분 부분 빈 공간이 생기게 되는데, 4번같이 새로운 메모리를 할당할 때 알맞은 공간(4칸)이 없기 때문에 마지막(우측) 부분에 프로세스가 위치해야 되고, 그러면 빈 공간 메모리가 남아 낭비가 발생하게 된다.
이 현상이 계속되면 실제 physical 메모리가 커져 프로세스가 죽는 현상이 발생할 수도 있다.
또한 쓰기 연선이 copy on write 방식으로 동작하기 때문에 최대 메모리를 2배 이상까지 사용하기도 한다.
그래서 redis를 사용할 때 메모리를 적당히 여유 있게 사용하는 것이 좋다.
참고 자료 :
'백엔드 > 데이터베이스' 카테고리의 다른 글
[데이터베이스] Redis 캐시(Cache) 설계 전략 (0) | 2023.06.09 |
---|