Redis는 'Remote Dictionary Server'의 약자로, 분산 환경에서 여러 서버가 공유할 수 있는 인메모리 데이터 저장소입니다. 주요 특징은 다음과 같습니다.
Redis는 오랫동안 오픈 소스 프로젝트로 알려져 왔지만, 2024년 3월부터 라이선스가 변경되었습니다. BSD에서 SSPL(Server Side Public License) + RSAL(Redis Source Available License) 조합으로 변경되었습니다.
이로 인해 Redis의 '오픈 소스' 지위에 대한 논쟁이 있죠!
다음으로 레디스의 특징을 알아봅시당
레디스는 아래와 같은 특징을 가지고 있습니다.
그럼, 레디스를 왜 쓸까요?
레디스는 아래와 같은 장점을 가지고 있습니다.
이제 레디스의 자료 구조에 대해 알아봅시땅 💪🏼💪🏼
참고 자료: Understand Redis data types
가장 기본적인 데이터 타입으로, 문자열, 숫자, 직렬화된 객체(JSON string) 등 저장하는 자료 구조입니다.
> SET bike:1 Deimos
OK
> GET bike:1
"Deimos"
String의 연결 리스트(Linked List)입니다. O(1)의 상수 시간 복잡도로 양쪽 끝에서 푸시/팝 연산이 가능하며, 큐나 스택으로 사용할 수 있습니다.
큐, 스택 구현에 유용하겠죵? 😉
명령어
LPUSH
: 리스트의 머리(앞쪽)에 새로운 요소를 추가합니다. RPUSH
: 꼬리(뒤쪽)에 추가합니다.LPOP
: 리스트의 머리에서 요소를 제거하고 반환합니다.RPOP
: 동일한 작업을 리스트의 꼬리에서 수행합니다.LLEN
: 리스트의 길이를 반환합니다.LMOVE
: 원자적으로(atomic) 한 리스트에서 다른 리스트로 요소를 이동시킵니다.LRANGE
: 리스트에서 지정된 범위의 요소들을 추출합니다.LTRIM
: 리스트를 지정된 범위의 요소들로 줄입니다.순서가 없는 고유한 문자열(Unique String)의 집합입니다.
합집합, 교집합, 차집합 등의 집합 연산을 지원합니다!
명령어
SADD
집합에 새로운 멤버를 추가합니다.SREM
: 집합에서 지정된 멤버를 제거합니다.SISMEMBER
: 특정 문자열이 집합의 멤버인지 테스트합니다.SINTER
: 두 개 이상의 집합에 공통으로 존재하는 멤버들의 집합(즉, 교집합)을 반환합니다.SCARD
: 집합의 크기(일명 카디널리티)를 반환합니다.필드-값 구조를 갖는 데이터 타입으로, 다양한 속성을 갖는 객체 데이터를 저장하는 데 적합합니다.
명령어
HSET
: 해시(hash)에서 하나 이상의 필드에 값을 설정합니다.HGET
: 주어진 필드의 값을 반환합니다.HMGET
: 하나 이상의 주어진 필드들의 값을 반환합니다.HINCRBY
: 주어진 필드의 값을 제공된 정수만큼 증가시킵니다.사용 예시
> HSET bike:1 model Deimos brand Ergonom type 'Enduro bikes' price 4972
(integer) 4
> HGET bike:1 model
"Deimos"
> HGET bike:1 price
"4972"
> HGETALL bike:1
1) "model"
2) "Deimos"
3) "brand"
4) "Ergonom"
5) "type"
6) "Enduro bikes"
7) "price"
8) "4972"
> HMGET bike:1 model price no-such-field
1) "Deimos"
2) "4972"
3) (nil)
> HINCRBY bike:1 price 100
(integer) 5072
> HINCRBY bike:1 price -100
(integer) 4972
Set과 유사하지만, 각 멤버에 점수(score)가 연결되어 있어 정렬된 상태를 유지합니다.
명령어
ZADD
: 정렬된 집합(sorted set)에 새로운 멤버와 연관된 점수를 추가합니다. 만약 멤버가 이미 존재한다면, 점수가 업데이트됩니다.ZRANGE
: 주어진 범위 내에서 정렬된 멤버들을 반환합니다.ZRANK
: 제공된 멤버의 순위를 반환합니다. 이때 정렬된 집합이 오름차순으로 정렬되어 있다고 가정합니다.ZREVRANK
: 제공된 멤버의 순위를 반환합니다. 이때 정렬된 집합이 내림차순으로 정렬되어 있다고 가정합니다.사용 예시
> ZADD racer_scores 10 "Norem"
(integer) 1
> ZADD racer_scores 12 "Castilla"
(integer) 1
> ZADD racer_scores 8 "Sam-Bodden" 10 "Royce" 6 "Ford" 14 "Prickett"
(integer) 4
> ZRANGE racer_scores 0 -1
1) "Ford"
2) "Sam-Bodden"
3) "Norem"
4) "Royce"
5) "Castilla"
6) "Prickett"
> ZREVRANGE racer_scores 0 -1
1) "Prickett"
2) "Castilla"
3) "Royce"
4) "Norem"
5) "Sam-Bodden"
6) "Ford"
> ZRANGE racer_scores 0 -1 withscores
1) "Ford"
2) "6"
3) "Sam-Bodden"
4) "8"
5) "Norem"
6) "10"
7) "Royce"
8) "10"
9) "Castilla"
10) "12"
11) "Prickett"
12) "14"
로그나 시계열 데이터를 저장하는 데 최적화된 데이터 타입입니다. 고유한 ID로 각 항목을 식별합니다.
명령어
XADD
: 스트림에 새로운 항목을 추가합니다.XREAD
: 주어진 위치에서 시작하여 시간순으로 전진하면서 하나 이상의 항목을 읽습니다.XRANGE
: 제공된 두 항목 ID 사이의 범위에 있는 항목들을 반환합니다.XLEN
: 스트림의 길이(항목의 개수)를 반환합니다.사용 예시
# 1. 'race:france': 'race:france' 스트림에 새 항목을 추가
# 2. '*': Redis가 자동으로 ID를 생성하도록 지시
# 3. 추가된 데이터(첫줄의 경우)
# - rider (선수): Castilla
# - speed (속도): 30.2
# - position (위치): 1
# - location_id (위치 ID): 1
# 4. 반환된 "1692632086370-0": 자동 생성된 항목의 ID
> XADD race:france * rider Castilla speed 30.2 position 1 location_id 1
"1692632086370-0"
> XADD race:france * rider Norem speed 28.8 position 3 location_id 1
"1692632094485-0"
> XADD race:france * rider Prickett speed 29.7 position 2 location_id 1
"1692632102976-0"
# `XRANGE`: 특정 범위의 스트림 항목들을 반환하는 명령어
# `race:france`: 조회할 스트림의 이름
# `1692632086370-0`: 조회를 시작할 ID
# `+`: 스트림의 마지막까지를 의미하는 인자
# `COUNT 2`: 최대 2개의 항목만 반환하도록 제한
> XRANGE race:france 1692632086370-0 + COUNT 2
1) 1) "1692632086370-0"
2) 1) "rider"
2) "Castilla"
3) "speed"
4) "30.2"
5) "position"
6) "1"
7) "location_id"
8) "1"
2) 1) "1692632094485-0"
2) 1) "rider"
2) "Norem"
3) "speed"
4) "28.8"
5) "position"
6) "3"
7) "location_id"
8) "1"
위도와 경도 정보를 저장하고 검색할 수 있는 데이터 타입입니다.
거리 계산, 범위 탐색 등을 지원하는 타입이죠!
명령어
GEOADD
: 주어진 지리공간 인덕스에 위치를 추가합니다. (이 명령어에서는 경도가 위도보다 먼저 옵니다.)GEOSEARCH
: 주어진 반경 내 또는 경계 상자(bounding box) 내의 위치들을 반환합니다.사용 예시
> GEOADD bikes:rentable -122.27652 37.805186 station:1
(integer) 1
> GEOADD bikes:rentable -122.2674626 37.8062344 station:2
(integer) 1
> GEOADD bikes:rentable -122.2469854 37.8104049 station:3
(integer) 1
# 주어진 위치(FROMLONLAT부터 BYRADIUS 전까지)로부터 5킬로미터 반경 내의 모든 대여 가능한 자전거 정류장(bikes:rentable)을 찾고, 각 정류장까지의 거리를 반환
> GEOSEARCH bikes:rentable FROMLONLAT -122.2612767 37.7936847 BYRADIUS 5 km WITHDIST
1) 1) "station:1"
2) "1.8523"
2) 1) "station:2"
2) "1.4979"
3) 1) "station:3"
2) "2.2441"
비트 단위의 연산을 지원하는 구조로, 공간 효율적인 데이터 저장에 유용합니다.
Bitmaps의 경우 실제 데이터 타입은 아니고, String에 바이너리 연산을 적용한 것입니다!
최대 42억 개(32비트)의 이진 데이터를 표현할 수 있습니다(2^32)
명령어
SETBIT
: 주어진 오프셋에서 비트를 0 또는 1로 설정합니다.GETBIT
: 주어진 오프셋에서 비트의 값을 반환합니다.사용 예시(로그인 사례)
# 사용자 ID 1의 로그인 상태를 표시하는 비트맵 키 설정
# 사용자 ID 1이 로그인함 (=1번째 비트를 1로 설정)
> SETBIT user:logins 1 1
(integer) 0
# 사용자 ID 0의 로그인 상태 확인
> GETBIT user:logins 0
(integer) 0 # 결과: 0 (사용자 ID 0은 로그인하지 않음)
# 사용자 ID 1의 로그인 상태 확인
> GETBIT user:logins 1
(integer) 1 # 결과: 1 (사용자 ID 1은 로그인함)
# 사용자 ID 100의 로그아웃 상태 설정
# 사용자 ID 100이 로그아웃함 (=100번째 비트를 0으로 설정)
> SETBIT user:logins 100 0
(integer) 0
# 사용자 ID 100의 로그인 상태 확인
> GETBIT user:logins 100 # 결과: 0 (사용자 ID 100은 로그아웃 상태)
(integer) 0
# 존재하지 않는 비트의 값 확인 (기본값은 0)
> GETBIT user:logins 1000 # 결과: 0 (1000번째 비트는 설정되지 않았으므로 0 반환)
(integer) 0
집합의 근사적인 카디널리티(유니크한 요소의 개수)를 매우 적은 메모리로 추정할 수 있는 확률적 자료구조
입니다. 실제 값을 저장하지 않으며, 해시값에 대한 통계적인 정보만을 저장합니다.
(각 요소를 해시 함수를 통해 처리한 뒤 해시값의 비트 패턴을 분석해 통계 지표를 업데이트하는 식으로)
'확률적 구조'라는 점에서 유추할 수 있듯이, HyperLogLog는 정확성을 일부 포기하는 대신 저장공간을 효율적으로 사용할 수 있는 자료구조입니다.
정확성이 깨지는 기준인 표준 에러는 0.81% 정도라고 하네요!
HyperLogLog는 주로 방문자 수 추적, 검색어 카운트 등에 사용됩니다(딱! 정확하진 않아도 되는데, 필요하긴 한?).
명령어
PFADD
: HyperLogLog에 항목을 추가합니다.PFCOUNT
: 집합 내 항목 수의 추정치를 반환합니다.PFMERGE
: 두 개 이상의 HyperLogLog를 하나로 결합합니다.사용 예시
# bikes라는 HyperLogLog 구조에 뒤 요소를 추가하라~
> PFADD bikes Hyperion Deimos Phoebe Quaoar
(integer) 1
> PFCOUNT bikes
(integer) 4
> PFADD commuter_bikes Salacia Mimas Quaoar
(integer) 1
# all_bikes라는 HyperLogLog 구조에 bikes랑 commuter_bikes를 병합해라~
> PFMERGE all_bikes bikes commuter_bikes
OK
> PFCOUNT all_bikes
(integer) 6
요소가 집합에 속하는지 빠르게 확인할 수 있는 확률적 자료구조입니다.
거짓 양성(false positive)은 가능하지만, 거짓 음성(false negative)은 없습니다.
명령어
사용 예시
# 'bikes:models'라는 이름의 블룸 필터를 생성
# 오류율 0.001(0.1%), 예상 원소 수 1,000,000으로 설정
> BF.RESERVE bikes:models 0.001 1000000
OK
# 블룸 필터에 "Smoky Mountain Striker" 모델을 추가
> BF.ADD bikes:models "Smoky Mountain Striker"
(integer) 1 # 1: 성공적으로 추가되었음을 의미
# "Smoky Mountain Striker" 모델이 블룸 필터에 존재하는지 확인
> BF.EXISTS bikes:models "Smoky Mountain Striker"
(integer) 1 # 1: 존재함
# 여러 모델을 한 번에 블룸 필터에 추가
# 각 1은 해당 모델이 성공적으로 추가되었음을 의미
> BF.MADD bikes:models "Rocky Mountain Racer" "Cloudy City Cruiser" "Windy City Wippet"
1) (integer) 1
2) (integer) 1
3) (integer) 1
# 여러 모델이 블룸 필터에 존재하는지 한 번에 확인
# 각 1은 해당 모델이 존재한다는 것을 의미
> BF.MEXISTS bikes:models "Rocky Mountain Racer" "Cloudy City Cruiser" "Windy City Wippet"
1) (integer) 1
2) (integer) 1
3) (integer) 1