레디스를 사용해 본 적 없는 백엔드, 데이터베이스 개발자를 위해
레디스는 시스템 메모리를 사용하는 키-값 데이터 스토어입니다. 인메모리 상태에서 데이터를 처리함으로써 흔히 사용하는 관계형 데이터베이스(RDS) 그리고 몽고 DB로 대표되는 문서형(Document) 데이터베이스보다도 빠르고 가볍게 동작합니다.
레디스를 사용할 때 개발자는 크게 2가지 방법을 사용합니다.
첫째는 로컬 환경에서 레디스를 호출합니다. AWS EC2를 예로 들자면 인스턴스에 레디스를 설치해 인스턴스 메모리를 사용해 레디스를 사용하는 방법입니다. 인스턴스의 메모리 여유가 있다면 비용적인 측면으로나 사용성 측면으로나 뛰어납니다.
두 번째는 클라우드 서비스를 사용해 외부 자원을 사용합니다. 레디스 랩(https://redislabs.com/) 등의 서드파티 서비스를 사용해 레디스를 사용하게 되면 통신하는 웹 서버가 아무리 많아도 하나의 프레임워크 바인딩을 사용할 수 있습니다. 이 경우에 레디스는 여러 웹 서버들의 공유 메모리 역할을 감당할 수 있습니다.
레디스와 백엔드 프레임워크와의 궁합은 전반적으로 괜찮습니다. 대부분의 백엔드 프레임워크를 지원하고, 모듈을 사용해 적절한 형태로 구현할 수 있어 개발 난이도는 비교적 낮습니다.
레디스를 사용하기 위해 억지로 넣는 건 좋은 접근이 아닙니다. 운영 중인 웹 서버에서 키-값 형태의 데이터 타입을 처리해야 하고, I/O가 빈번히 발생해 다른 저장 방식을 사용하면 효율이 떨어지는 경우에 사용합니다.
우리가 처리하는 데이터 중 I/O가 가장 빈번한 데이터는 아무래도 조회수와 같은 카운트 형태의 데이터일 겁니다. 유튜브를 생각해보면 인기 채널의 신규 동영상을 1시간도 안돼서 100만 조회수를 넘기는 경우도 있습니다. 이때 조회수에 해당하는 데이터를 RDS 형태의 데이터에 저장해 I/O를 반복한다면 엄청난 자원이 사용될 것이란 건 불 보듯 뻔한 일입니다.
이처럼 어마어마한 I/O를 발생시키는 데이터를 처리할 때 레디스를 사용해 데이터를 캐싱 처리하고, 일정한 주기에 따라 RDS에 업데이트를 한다면 RDS에 가해지는 부담을 크게 줄이고, 성능은 크게 향상할 수 있습니다.
물론 이 과정을 단순히 레디스만 사용해 처리한다는 건 어렵습니다. 백엔드에서 연산 처리를 해줘야 할 것이고, 적당한 스케쥴러 코드를 만들어 일정 주기나 컨디션에 따라 레디스와 RDS 간 데이터 업데이트를 해줘야 합니다.
많은 I/O를 예시로 들었지만 가장 많이 사용되는 건 역시 사용자의 세션 관리입니다. 사용자의 세션을 유지하고, 불러오고, 여러 활동들을 추적하는 게 매우 효과적으로 사용할 수 있습니다. 또한 매우 빠르게 동작한다는 점을 사용해 메시지 큐잉에도 사용할 수 있습니다.
그 밖에도 강력하게 사용할 수 있는게 바로 API 캐싱입니다. 라우트로 들어온 요청에 대해 요청 값을 캐싱해면 동일 요청에 대해 캐싱된 데이터를 리턴하는 방식입니다. 이와 관련된 쉬운 링크가 있어 아래에 첨부합니다.
한 가지 초보자 분들이 헷갈리시는 부분이 있는데 레디스는 관계형 데이터베이스가 아닙니다. 레디스는 RDS와 달리 데이터 타입(VARCHAR, INT, DATETIME) 등을 지원하지 않습니다. 오직 몇 가지 로우 레벨의 데이터 타입을 지원합니다.
거의 대부분의 데이터를 문자열로 표현합니다. 가령 숫자나 날짜 및 시간 등을 문자열로 저장한다고 생각하시면 됩니다.
해시는 필드를 가질 수 있습니다. 예를 들어 사용자 정보라는 해시가 있다면 이메일과 닉네임을 가질 수 있습니다. 해시는 전체를 가져오거나 개별 필드를 가져올 수 있습니다.
리스트는 여러분들이 다 아시는 연결 리스트(Linked list)입니다. 배열의 왼쪽, 오른쪽에 엘리먼트를 추가할 수 있습니다. 리스트 안의 데이터는 문자열만 가능합니다.
셋은 리스트와 유사한 특징을 보이지만 고유 값(Distinct value)을 저장한다는 점에서 차이점이 있습니다. 고유 값이 이미 정해진 셋에는 고유 값에 해당하는 멤버를 생성할 수 없습니다. 또한 셋은 정렬을 할 수 있습니다.(Sorted Set) 정렬을 통해서 특정 기준 값에 들어온 데이터만 필터링하는 게 가능합니다.
앞서 "레디스는 데이터베이스가 아니다!"라고 말했는데 스키마를 말하니 당혹스러울 수도 있습니다. 레디스의 스키마는 데이터를 정규화하고, 데이터의 로우에 대해 일관된 레퍼런스를 가지게 해 줄 수 있게 해주는 용도로 존재합니다. 이는 데이터 입출력에 큰 도움을 줍니다.
그렇다면 스키마는 어떤 구조로 작동될까요? 우리가 흔히 사용하는 몽고 DB의 스키마를 생각하면 오산입니다. 다시 말하지만 레디스는 키-값 스토리지입니다.
예를 들어 사용자의 이메일, 닉네임, 최근 로그인 시간을 레디스에 저장한다고 하겠습니다.
user:userId:email:문자열
user:userId:nickname:문자열
user:userId:lastLogin:문자열
우리가 알던 스키마의 모습과는 다르지만 개발자라면 어떤 느낌인지 감이 올 것입니다. 데이터를 :로 구분해 접근이 가능한 걸 유추할 수 있습니다. 이렇게 스키마를 활용하면 사용자 아이디에 해당하는 userId만 알아도 연결된 데이터인 email, nickname, lastLogin도 알 수 있게 됩니다.
레디스 스키마 사용법을 보면 알겠지만 레디스는 하나의 키-값 데이터에 여러 키가 존재하는 것처럼 보입니다. 이를 본 개발자라면 키 안의 키 역할을 하는 스키마 필드에 대해 이런 걱정이 들 수 있습니다.
스키마를 깊고, 넓게 만들게 되면 성능에 악영향을 주지 않을까?
다행인 점은 레디스의 스키마는 RDS의 로우의 숫자와 비슷하게 동작하지 않습니다. 즉 성능에 영향을 주지 않습니다. RDS의 경우 탐색해야 하는 로우가 많아짐에 따라 검색 속도가 느려지다 보니 인덱스 로우, 프리머리 키(Primary Key), 포린 키(Foreign Key) 등을 설정해 사용한다. 반면 레디스는 O(1)의 수행 시간을 가지고, 많은 키(100,000,000,000개) 건 적은 키(1개) 건 스키마가 많건 적건 동일한 시간이 사용됩니다.
레디스의 강점이 뚜렷한 만큼 주의해야 할 점도 분명하다. 레디스는 한 번 생성한 키를 선택적으로 삭제하기 어렵습니다. "엥 이게 무슨 말이오?" 싶을 수 있습니다. 그럼 키가 영원히 쌓이는 방식이란 말인가 물으실 수 있습니다. 놀랍게도 그렇습니다. 특별한 조치를 하지 않으면 레디스의 키는 삭제가 아닌 보관되며 심지어 서버를 종료했다가 다시 켜도 상태가 유지됩니다.
레디스 키를 삭제하는 방법은 크게 3가지입니다.
FLUSHDB 명령어를 통해 모든 키를 파괴합니다. 복구가 불가능하며 보통 개발 수준에서 사용합니다.
각각의 키를 저장할 때 셋에 저장해 특정 시간이나 조건에 따라 삭제하는 방법입니다. 이 방법은 실제 삭제를 사용한다기 보단 밀어내기를 한다고 이해할 수도 있습니다. 셋은 하나의 키에 대해 여러 번 등록을 해도 하나의 데이터만 남게 됩니다. 이러한 특징과 더불어 셋은 교집합, 차집합, 합집합 같은 기능을 제공합니다. 두 가지 특징을 활용해 교차되는 키만 남기거나 뺀 연산을 적용해 키를 삭제할 수 있습니다.
가장 많이 사용되는 방법입니다. 키를 추적할 필요도 없고, 쉽게 관리할 수 있습니다. 이 방식을 사용할 때는 키-값을 SET 커맨드로 저장해 EXPIRE 커맨드로 기간 만료 시간을 정하는 방법을 사용합니다. 또는 레디스 버전 2.0.0 이상을 사용할 때 SETEX 커맨드를 사용하는 방법입니다. SETEX 방법은 SET과 EXPIRE를 합친 것과 같다고 보시면 됩니다.
웹 개발, 블록체인 컨트렉트 개발 문의: https://flexweb.io
개인 유튜브: https://youtube.com/@josehp_han