들어가기
항상 프로젝트를 진행할 때면 서버를 정하고 데이터베이스를 연결하고, 외부 API 요청 시 DB로부터 데이터를 가져오는 과정을 밟아 왔다. 데이터베이스로는 mysql을 주로 사용해 왔다. mysql을 사용하는 것만큼 익숙한 것이 캐시라는 것인데 캐시 하면 항상 거론되는 것 중 하나가 redis이다. 데이터베이스는 서버에서 디스크에 접근하여 데이터를 가져오는 것이지만, 캐시는 디스크에 데이터들을 메모리로 옮긴 후, 서버에서 디스크에 접근하는 것 대신에, 메모리 공간에 위치한 캐시에서 데이터를 가져오기 때문에 성능 향상을 이룰 수 있는 방법 중 하나이다.
이 글에서는 이러한 redis에 대해 기본적인 내용들을 작성할 예정이다.
Redis란 무엇일까?
1. NoSql 로서의 Redis
Redis는 2009년 '살바토르 산필리포'가 개발하였는데 위키백과에 의하면 Redis는 Remote Dictionary Server의 약자로 '키-값' 구조의 비정형 데이터를 저장하고 관리하기 위한 오픈 소스 기반의 비관계형 데이터베이스 관리 시스템 (DBMS: Database Management System)이다.
데이터를 메모리로 불러와서 처리하는 메모리 기반 DBMS이며 여기서 볼 키워드들은 '키-값 구조의 비정형 데이터', '비관계형 데이터베이스'이다.
비정형 데이터는 정의된 구조가 없이 정형화되지 않은 데이터라는 의미이며 이는 비관계형 데이터베이스와 관련이 있다.
일반적으로 우리가 데이터베이스로 사용하는 것은 mysql, oracle 등 관계형 데이터베이스다. 이러한 관계형 데이터베이스의 특징으로는 하나 이상의 열과 행으로 구성되는 테이블 형태의 데이터 구조로 여러 속성, 관계, 튜플이 있고 이러한 테이블끼리도 서로 연결되어 연관되어 있는 것이다.
데이터베이스에서 데이터를 조회, 수정, 추가, 삭제하기 위해서는 sql이라는 명령어를 사용하여 접근을 할 수 있다.
이러한 관계형 데이터베이스는 데이터의 무결성 유지에 용이하고, 정규화를 통해 중복되는 값을 제거할 수 있으며 구조가 정해져 있기에 sql을 통해 복잡한 질의를 수행 가능하고 어떤 정보를 찾을 수 있다는 장점이 있다.
하지만 고정된 스키마(형식, 구조)가 정의되어 있고 테이블과 테이블끼리의 join으로 인한 성능 문제가 생길 수 있다. 그리고 데이터들은 연관관계라는 복잡한 관계를 맺고 있기 때문에 데이터 베이스의 수평적 확장(스케일 아웃) 이 힘들다. 데이터베이스 서버를 확장하는 방식으로는 스케일 업, 스케일 아웃이 있다.
스케일 업은 수직적 확장이라고도 하며 데이터 베이스 서버의 크기 자체를 확장하는 것이다.
스케일 아웃은 수평적 확장이라고도 하며 데이터베이스 서버의 개수를 늘려서 확장하는 것이다.
하지만 관계형 데이터베이스는 데이터들끼리 관계가 이어져 있기에 서버의 개수를 늘리면 하나의 서버에서 다른 서버로 데이터가 연관관계를 맞기가 힘들어 스케일 아웃이 힘든 것이며 가장 큰 문제라고도 할 수 있다.
이러한 문제들을 해결하기 위해 등장한 것이 비관계형 데이터베이스이다.
그렇다면 비관계형 데이터베이스는 어떤 것인가?
말 그대로 관계형 데이터베이스가 아닌 것을 의미한다. 테이블 형태가 아니고 sql이 존재하지 않는, 그러한 데이터베이스들을 모두 비관계형 데이터베이스 혹은, NoSql 데이터베이스라고 한다.
NoSql은 'Not Only' Sql 혹은 'No' Sql을 뜻하며 말 그대로 sql을 사용하지 않는다는 의미이다.
이러한 비관계형 데이터베이스는 Sql을 사용하지 않고 여러 종류의 모델을 가지고 있는데 아래는 각 모델과 모델을 사용하는 DB를 나타낸다.
- Key-value 모델: Redis, Memcached, Riak, DynamoDB
- Document 모델: MongoDB, CouchDB
- Wide-column 모델: Cassandra, HBase, Google Big Table
- Graph 모델: Neo4j, OrientDb, AgensGraph
위에서 보는 바와 같이 Redis는 Key-value 모델을 사용하는 NoSql이라는 것을 알 수 있다.
key-value는 위에서 언급한 '키-값'을 의미하고 정해진 key마다 value를 가지고 있는 것이다.
즉, value를 가리키는 특정 key를 통해서 데이터에 해당하는 value를 조회할 수 있는 형태이다.
Java에서의 Map과 동일한 구조라고 생각하면 된다. Redis는 이러한 Key -Value 구조를 사용하여 여러 장단점들을 가진다.
우선 Key-Value는 가장 단순한 데이터의 저장방식이며, 단순한 만큼 빠르고 사용하기 쉽다는 장점이 있다. 이는 복잡한 질의를 요구하는 Sql과 대비되는 점이라 할 수 있다.
하지만 구조가 따로 없고 오직 key를 통해서만 데이터를 조회할 수 있기 때문에 복잡한 검색을 할 수 없다는 단점이 있다.
2. In-Memory DB로서의 Redis
지금까지는 NoSql에 대해서 알아보았고, 이제는 Nosql 중 하나인 Redis의 특징을 집중적으로 살펴보겠다.
Redis는 In-Memory DB라고도 불리는데 그 이유는 다른 DBMS처럼 디스크에 데이터를 저장하는 것이 아니라 메모리에 저장하기 때문이다. 일반적으로 디스크에 접근하는 비용보다는 메모리에 접근하는 비용이 더 적기 때문에 Redis에서 데이터를 조회하는 것이 성능적으로 더 이득이다.
하지만 메모리에 넣었을 경우 바로 떠오르는 문제가 하나 있다. 바로 '휘발성'이다. 일반적인 데이터베이스 서버는 껐다가 다시 켜도 데이터가 디스크에 저장되어 있기 때문에 다시 확인할 수 있다. 하지만 레디스 서버가 꺼졌다가 켜지면 메모리 특성상 데이터가 전부 날아가는 휘발성을 가지고 있기 때문에 저장해 놓은 데이터 또한 날아갈 것이다.
물론 레디스에는 다시 메모리에 데이터를 복구할 수 백업 및 복제 기능이 구현되어 있기에 레디스 서버가 꺼진다고 정말로 전부 날아가는 문제는 생기지 않느다.
일반적인 DBMS들은 디스크에 데이터를 저장하여 영속성을 제공한다. redis는 디스크에 저장을 하지 않기에 영속성을 제공하지 않는다. 그렇다면 이러한 redis를 DBMS라고 할 수 있을까? 하는 의문이 문득 들었다.
하지만 이는 내 착각이었고 데이터베이스의 개념 자체는 영속성이 있냐 없냐로 나누는 것이 아니기 때문에 이상할 것은 없었다. 데이터베이스 관리 시스템(DBMS)은 데이터베이스에 적재된 데이터 작업을 수행할 뿐만 아니라 데이터베이스를 보호하고 보안을 제공하는 것이다.
redis는 데이터 작업을 수행하는 인터페이스를 제공하기 때문에 DBMS라고 할 수 있다.
아무튼 redis는 메모리에 데이터를 저장하기 때문에 더 빠른 조회가 가능하다. 이를 활용한 것이 세션(Session)과 캐시(cache)로서 redis를 사용하는 것이다.
세션(session)이란 웹 사이트의 여러 페이지에 걸쳐 사용되는 사용자 정보를 저장하는 방법을 의미한다. 통신간 두 장치가 연결되어 있는 일정 시간 동안 유지되는 정보이다. 로그인 정보를 저장할 때도 사용되는데, 사용자가 로그인을 한 다음에 서버에 개인 정보를 요청할 때, 서버는 사용자가 어느 계정으로 로그인 했는지를 확인할 수 있어야 한다. 사용자 브라우저는 자신이 로그인 했음을 증명하는 정보를 Cookie에 담아 저장하고, 서버는 해당 Cookie에 연관된 세션 정보를 세션 스토리지(session storage)에 저장해놓는다. 사용자가 요청 시 Cookie를 헤더에 같이 요청하여 서버는 사용자의 로그인 정보를 확인하여 사용자 정보를 응답할 수 있는 것이다.
결국 유저가 로그아웃을 하거나 세션이 만료될 때까지 유지되어 사용자마다 개인적인 서비스 이용이 가능하게 된다.
아래는 사용자가 로그인을 하고 사용자 정보를 요청하는 시퀀스 다이어그램을 나타낸다.
하지만 여기엔 문제가 있다. 만약 서버가 여러대라면 어떻게 될까? 특정 서버의 session storage에 저장한 정보는 다른 서버에서는 이를 확인할 수가 없으므로 사용자 요청에 로그인 검증을 하지를 못한다. 이때 redis가 있다면 문제를 해결할 수 있다. session storage를 redis로 하는 것이다. 그렇게 하면 여러 서버를 운용한다고 해도 session storage는 redis라는 공통의 데이터 저장소이므로 어떤 서버든지 사용자 요청에 의해 사용자 정보를 전달할 수가 있는 것이다.
캐시(cache)란 데이터를 미리 복사해 놓은 임시 저장소를 의미한다. 사실, 캐시는 꽤나 자주 사용되는 용어이다. 당장에 컴퓨터 저장장치 계층구조만 봐도 캐시라는 용어가 나온다.
이러한 캐시는 성능향상에 큰 이점이 있는데 바로 DB접근을 줄일 수 있는 것이다. 위에서 잠깐 언급을 했었는데 redis는 인메모리 DB로 디스크가 아닌 메모리에 데이터를 저장한다. 이러한 redis에 특징을 사용하여 캐시 공간으로서 사용할 수가 있다. 우선 사용자의 첫 요청 시에는 DB에 접근하여 데이터를 가져온다. 이때, DB에서 가져온 데이터를 redis에 저장을 해놓는다. 만약 똑같은 요청이 더 온다면 DB에 접근할 필요 없이 redis에서 데이터를 가져갈 수가 있다. DB에 접근을 하지 않는 것이니 필히 성능향상이 있을 것이다. 혹은, 처음부터 서버를 구동 시 DB로부터 데이터를 미리 redis로 옮겨 놓을 수도 있다. 이런 식으로 redis를 캐시 공간으로서 활용할 수도 있다.
3. Middleware로서의 Redis
Middleware란 응용 소프트웨어가 운영체제로부터 제공받는 서비스 이외에 추가적으로 이용할 수 있는 서비스를 제공하는 컴퓨터 소프트웨어를 의미한다. 애플리케이션이 이용할 수 있는 유용한 기능을 제공하는 소프트웨어라고 할 수 있는데 redis가 어떻게 Middleware로서의 기능을 할 수 있을까?
Redis는 데이터를 String, Lists, Sets, Hashes, SortedSets, Bitmaps, HyperLogLogs 등 다양한 자료구조의 형태로 저장이 된다. 물론 이러한 자료구조들은 모두 Key-value의 형태를 가지고 있다.
이렇듯 다양한 자료구조를 제공하고 있는데 이러한 자료구조가 어떻게 Middleware로서 사용되는 것일까?
한번 SortedSet을 봐보자. SortedSet은 Set과 유사하게 유니크한 값들의 집합이다. Set에서 추가로 자동 정렬이 이루어진다는 특징이 있다. 이러한 자동 정렬된 집합으로부터 첫 번째 값, 혹은 마지막 값, 특정 값의 위치, 범위를 쉽게 가져올 수가 있다. 이는 관계형 DB와 비교했을 때 빠른 업데이트, 조회 등 성능 향상을 이룰 수가 있는데 특히 순위 데이터가 필요할 때 빛을 발한다.
아래는 DB에서 1위부터 10위까지 score 순서로 ranking 테이블에서 id 값을 가져오는 예시 쿼리이다.
SELECT id FROM member_ranking ORDER BY score DESC LIMIT 0, 10;
order by, limit, count() 등의 집계함수를 사용할 수 있고 데이터가 늘어날수록 속도는 늦어질 것이다.
이때 데이터를 redis에 sortedSet 구조로 넣어둔다면 어떨까? 데이터가 추가될 때마다 redis에 추가하면 sortedSet이므로 자동 정렬이 될 것이고 1순위, 마지막 순위, 특정 순위, 범위 등 조회하고 싶은 부분을 훨씬 빠르게 조회할 수가 있다. 더욱이 redis는 in-memory db이지 않는가?!
db를 조회하는 것보다 memory를 조회하여 더 빠른 속도로 사용자에게 응답을 해줄 수가 있다.
결론
간단하게 redis에 대해서 알아보았다. 공부하기 전에는 'redis는 캐시로 사용한다'라고만 인지하고 있었지만 하나하나 살펴보니 다양한 활용성을 가지고 있었다.
정리하자면
1. 아주 빠른 데이터 저장소로 활용: in memory(캐시, 세션 등의 저장소)
2. 분산된 서버들 간의 커뮤니케이션(session을 통한 동기화, 작업 분할 등)
3. 내장된 자료구조를 활용한 기능 구현(ex. sorted set을 통한 랭킹 시스템)
4. 분산 환경에서의 수평적 확장성(noSql로서 스케일 아웃을 통한 높은 확장성)
이렇게 정리할 수가 있다.
다음 글에서는 redis를 통해 캐시를 프로젝트에 적용되는 과정을 담을 것이다.
궁금한 점은 언제든 댓글로 달아주길 바랍니다.
'redis' 카테고리의 다른 글
Springboot With Redis: 데이터 Dto 저장, 조회 시 직렬화 문제 및 Enum을 활용한 갱신 (1) | 2023.10.14 |
---|---|
Springboot: redis를 통해 캐시 기능 간단 적용 (0) | 2023.10.02 |