레디스란?

Redis는 'Remote Dictionary Server'의 약자로, 분산 환경에서 여러 서버가 공유할 수 있는 인메모리 데이터 저장소입니다. 주요 특징은 다음과 같습니다.

  1. 고성능 키-값 저장소로 동작합니다.
  2. 다양한 데이터 구조를 지원합니다. (문자열, 해시, 리스트 등)
  3. 빠른 읽기/쓰기 속도를 제공합니다.

Redis는 오랫동안 오픈 소스 프로젝트로 알려져 왔지만, 2024년 3월부터 라이선스가 변경되었습니다. BSD에서 SSPL(Server Side Public License) + RSAL(Redis Source Available License) 조합으로 변경되었습니다.

이로 인해 Redis의 '오픈 소스' 지위에 대한 논쟁이 있죠!

레디스(Redis) 이슈로 보는 오픈소스 논쟁

다음으로 레디스의 특징을 알아봅시당

레디스의 특징

레디스는 아래와 같은 특징을 가지고 있습니다.

  • 인메모리: 데이터를 주 메모리(RAM)에 저장하여 빠른 읽기와 쓰기 속도를 제공
  • 싱글 스레드: 기본적으로 단일 스레드로 동작하여 데이터 일관성을 보장하고 데드락 가능성 X
  • 클러스터: 여러 노드에 데이터를 분산 저장하여 높은 가용성과 확장성 제공
  • 영속성 지원: 데이터를 SSD와 같은 영구 저장소에 기록할 수 있음
    옵션 1 - RDB(Redis Database): 특정 시점 간격마다 스냅샷 생성
    옵션 2 - AOF(Append Only File): 서버가 받은 모든 쓰기 작업을 로그로 기록
  • Pub/Sub: Pub/Sub 패턴 사용으로 간편한 애플리케이션(채팅, 알림 등) 구현 지원

그럼, 레디스를 왜 쓸까요?

레디스 왜 씀(장점에 대하여)?

레디스는 아래와 같은 장점을 가지고 있습니다.

  • 높은 성능
    모든 데이터를 메모리(RAM)에 저장하기 때문에 빠른 읽기와 쓰기 속도가 보장됩니다.
    이미지 출처: geeksforgeeks - 메모리 계층 구조 설계 및 특성
  • 데이터 타입 지원
  • 클라이언트 라이브러리
    레디스는 파이썬, 자바, 노드JS, C#/.NET 등 다양한 클라이언트 측에서 라이브러리 사용을 지원합니다.
    Libraries and tools
  • 커뮤니티
    * 레디스는 전체 데이터베이스에서 인지도 6위에 랭크되어 있습니다. 많은 사람들이 사용하고 있으니, 트러블슈팅 사례가 많고 커뮤니티도 크겠죠?!

이제 레디스의 자료 구조에 대해 알아봅시땅 💪🏼💪🏼

레디스 자료 구조

참고 자료: Understand Redis data types

Strings

가장 기본적인 데이터 타입으로, 문자열, 숫자, 직렬화된 객체(JSON string) 등 저장하는 자료 구조입니다.

> SET bike:1 Deimos
OK
> GET bike:1
"Deimos"

Lists

String의 연결 리스트(Linked List)입니다. O(1)의 상수 시간 복잡도로 양쪽 끝에서 푸시/팝 연산이 가능하며, 큐나 스택으로 사용할 수 있습니다.

큐, 스택 구현에 유용하겠죵? 😉

명령어

  • LPUSH: 리스트의 머리(앞쪽)에 새로운 요소를 추가합니다.
  • RPUSH: 꼬리(뒤쪽)에 추가합니다.
  • LPOP: 리스트의 머리에서 요소를 제거하고 반환합니다.
  • RPOP: 동일한 작업을 리스트의 꼬리에서 수행합니다.
  • LLEN: 리스트의 길이를 반환합니다.
  • LMOVE: 원자적으로(atomic) 한 리스트에서 다른 리스트로 요소를 이동시킵니다.
  • LRANGE: 리스트에서 지정된 범위의 요소들을 추출합니다.
  • LTRIM: 리스트를 지정된 범위의 요소들로 줄입니다.

Sets

순서가 없는 고유한 문자열(Unique String)의 집합입니다.

합집합, 교집합, 차집합 등의 집합 연산을 지원합니다!

명령어

  • SADD 집합에 새로운 멤버를 추가합니다.
  • SREM: 집합에서 지정된 멤버를 제거합니다.
  • SISMEMBER: 특정 문자열이 집합의 멤버인지 테스트합니다.
  • SINTER: 두 개 이상의 집합에 공통으로 존재하는 멤버들의 집합(즉, 교집합)을 반환합니다.
  • SCARD: 집합의 크기(일명 카디널리티)를 반환합니다.

Hashes

필드-값 구조를 갖는 데이터 타입으로, 다양한 속성을 갖는 객체 데이터를 저장하는 데 적합합니다.

명령어

  • 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

Sorted Sets

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"

Streams

로그나 시계열 데이터를 저장하는 데 최적화된 데이터 타입입니다. 고유한 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"

Geospatials

위도와 경도 정보를 저장하고 검색할 수 있는 데이터 타입입니다.

거리 계산, 범위 탐색 등을 지원하는 타입이죠!

명령어

  • 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

비트 단위의 연산을 지원하는 구조로, 공간 효율적인 데이터 저장에 유용합니다.

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

집합의 근사적인 카디널리티(유니크한 요소의 개수)를 매우 적은 메모리로 추정할 수 있는 확률적 자료구조입니다. 실제 값을 저장하지 않으며, 해시값에 대한 통계적인 정보만을 저장합니다.
(각 요소를 해시 함수를 통해 처리한 뒤 해시값의 비트 패턴을 분석해 통계 지표를 업데이트하는 식으로)

'확률적 구조'라는 점에서 유추할 수 있듯이, 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

BloomFilter

요소가 집합에 속하는지 빠르게 확인할 수 있는 확률적 자료구조입니다.

거짓 양성(false positive)은 가능하지만, 거짓 음성(false negative)은 없습니다.

  • 거짓 양성: "특정 요소가 집합에 있다"고 말하지만, 실제로는 없는 경우
  • 거짓 음성: "특정 요소가 집합에 없다"고 말하지만, 실제로는 있는 경우

명령어

  • BF.RESERVE: 지정된 오류율과 용량으로 Bloom filter를 생성합니다.
  • BF.ADD: Bloom filter에 단일 요소를 추가합니다.
  • BF.MADD: Bloom filter에 여러 요소를 한 번에 추가합니다.
  • BF.EXISTS: 요소가 Bloom filter에 존재하는지 확인합니다.
  • BF.MEXISTS: 여러 요소가 Bloom filter에 존재하는지 한 번에 확인합니다.

사용 예시

# '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
profile
계속 읽고 싶은 글을 쓰고 싶어요 ☺

0개의 댓글