1. Redis란
Redis는 오픈소스로 시스템 메모리를 사용하는 키-값 형태의 데이터 구조 저장소이다. In-Memory 상태에서 데이터를 처리하기 때문에 디스크에 접근하는 데이터베이스보다 빠르게 읽고 쓸 수 있다. 즉, 우리가 흔히 사용하는 관계형 데이터베이스(RDB), 문서형 데이터베이스(NoSQL) 보다 빠르고 가볍게 동작한다.
- Key-Value 구조이기 때문에 쿼리를 사용할 필요가 없다.(Key에 올 수 있는 자료형은 String, Value는 다양한 타입을 지원)
- 디스크가 아닌 메모리에서 데이터를 처리하기 때문에 빠른 속도로 처리 가능
- String, List, Set, Hash, Sorted set, Stream, Geospatial index, Bitmap, Bitfield 등의 자료구조 지원
- Single Thread로 한번에 하나의 명령만 처리 가능 - Race Condition 발생 X
- 트랜잭션 : Redis는 명령 그룹을 격리된 원자적 작업으로 실행할 수 있는 트랜잭션을 지원한다.
- 스냅샷 : Redis를 사용하면 특정 시점에 데이터를 디스크에 저장하여 보관할 수 있고, 장애 상황 시에 복구에 사용가능
- 복제 : Redis를 사용하면 Master-Slave 구조로 여러 개의 복제본을 만들 수 있다. 이를 통해 데이터베이스 읽기를 확장하고 높은 가용성(오랜 시간 고장 나지 않는) 클러스터를 가질 수 있다.
- Pub / Sub Messaging : Pub(발행) / Sub(구독) 방식의 메시징을 지원. 따라서 고성능 채팅방, 실시간 댓글 스트림, 소셜 미디어 피드 및 서버 상호 통신에 사용할 수 있음
- 루아 스크립트 지원 : 매우 경량화된 절차 스크립트 언어인 루아를 지원. 스크립트를 통해 성능을 높이고 응용 프로그램을 단순화하는 데 도움이 될 수 있다.
- 위치기반 데이터 타입 지원 : Redis에는 대규모 실시간 위치 기반 데이터 작업을 지원. 따라서 두 위치의 거리를 찾거나, 사이에 있는 요소 찾기 등의 작업을 수행할 수 있다. 이를 활용하여 맛집, 길 찾기, 지도기반의 고성능 서비스를 제공할 수 있다.
2. Redis의 데이터 구조
Redis는 Key-Value 구조이기 때문에 관계형 데이터베이스의 데이터타입인 VARCHAR, INT, DATETIME 등을 지원하지 않는다.
1. 문자열(String)[참고]
- 가장 일반적인 Key-Value 구조의 형태로 문자열을 저장
- 문자열에는 일반적인 텍스트 데이터뿐만 아니라 숫자, 날짜, 시간, 이진 데이터도 저장
- JPEG 이미지를 저장하거나, HTML fragment를 캐시 하는 용도로 자주 사용
- 주로 단순한 캐싱, 카운터, 설정 등에 사용(특히 카운팅)
- Key, Value의 최대길이는 512MB를 넘을 수 없음
함수 이름 | 명령어(bash 기준) | 설명 |
SET | SET key value | 주어진 key에 대해 값 설정 |
GET | GET key | 주어진 key에 대한 값을 얻음 |
MSET | MSET key value [key value ...] | 여러 개의 key-value 쌍을 한번에 설정 |
MGET | MGET key [key ...] | 여러 개의 key에 대한 값을 한번에 얻음 |
APPEND | APPEND key value | 주어진 key에 대해 값을 추가 |
STRLEN | STRLEN mykey | 주어진 key에 저장된 값의 길이를 반환 |
INCR | INCR key | 주어진 key에 저장된 값을 1씩 증가 |
DECR | DECR key | 주어진 key에 저장된 값을 1씩 감소 |
SETEX | SETEX key seconds value | 주어진 key에 대해 값을 설정하고, 만료 시간을 초 단위로 지정 |
SETNX | SETNX key value | 주어진 key가 존재하지 않을 때에만 값 설정 |
GETSET | GETSET key value | 주어진 key에 대한 현재 값을 얻은 후, 새로운 값을 설정 |
2. 리스트(List)
- Array 형식의 데이터 구조로 linked list의 특징을 가지고 있다.
- 연속된 데이터를 저장하는데 사용되며, 중복된 값을 허용
- 양방향으로 데이터를 리스트의 처음과 끝에 삽입, 삭제할 수 있지만, 중간의 값을 삭제하기에는 어려움이 있다.
- 중간의 값을 삭제하려면 삭제하려는 값의 인덱스를 찾고, 해당 인덱스를 제거하는 함수를 통해 제거한다.
- 리스트 안의 데이터는 문자열만 가능
- blocking 기능을 제공 - pub/sub 상황에서 list가 비어있을 때 pop을 시도하면 대개 NULL을 반환한다. 이 경우 소비자는 일정시간을 기다린 후 다시 pop을 시도한다.(polling) redis의 BRPOP을 사용하면 새로운 아이템이 리스트에 추가될 때에만 응답하므로 불필요한 polling 프로세스를 줄일 수 있다.
- 키가 이미 존재할 때만 값이 추가되는 LPUSHX / RPUSHX 함수를 통해 불필요한 키 생성을 막을 수 있다.
- list는 여러작업에 유용하지만, pub/sub 패턴에 많이 사용한다. 생산자가 아이템을 list에 넣으면 소비자가 받아서 액션 수행
- 주로 메시지 큐, 작업 큐에 사용
3. 해시(Hash)
- 필드와 값의 쌍을 저장하는데 사용(RDB의 테이블과 비슷하다.)
- 주로 사용자의 프로필, 객체 특성 등에 사용
- 해시는 전체를 가져오거나 개별 필드를 가져올 수 있음
4. 셋(Set)
- 중복없이 고유한 값을 저장하는 데 사용
- String의 집합. 여러 개의 값을 하나의 value에 넣을 수 있다.
- 교집합, 합집합, 차집합과 같은 집합 연산이 가능하다.
- 주로 태그, 관심사, 친구 목록 등에 사용
- Set은 정렬을 할 수 있다.
- (Sorted Set) 정렬을 통해서 특정 기준 값에 들어온 데이터만 필터링하는 게 가능하다.
5. 비트맵(Bitmap)
- 비트의 배열로 표현되는 데이터 구조로, 비트 연산을 통해 다양한 비트 기반의 작업을 수행할 수 있음
- 데이터 저장공간을 절약할 수 있음
- 카운팅 할 때 좋은 데이터 형태
- 주로 사용자 온라인 여부, 이벤트 발생 여부 등을 추적하는 데 사용
- SETBIT key offset value : 주어진 key의 비트열에서 특정 오프셋에 값을 설정. 값은 0 또는 1
- BITCOUNT key [start end] : 주어진 범위 내의 비트열에서 비트가 설정된 횟수를 반환
6. HyperLogLog[참고]
- 집합의 카디널리티(원소의 개수)를 근사화하기 위한 확률적인 데이터 구조
- 대용량 데이터를 카운팅 할 때 적절(오차 0.81%)
- 방대한 데이터 속에서 unique 한 값을 찾거나 카운팅 할 때 적절(중복을 허용하지 않는 고유한 원소의 개수를 근사치로 추정할 수 있음)
- set과 비슷하지만 저장되는 용량은 매우 작음(12KB 고정)
- 저장된 데이터는 다시 확인할 수 없음
7. Stream
- 로그항목, 메시징 시스템, 이벤트 소싱 등과 같이 이벤트나 메시지를 시간순으로 기록해야 하는 상황에서 사용
- append-only로 데이터 추가만 가능
- 가장 큰 특징은 소비자(Consumer) 그룹을 지정할 수 있다.
- 시간 범위로 검색, 신규 추가 데이터 수신, 소비자별 다른 데이터 수신 등 조건에 따라 특정 데이터만 읽을 수 있다.
- 공식 문서에는 Kafka를 대체해서 간단하게 사용할 수 있는 자료구조로 소개하고 있다.
3. Redis VS 데이터베이스
위에서도 언급하였듯 가장 큰 차이는 메모리 기반이냐!! 디스크 기반이냐!!이다. 이것은 데이터 처리속도의 차이로 이어진다. 데이터베이스에서 디스크에 저장되어 있는 10000개의 레코드를 읽는다고 가정했을 때 약 30초의 시간이 걸리는 반면, RAM에서는 0.0002초 밖에 걸리지 않는다. 하지만 메모리 비용적 측면도 고려해야 하므로 대규모 데이터를 다루는 경우는 적합하지 않으니 사용용도에 맞게 사용해야 한다.
Redis
- In-Memory영역에 저장하기 때문에 속도가 빠르다.
- In-Memory이기 때문에 서버 재시작 시 Redis의 모든 데이터가 유실될 수 있다.
- Redis는 In-Memory이지만 Persistent on-disk 데이터베이스이기도 하다. Redis는 특정한 때에 현재까지의 In-Memory의 데이터 상태를 disk에 저장해 두었다가 Redis를 다시 시작했을 때 disk에 저장해 두었던 dump 파일들을 로드하기 때문에 데이터의 손실 발생을 방지할 수도 있다.
- AOF 방식 : Append Only File의 약자로 데이터를 변경하는 커맨드가 들어오면 커맨드를 그대로 모두 저장(장애 상황 직전까지의 모든 데이터가 보장되어야 할 경우). 실시간 데이터 백업이 가능하고, 데이터 손실이 거의 없지만, 커맨드를 모두 기록하기 때문에 파일크기가 크다.
- RDB방식 : Redis Database Backup의 약자로 Snapshot 방식으로 동작. 특정 간격으로 저장당시의 메모리에 있는 데이터 그대로를 파일로 저장(백업은 필요하지만 어느 정도 데이터 손실이 발생해도 괜찮을 경우)
- 제일 강력한 내구성이 필요한 경우 AOF, RDB 둘 다 사용
데이터베이스(RDB, NoSQL)
- 디스크의 접근은 메모리의 접근보다 느리기 때문에 사용자가 많아질수록 부하가 많아져 느려질 수 있다.
- 디스크에 데이터를 저장하기 때문에 서버의 문제가 발생하더라도 데이터가 지워지지 않는다.
4. Redis를 캐시 서버로 활용하기
Redis는 캐시 서버로 많이 활용된다. 규모가 작거나, 사용자가 많지 않은 서비스의 경우에는 데이터베이스에 많은 부하가 가지 않는다. 하지만 사용자가 늘어난다면 데이터베이스에 접근이 많아지면서 부하가 올 수 있는데, 부하를 줄이기 위해 캐시 서버로 Redis를 사용한다. 따라서 같은 요청이 여러 번 발생하는 경우 매번 데이터베이스에 접근하지 않고, 캐시메모리에서 결과를 받아오기에 데이터 처리 속도가 빨라진다.
캐시 : 한번 읽어온 데이터를 메모리에 저장하여 다음에 읽을 때는 빠르게 결괏값을 받을 수 있도록 도와주는 공간
캐시 서버의 패턴(Caching Strategies)[참고]
1. Look-Aside cache(Lazy Loading)
- 클라이언트로부터 데이터 요청
- 웹 서버는 데이터가 존재하는지 Cache서버에서 먼저 확인
- cache 서버에 데이터가 있으면 데이터베이스를 조회하지 않고, cache 서버에 있는 결과값을 클라이언트에게 바로 반환(Cache hit)
- cache 서버에 데이터가 없으면 애플리케이션이 DB에 데이터를 조회하여 cache 서버에 저장하고 결과값을 클라이언트에게 반환(Cache Miss)
- 읽기 요청이 많은 경우에 적합.
- Redis에서 장애가 발생하더라도 전체 서비스의 장애로 이어지지 않고, 데이터베이스에서 데이터 조회가 가능하기 때문에 안전하다.
- Write-Around와 결합하여 사용.
- 이 경우에는 캐시서버와 DB의 데이터가 일치하지 않을 수 있는데, TTL(Time To Leave)을 사용하여 TTL이 만료되기 전까지는 변경되지 않은 캐시 데이터를 제공한다.
- 정합성(데이터들의 값이 서로 일치하는 상태)을 유지하기 위해서는 적절하지 않으므로 다른 전략을 사용해야 함
초반에는 Cache 서버에 데이터가 존재하지 않기 때문에 계속해서 cache miss가 발생한다. 이럴 때는 데이터베이스에서 캐시 서버로 데이터를 미리 넣어주는 작업을 하기도 하는데 이것을 Cache Warming이라고 한다.
2. Read-Through
- 클라이언트로부터 데이터 요청
- cache서버에 데이터가 없으면 데이터베이스에 요청
- 데이터를 캐시 서버에 저장
- 클라이언트에게 결과값 반환
- 데이터를 조회할 때 캐시 서버에서만 조회한다.
- Cache Miss 발생 시 DB에서 해당 데이터를 조회해 cache 서버에 바로 저장
- cache 서버의 장애는 전체 서비스의 장애로 이어짐
- Look-Aside와의 차이점은 Cache Miss시에 Look-Aside는 애플리케이션에서 DB에 요청하지만, Read-Through는 cache서버가 요청
- 그리고 Read-Through는 cache서버와 DB 간 데이터의 정합성이 항상 유지된다.
3. Write-Through
우선적으로 cache서버에 저장 후 데이터베이스에 저장
- 캐시는 항상 최신 상태의 정보를 가지고 있고, DB와 데이터 일관성을 보장함
- 저장할 때마다 2단계의 스텝을 거치기에 느리다.
- 재사용할지 알 수 없는 데이터들도 캐시서버에 넣기 때문에 리소스 낭비가 발생한다.
- 만료시간(TTL)을 두어 리소스 낭비를 막기도 함
4. Write-Around
데이터베이스에 모든 데이터를 저장하고, 읽은 데이터만 캐시에 저장
- Read시에 Cache Miss가 발생한 데이터만 cache 서버에 저장
- cache 서버의 리소스를 절약할 수 있음
- cache 서버와 DB 간의 데이터 일관성을 보장할 수 없음(이미 cache에 데이터가 존재할 경우)
- Look Aside, Read Through와 결합하여 사용
5. Write-Back
우선적으로 cache서버에 저장하고 있다가 특정 시점마다 데이터베이스에 저장
- 웹 서버는 모든 데이터를 Cache 서버에 저장
- Cache 서버에 특정시간 동안 데이터가 저장되어 있음
- Cache 서버에 있는 데이터를 데이터베이스에 저장 후 캐시 서버의 데이터는 삭제
- 쓰기가 많은 경우에 적합. 쿼리를 한 번씩 500번 날리는 것보다, 한 번에 500개의 쿼리를 날리는 것이 효율적
- 해당 방식은 우선적으로 메모리에 데이터가 저장이 되는데, 이때 서버에 문제가 발생한다면 데이터가 손실될 수 있다는 단점이 있음
5. In-Memory 데이터베이스인 Memcached VS Redis
Memcached와 Redis는 In-Memory 데이터 스토어로서 캐싱, 세션 스토어, 메시지 브로커 등 여러 용도로 사용되는 비슷한 툴이다. 하지만 이들 사이에도 몇 가지 중요한 차이점들이 존재한다. 우선 공통점 먼저 살펴보겠다.
공통점
- 인 메모리 데이터 스토어로써 1ms 이하의 지연시간을 가진다.
- 데이터는 각각 key-value 형태로 저장되며, 각 키는 고유한 식별자를 가지고 있다.
- 캐싱 및 세션 스토어로 활용
- 분산된 환경에서 사용할 수 있도록 데이터 파티셔닝 지원
차이점
Memcached | Redis | |
다양한 자료구조 지원 | O | |
멀티 쓰레드 | O | |
스냅샷(Snapshots) | O | |
데이터복제(Replication) | O | |
트랜잭션 지원 | O | |
pub/sub | O | |
루아 스크립트 지원 | O | |
위치기반 데이터 타입 지원 | O |
- Memcached는 단순한 key-value 형태의 데이터만 저장 가능한 반면에, Redis는 다양한 자료구조를 지원한다.
- Memcached는 메모리에만 데이터를 저장하고, 재시작하면 데이터가 사라진다. 반면 Redis는 옵션을 통해 디스크에 데이터를 저장하고 지속성을 확보할 수 있다.
6. Redis를 어떻게 사용하는 것이 좋을까?
Redis를 사용하기 위해서 억지로 넣는 것은 좋은 방법이 아니다. 운영 중인 웹 서버에서 key-value 형태의 데이터 타입을 처리해야 하거나, 디스크 I/O가 빈번히 발생해 다른 저장방식을 사용하면 효율이 떨어지는 경우에 사용한다.(데이터베이스에 지속적으로 접근해야 하는 경우)
우선 Redis의 목적을 정하자. 캐시로 활용할 것인지 데이터 저장소로 활용할 것인지를 결정해보자.
- 데이터 저장소 : 백업기능을 지원하기 때문에 데이터 저장소로 활용할 수 있다. 하지만 데이터의 용량이 큰 경우는 적절하지 않다.
- 데이터 캐시 : 데이터베이스에서 자주 사용되는 데이터를 캐시 하여서 빠르게 읽을 수 있도록 한다.
- 분산 환경에서 사용 : 캐싱, 세션스토어, 메시징 및 이벤트 기반 아키텍처에서의 활용, 분산 락, 클러스터링, 트랜잭션 등의 기능을 할 수 있기 때문에 많이 활용한다.
- 순위계산 : Sorted Set을 활용하여 순위 계산 용도로 활용
- 카운팅 기록(조회수 등)
- 사용자 세션관리, 인증 토큰 등
- API 캐싱[참고] - 동일한 요청에 대해서는 캐싱된 데이터를 반환
- 잡큐(List)
7. Redis 데이터 삭제하기
Redis는 In-Memory이면서 Persistent on-disk 데이터베이스이기도 하다. 즉, 사용자의 특별한 설정이 없으면 영구적으로 저장이 된다. 그렇게 되면 매번 서버를 재실행할 때마다 메모리에 Redis의 데이터가 올라오게 되는데 그럼 영원히 키가 쌓일 것이다. 이는 메모리 측면에서 좋지 않다. 사용하지 않는 키값은 제거하고, 다시 새로운 키를 생성해야 효율적일 것이다.
1. 일괄 삭제
FLUSHDB(해당 DB의 모든 키 삭제), FLUSHALL(모든 DB의 키 삭제) 명령어로 모든 키를 삭제한다. 복구 불가능하며 보통 개발 수준에서 사용한다.
2. ExpireTime으로 기간 만료 후 삭제
가장 많이 사용되는 방법으로 키를 추적할 필요 없이 쉽게 관리할 수 있다. 해당 데이터를 입력할 때 이 데이터의 사용기한이 언제까지인지를 직접 설정해 줌으로써 애플리케이션이 직접 데이터의 사용 만료 시간을 정할 수 있다.
참고 사이트
레디스Redis가 뭐에요? 레디스 설치하기, 레디스 튜토리얼
서문. 레디스가 뭔가요?
sihyung92.oopy.io
레디스(Redis)는 언제 어떻게 사용하는 게 좋을까
레디스를 사용해 본 적 없는 백엔드, 데이터베이스 개발자를 위해 | 레디스는 시스템 메모리를 사용하는 키-값 데이터 스토어입니다. 인메모리 상태에서 데이터를 처리함으로써 흔히 사용하는
brunch.co.kr
개발자를 위한 레디스 튜토리얼 01 : NHN Cloud Meetup
레디스는 오픈소스이고, 다양한 서비스에서 레디스를 자유롭게 사용하고 있습니다. 위의 사진에서 볼 수 있듯이 Airbnb, Uber, Instagram도 레디스를 사용하고 있네요. 핑크다이어리, 토스트파일, 두
meetup.nhncloud.com
캐시(Cache) 알아보기
목차 캐시(Cache) 란? 캐시(Cache)는 자주 사용하는 데이터나 값을 미리 복사해 놓는 임시 저장소입니다. 속도 향상을 위해 캐시를 사용하며, 다음과 같은 상황에서 캐시를 주로 사용합니다. 원본 데
yoongrammer.tistory.com