분류 sql

Redis 데이터 유형 및 추상화 소개

컨텐츠 정보

  • 조회 420 (작성일 )

본문

Redis는 일반 키-값 저장소가 아니라 실제로 데이터 구조 서버이며 다양한 종류의 값을 지원합니다. 

이것이 의미하는 바는 기존의 키-값 저장소에서는 문자열 키를 문자열 값에 연결하지만 Redis에서는 값이 단순한 문자열로 제한되지 않고 더 복잡한 데이터 구조를 보유 할 수도 있다는 것입니다. 

다음은 Redis에서 지원하는 모든 데이터 구조의 목록이며 이 자습서에서 별도로 다룹니다.

  • Binary-safe strings(바이너리 안전 문자열).
  • 목록(Lists) : 삽입 순서에 따라 정렬 된 문자열 요소 모음입니다. 그들은 기본적으로 연결된 목록입니다.
  • 집합(Sets) : 정렬되지 않은 고유 한 문자열 요소의 모음입니다.
  • Sorted sets(정렬 된 집합): 집합과 유사하지만 모든 문자열 요소가 점수라고 하는 부동 숫자 값에 연결되는 정렬 된 집합입니다. 요소는 항상 점수별로 정렬되므로 세트와 달리 요소 범위를 검색 할 수 있습니다 (예를 들어 질문 할 수 있습니다 : 상위 10 개 또는 하위 10 개 제공).
  • Hashes(해시) : 값과 관련된 필드로 구성된 맵인 해시. 필드와 값은 모두 문자열입니다. 이것은 Ruby 또는 Python 해시와 매우 유사합니다.
  • Bit arrays(비트 배열 (또는 단순히 비트 맵)) : 특수 명령을 사용하여 비트 배열과 같은 문자열 값을 처리 할 수 ​​있습니다. 개별 비트를 설정 및 삭제하고, 모든 비트를 1로 설정하고, 첫 번째 설정 또는 설정 해제 된 비트를 찾을 수 있습니다. 기타 등등.
  • HyperLogLogs : 집합의 카디널리티를 추정하는 데 사용되는 확률적 데이터 구조입니다. 두려워하지 마십시오. 보기보다 간단합니다 ...이 자습서의 HyperLogLog 섹션의 뒷부분을 참조하십시오.
  • Streams(스트림) : 추상 로그 데이터 유형을 제공하는 맵과 유사한 항목의 추가 전용 모음입니다. Redis Streams 소개에서 자세히 다룹니다.

이러한 데이터 유형이 작동하는 방식과 명령 참조에서 주어진 문제를 해결하기 위해 무엇을 사용해야 하는지 파악하는 것이 항상 사소한 것은 아니므로 이 문서는 Redis 데이터 유형과 가장 일반적인 패턴에 대한 집중 과정입니다.


모든 예제에서 간단하지만 편리한 명령 줄 유틸리티인 redis-cli 유틸리티를 사용하여 Redis 서버에 대해 명령을 실행합니다.


Redis keys 


Redis 키는 바이너리로부터 안전합니다. 즉, "foo"와 같은 문자열에서 JPEG 파일의 내용에 이르기까지 모든 바이너리 시퀀스를 키로 사용할 수 있습니다. 빈 문자열도 유효한 키입니다.


키에 대한 몇 가지 다른 규칙 :

  • 아주 긴 키는 좋은 생각이 아닙니다. 예를 들어 1024 바이트의 키는 메모리 측면에서 뿐만 아니라 데이터 세트에서 키를 조회하는 데 몇 가지 비용이 많이 드는 키 비교가 필요할 수 있기 때문에 나쁜 생각입니다. 당면한 작업이 큰 값의 존재와 일치하는 경우에도 이를 해싱 (예 : SHA1 사용)하는 것이 특히 메모리 및 대역폭의 관점에서 더 나은 아이디어입니다.
  • 아주 짧은 키는 종종 좋은 생각이 아닙니다. 대신 "user : 1000 : followers"를 쓸 수 있다면 "u1000flw"를 키로 쓰는 데는 별 의미가 없습니다. 후자는 더 읽기 쉽고 추가 된 공간은 키 객체 자체와 값 객체가 사용하는 공간에 비해 작습니다. 짧은 키는 분명히 메모리를 조금 덜 사용하지만 올바른 균형을 찾는 것입니다.
  • 스키마를 고수하십시오. 예를 들어 "object-type : id"는 "user : 1000"에서와 같이 좋은 생각입니다. 점 또는 대시는 "comment : 1234 : reply.to"또는 "comment : 1234 : reply-to"와 같이 여러 단어로 된 필드에 자주 사용됩니다.
  • 허용되는 최대 키 크기는 512MB입니다.


Redis Strings 


Redis 문자열 유형은 Redis 키와 연결할 수 있는 가장 간단한 유형의 값입니다. Memcached의 유일한 데이터 유형이므로 초보자가 Redis에서 사용하는 것도 매우 자연스러운 일입니다.

Redis 키는 문자열이기 때문에 문자열 유형을 값으로 사용할 때 문자열을 다른 문자열에 매핑합니다. 문자열 데이터 유형은 HTML 조각 또는 페이지 캐싱과 같은 여러 사용 사례에 유용합니다.

redis-cli를 사용하여 문자열 유형을 조금 사용해 보겠습니다 (이 자습서에서는 모든 예제가 redis-cli를 통해 수행됩니다).


> set mykey somevalue
OK
> get mykey
"somevalue"

보시다시피 SET 및 GET 명령을 사용하는 것은 문자열 값을 설정하고 검색하는 방법입니다. SET는 키가 문자열이 아닌 값과 연결되어 있어도 키가 이미 있는 경우 키에 이미 저장된 기존 값을 대체합니다. 따라서 SET는 할당을 수행합니다.

값은 모든 종류의 문자열 (이진 데이터 포함)이 될 수 있습니다. 예를 들어 값 안에 jpeg 이미지를 저장할 수 있습니다. 값은 512MB보다 클 수 없습니다.

SET 명령에는 추가 인수로 제공되는 흥미로운 옵션이 있습니다. 예를 들어, 키가 이미 존재하면 SET에 실패하거나 키가 이미 존재하는 경우에만 성공하도록 SET에 요청할 수 있습니다.


> set mykey newval nx
(nil)
> set mykey newval xx
OK

문자열이 Redis의 기본 값이더라도 문자열로 수행 할 수 있는 흥미로운 작업이 있습니다. 예를 들어, 하나는 원자적 증분입니다.


> set counter 100
OK
> incr counter
(integer) 101
> incr counter
(integer) 102
> incrby counter 50
(integer) 152

INCR 명령은 문자열 값을 정수로 구문 분석하고 1 씩 증가 시킨 다음 마지막으로 얻은 값을 새 값으로 설정합니다. INCRBY, DECRDECRBY와 같은 다른 유사한 명령이 있습니다. 내부적으로는 항상 동일한 명령이며 약간 다른 방식으로 작동합니다.


INCR이 원자적이라는 것은 무엇을 의미합니까? 동일한 키에 대해 INCR을 발행하는 여러 클라이언트조차도 경쟁 조건에 들어 가지 않습니다. 예를 들어, 클라이언트 1이 "10"을 읽고, 클라이언트 2가 동시에 "10"을 읽고, 둘 다 11로 증가하고, 새 값을 11로 설정하는 일은 절대 발생하지 않습니다. 다른 모든 클라이언트가 동시에 명령을 실행하지 않는 동안 증가 설정 작업이 수행됩니다.

문자열에서 작동하기 위한 여러 명령이 있습니다. 예를 들어 GETSET 명령은 키를 새 값으로 설정하여 결과로 이전 값을 반환합니다. 예를 들어 웹 사이트가 새 방문자를 받을 때마다 INCR을 사용하여 Redis 키를 증가 시키는 시스템이 있는 경우 이 명령을 사용할 수 있습니다. 한 번의 증분을 잃지 않고 매시간 한 번씩 이 정보를 수집 할 수 있습니다. 키를 GETSET하여 새 값 "0"을 할당하고 이전 값을 다시 읽을 수 있습니다.

단일 명령에서 여러 키의 값을 설정하거나 검색하는 기능은 지연 시간을 줄이는데도 유용합니다. 이러한 이유로 MSETMGET 명령이 있습니다.


> mset a 10 b 20 c 30
OK
> mget a b c
1) "10"
2) "20"
3) "30"

MGET을 사용하면 Redis는 값 배열을 반환합니다.


Altering and querying the key space 


특정 유형에 정의되지 않은 명령이 있지만 키 공간과 상호 작용하는 데 유용하므로 모든 유형의 키와 함께 사용할 수 있습니다.


예를 들어 EXISTS 명령은 주어진 키가 데이터베이스에 존재하는지 여부를 알리기 위해 1 또는 0을 반환하고 DEL 명령은 값이 무엇이든 관계없이 키 및 관련 값을 삭제합니다.

> set mykey hello
OK
> exists mykey
(integer) 1
> del mykey
(integer) 1
> exists mykey
(integer) 0


예제에서 키가 제거되었는지 (존재했는지) 여부 (해당 이름을 가진 키가 없음)에 따라 DEL 자체가 1 또는 0을 반환하는 방법을 볼 수 있습니다.


키 공간 관련 명령이 많이 있지만 위의 두 가지 명령은 지정된 키에 저장된 값의 종류를 반환하는 TYPE 명령과 함께 필수적인 명령입니다.


> set mykey x
OK
> type mykey
string
> del mykey
(integer) 1
> type mykey
none


Redis expires: keys with limited time to live 


더 복잡한 데이터 구조를 계속하기 전에 값 유형에 관계없이 작동하며 Redis 만료라는 또 다른 기능에 대해 논의해야 합니다. 기본적으로 키에 대한 제한 시간을 설정할 수 있습니다. TTL (Time to Live)이 경과하면 사용자가 키로 DEL 명령을 호출 한 것처럼 키가 자동으로 폐기됩니다.


Redis에 대한 몇 가지 빠른 정보가 만료됩니다.

  • 초 또는 밀리 초 정밀도를 사용하여 둘 다 설정할 수 있습니다.
  • 그러나 만료 시간 확인은 항상 1 밀리 초입니다.
  • 만료에 대한 정보는 복제되고 디스크에 유지되며 Redis 서버가 중지 된 상태로 유지되는 시간은 가상으로 경과합니다 (이는 Redis가 키가 만료되는 날짜를 저장함을 의미 함).

만료 설정은 간단합니다.


> set key some-value
OK
> expire key 5
(integer) 1
> get key (immediately)
"some-value"
> get key (after some time)
(nil)


두 번째 호출이 5 초 이상 지연되었으므로 두 GET 호출 사이에 키가 사라졌습니다. 위의 예에서 만료를 설정하기 위해 EXPIRE를 사용했습니다 (이미 가지고 있는 키에 다른 만료를 설정하는 데에도 사용할 수 있습니다. 만료를 제거하고 키를 영구적으로 유지하기 위해 PERSIST를 사용할 수 있습니다.) ).

그러나 다른 Redis 명령을 사용하여 만료가 있는 키를 만들 수도 있습니다. 예를 들어 SET 옵션 사용 :


> set key 100 ex 10
OK
> ttl key
(integer) 9


위의 예에서는 만료가 10 초인 문자열 값이 100 인 키를 설정합니다. 나중에 키의 남은 시간을 확인하기 위해 TTL 명령이 호출됩니다.

밀리 초 단위로 만료를 설정하고 확인하려면 PEXPIREPTTL 명령과 SET 옵션의 전체 목록을 확인하십시오.


Redis Lists 


List 데이터 유형을 설명하려면 약간의 이론으로 시작하는 것이 좋습니다. List라는 용어는 정보 기술 담당자가 종종 부적절한 방식으로 사용하기 때문입니다. 예를 들어 "Python Lists"는 이름이 암시하는 것 (연결된 목록)이 아니라 배열 (실제로 Ruby에서는 동일한 데이터 유형을 Array라고 함)입니다.

매우 일반적인 관점에서 목록은 순서가 지정된 요소의 시퀀스 일 뿐입니다. 10,20,1,2,3은 목록입니다. 그러나 Array를 사용하여 구현 된 List의 속성은 Linked List를 사용하여 구현 된 List의 속성과 매우 다릅니다.

Redis 목록은 연결된 목록을 통해 구현됩니다. 즉, 목록 안에 수백만 개의 요소가 있어도 목록의 머리 부분이나 꼬리 부분에 새 요소를 추가하는 작업은 일정한 시간에 수행됩니다. LPUSH 명령을 사용하여 10 개의 요소가 있는 목록의 헤드에 새 요소를 추가하는 속도는 1,000 만 개의 요소가 있는 목록의 헤드에 요소를 추가하는 것과 같습니다.

단점은 무엇입니까? 인덱스로 요소에 액세스하는 것은 Array (일정 시간 인덱스 액세스)로 구현 된 목록에서 매우 빠르지 않고 연결 목록으로 구현 된 목록 (액세스 된 요소의 인덱스에 비례하는 작업량을 필요로 하는 작업)에서는 그렇게 빠르지 않습니다.

Redis 목록은 연결 목록으로 구현됩니다. 데이터베이스 시스템의 경우 매우 빠른 방법으로 매우 긴 목록에 요소를 추가 할 수 있어야 하기 때문입니다. 잠시 후에 보게 될 또 다른 장점은 Redis 목록을 일정한 시간에 일정한 길이로 가져올 수 있다는 것입니다.

대규모 요소 컬렉션의 중간에 빠르게 액세스하는 것이 중요 할 때 사용할 수 있는 다른 데이터 구조 (Sorted sets이라고 함)가 있습니다. 정렬 된 집합(Sorted sets)은 이 자습서의 뒷부분에서 다룹니다.


First steps with Redis Lists 


LPUSH 명령은 왼쪽 (머리)의 목록에 새 요소를 추가하고 RPUSH 명령은 오른쪽 (꼬리)의 목록에 새 요소를 추가합니다. 마지막으로 LRANGE 명령은 목록에서 요소 범위를 추출합니다.


> rpush mylist A
(integer) 1
> rpush mylist B
(integer) 2
> lpush mylist first
(integer) 3
> lrange mylist 0 -1
1) "first"
2) "A"
3) "B"

LRANGE는 반환 할 범위의 첫 번째 요소와 마지막 요소 인 두 개의 인덱스를 사용합니다. 두 인덱스 모두 음수가 될 수 있으며 Redis에게 끝부터 계산을 시작하도록 지시합니다. 따라서 -1은 마지막 요소이고 -2는 목록의 끝에서 두 번째 요소입니다.

보시다시피 RPUSH는 목록의 오른쪽에 요소를 추가하고 최종 LPUSH는 왼쪽에 요소를 추가했습니다.

두 명령 모두 가변 명령(variadic commands)이므로 한 번의 호출로 여러 요소를 목록에 자유롭게 푸시 할 수 있습니다.


> rpush mylist 1 2 3 4 5 "foo bar"
(integer) 9
> lrange mylist 0 -1
1) "first"
2) "A"
3) "B"
4) "1"
5) "2"
6) "3"
7) "4"
8) "5"
9) "foo bar"


Redis 목록에 정의 된 중요한 작업은 요소를 팝(pop elements)하는 기능입니다. 요소 팝핑은 목록에서 요소를 검색하는 동시에 목록에서 제거하는 작업입니다. 목록의 양쪽에 요소를 푸시하는 방법과 유사하게 왼쪽과 오른쪽에서 요소를 팝할 수 있습니다.


> rpush mylist a b c
(integer) 3
> rpop mylist
"c"
> rpop mylist
"b"
> rpop mylist
"a"


세 개의 요소를 추가하고 세 개의 요소를 팝 했으므로 이 명령 시퀀스의 끝에 목록이 비어 있고 더 이상 팝할 요소가 없습니다. 또 다른 요소를 팝하려고 하면 다음과 같은 결과가 나타납니다.


> rpop mylist
(nil)


Redis는 목록에 요소가 없음을 알리기 위해 NULL 값을 반환했습니다.


Common use cases for lists 


목록은 여러 작업에 유용하며 두 가지 대표적인 사용 사례는 다음과 같습니다.

  • 사용자가 소셜 네트워크에 게시 한 최신 업데이트를 기억하십시오.
  • 생산자가 항목을 목록에 푸시하고 소비자 (일반적으로 작업자)가 해당 항목을 소비하고 작업을 실행하는 소비자-생산자 패턴을 사용하는 프로세스 간의 통신입니다. Redis에는 이 사용 사례를 보다 안정적이고 효율적으로 만드는 특별한 목록 명령이 있습니다.

예를 들어 인기 있는 Ruby 라이브러리 resquesidekiq 모두 백그라운드 작업을 구현하기 위해 내부적으로 Redis 목록을 사용합니다.


인기 있는 Twitter 소셜 네트워크는 사용자가 게시 한 최신 트윗을 Redis 목록으로 가져옵니다.


일반적인 사용 사례를 단계별로 설명하려면 홈 페이지에 사진 공유 소셜 네트워크에 게시 된 최신 사진이 표시되고 액세스 속도를 높이고자 한다고 가정 해보십시오.

  • 사용자가 새 사진을 게시 할 때마다 LPUSH를 사용하여 목록에 ID를 추가합니다.
  • 사용자가 홈페이지를 방문하면 LRANGE 0 9를 사용하여 최근 게시 된 항목 10 개를 가져옵니다.

Capped lists 


많은 사용 사례에서 우리는 소셜 네트워크 업데이트, 로그 또는 기타 항목이 무엇이든 최신 항목을 저장하기 위해 목록을 사용하려고 합니다.

Redis를 사용하면 목록을 제한 컬렉션으로 사용할 수 있으며, 최신 N 항목 만 기억하고 LTRIM 명령을 사용하여 가장 오래된 항목을 모두 삭제할 수 있습니다.

LTRIM 명령은 LRANGE와 유사하지만 지정된 요소 범위를 표시하는 대신 이 범위를 새 목록 값으로 설정합니다. 지정된 범위를 벗어난 모든 요소가 제거됩니다.


예를 들어 더 명확하게 알 수 있습니다.

> rpush mylist 1 2 3 4 5
(integer) 5
> ltrim mylist 0 2
OK
> lrange mylist 0 -1
1) "1"
2) "2"
3) "3"


위의 LTRIM 명령은 Redis에게 인덱스 0에서 2까지의 목록 요소 만 가져 오도록 지시하고 나머지는 모두 버립니다. 이것은 매우 간단하지만 유용한 패턴을 허용합니다. 새로운 요소를 추가하고 제한을 초과하는 요소를 버리기 위해 목록 푸시 작업 + 목록 트림 작업을 함께 수행합니다.


LPUSH mylist <some element>
LTRIM mylist 0 999


위의 조합은 새 요소를 추가하고 목록에 최신 요소 1000 개만 가져옵니다. LRANGE를 사용하면 아주 오래된 데이터를 기억할 필요 없이 상위 항목에 액세스 할 수 있습니다.

참고 : LRANGE는 기술적으로 O (N) 명령이지만 목록의 앞부분이나 뒷부분을 향한 작은 범위에 액세스하는 것은 일정한 시간 작업입니다.


Blocking operations on lists 


목록에는 대기열을 구현하는 데 적합하고 일반적으로 프로세스 간 통신 시스템의 구성 요소 인 차단 작업에 적합한 특수 기능이 있습니다.

하나의 프로세스가 있는 목록으로 항목을 푸시하고 실제로 해당 항목에 대해 일종의 작업을 수행하기 위해 다른 프로세스를 사용한다고 가정 해보십시오. 이것은 일반적인 생산자 / 소비자 설정이며 다음과 같은 간단한 방법으로 구현할 수 있습니다.

  • 목록에 항목을 추가하기 위해 생산자는 LPUSH를 호출합니다.
  • 목록에서 항목을 추출 / 처리하기 위해 소비자는 RPOP를 호출합니다.

그러나 때때로 목록이 비어 있고 처리 할 항목이 없을 수 있으므로 RPOP는 NULL을 반환합니다. 이 경우 소비자는 잠시 기다렸다가 RPOP를 사용하여 다시 시도해야 합니다. 이를 폴링이라고 하며 다음과 같은 몇 가지 단점이 있기 때문에 이 컨텍스트에서 좋은 생각이 아닙니다.

  1. Redis와 클라이언트가 쓸모없는 명령을 처리하도록 합니다 (목록이 비어있는 모든 요청은 실제 작업이 수행되지 않고 NULL을 반환합니다).
  2. 작업자가 NULL을 받은 후 얼마 동안 대기하므로 항목 처리에 지연을 추가합니다. 지연 시간을 줄이려면 RPOP 호출 사이에 더 적게 대기 할 수 있으며, 문제 번호 1을 증폭 시키는 효과, 즉 Redis에 대한 더 쓸모없는 호출이 가능합니다.

따라서 Redis는 목록이 비어있는 경우 차단할 수 있는 RPOP 및 LPOP 버전인 BRPOPBLPOP라는 명령을 구현합니다. 목록에 새 요소가 추가되거나 사용자가 지정한 시간 제한이 있을 때만 호출자에게 반환됩니다. 도달했습니다.


다음은 워커에서 사용할 수 있는 BRPOP 호출의 예입니다.


> brpop tasks 5
1) "tasks"
2) "do_something"

이는 "목록 작업의 요소를 기다리지 만 5 초 후에 요소를 사용할 수 없으면 반환"을 의미합니다.

0을 시간 제한으로 사용하여 요소를 영원히 기다릴 수 있으며, 동시에 여러 목록에서 대기하고 첫 번째 목록이 요소를 수신 할 때 알림을 받기 위해 하나가 아닌 여러 목록을 지정할 수도 있습니다.

BRPOP에 대해 유의해야 할 몇 가지 사항 :

  1. 클라이언트는 순서대로 제공됩니다. 목록 대기를 차단 한 첫 번째 클라이언트는 다른 클라이언트가 요소를 푸시 할 때 먼저 제공됩니다.
  2. 반환 값은 RPOP에 비해 다릅니다. BRPOPBLPOP는 여러 목록의 요소 대기를 차단할 수 있기 때문에 키 이름도 포함하므로 두 요소 배열입니다.
  3. 제한 시간에 도달하면 NULL이 반환 됩니다.

목록 및 차단 작업에 대해 알아야 할 사항이 더 있습니다. 다음 내용에 대해 자세히 읽어 보시기 바랍니다.

  • LMOVE를 사용하여 보다 안전한 대기열 또는 순환 대기열을 구축 할 수 있습니다.
  • BLMOVE라는 명령의 차단 변형도 있습니다.

Automatic creation and removal of keys 


지금까지 예제에서 요소를 푸시하기 전에 빈 목록을 만들거나 더 이상 내부에 요소가 없을 때 빈 목록을 제거 할 필요가 없었습니다. 목록이 비어있을 때 키를 삭제하거나 키가 존재하지 않고 예를 들어 LPUSH를 사용하여 요소를 추가하려는 경우 빈 목록을 만드는 것은 Redis의 책임입니다.

이는 목록에만 국한되지 않으며 스트림, 세트, ​​정렬 된 세트 및 해시와 같은 여러 요소로 구성된 모든 Redis 데이터 유형에 적용됩니다.

기본적으로 세 가지 규칙으로 동작을 요약 할 수 있습니다.

  1. 집계 데이터 유형에 요소를 추가 할 때 대상 키가 존재하지 않으면 요소를 추가하기 전에 빈 집계 데이터 유형이 생성됩니다.
  2. 집계 데이터 유형에서 요소를 제거 할 때 값이 비어 있으면 키가 자동으로 삭제됩니다. 스트림 데이터 유형은 이 규칙에 대한 유일한 예외입니다.
  3. LLEN (목록의 길이를 반환 함)과 같은 읽기 전용 명령을 호출하거나 빈 키를 사용하여 요소를 제거하는 쓰기 명령을 호출하면 키가 다음 유형의 빈 집계 유형을 보유하고 있는 것처럼 항상 동일한 결과를 생성합니다. 명령은 찾을 것으로 예상합니다.

규칙 1의 예 :


> del mylist
(integer) 1
> lpush mylist 1 2 3
(integer) 3

그러나 키가 존재하면 잘못된 유형에 대해 작업을 수행 할 수 없습니다.


> set foo bar
OK
> lpush foo 1 2 3
(error) WRONGTYPE Operation against a key holding the wrong kind of value
> type foo
string


규칙 2의 예 :


> lpush mylist 1 2 3
(integer) 3
> exists mylist
(integer) 1
> lpop mylist
"3"
> lpop mylist
"2"
> lpop mylist
"1"
> exists mylist
(integer) 0


모든 요소가 팝된 후에는 키가 더 이상 존재하지 않습니다.


규칙 3의 예 :


> del mylist
(integer) 0
> llen mylist
(integer) 0
> lpop mylist
(nil)


Redis Hashes 


Redis 해시는 필드-값 쌍을 사용하여 "해시"가 예상되는 방식과 정확히 일치합니다.


> hmset user:1000 username antirez birthyear 1977 verified 1
OK
> hget user:1000 username
"antirez"
> hget user:1000 birthyear
"1977"
> hgetall user:1000
1) "username"
2) "antirez"
3) "birthyear"
4) "1977"
5) "verified"
6) "1"


해시는 객체를 표현하는 데 편리하지만 실제로 해시 안에 넣을 수 있는 필드의 수에는 실제적인 제한 (사용 가능한 메모리 제외)이 없으므로 응용 프로그램 내에서 여러 가지 방법으로 해시를 사용할 수 있습니다. HMSET 명령은 해시의 여러 필드를 설정하는 반면 HGET은 단일 필드를 검색합니다. HMGETHGET과 유사하지만 값 배열을 반환합니다.


> hmget user:1000 username birthyear no-such-field
1) "antirez"
2) "1977"
3) (nil)


HINCRBY와 같이 개별 필드에서도 작업을 수행 할 수 있는 명령이 있습니다.


> hincrby user:1000 birthyear 10
(integer) 1987
> hincrby user:1000 birthyear 10
(integer) 1997


문서에서 해시 명령의 전체 목록을 찾을 수 있습니다. 작은 해시 (즉, 작은 값을 가진 몇 가지 요소)가 메모리에서 특별한 방식으로 인코딩되어 메모리를 매우 효율적으로 만듭니다.


Redis Sets 


Redis 세트는 정렬되지 않은 문자열 모음입니다. SADD 명령은 세트에 새 요소를 추가합니다. 주어진 요소가 이미 존재하는지 테스트하고 여러 세트 간의 교차, 결합 또는 차이 수행 등과 같은 세트에 대해 여러 다른 작업을 수행 할 수도 있습니다.


> sadd myset 1 2 3
(integer) 3
> smembers myset
1. 3
2. 1
3. 2


여기에 세 가지 요소를 세트에 추가하고 Redis에 모든 요소를 ​​반환하도록 지시했습니다. 보시다시피 정렬되지 않았습니다. 요소 순서에 대해 사용자와 계약이 없기 때문에 Redis는 모든 호출에서 모든 순서로 요소를 자유롭게 반환 할 수 있습니다.

Redis에는 멤버십을 테스트하는 명령이 있습니다. 예를 들어, 요소가 있는지 확인합니다.


> sismember myset 3
(integer) 1
> sismember myset 30
(integer) 0


"3"은 세트의 구성원이고 "30"은 그렇지 않습니다.

세트는 객체 간의 관계를 표현하는 데 좋습니다. 예를 들어 태그를 구현하기 위해 세트를 쉽게 사용할 수 있습니다.

이 문제를 모델링 하는 간단한 방법은 태그를 지정하려는 모든 개체에 대한 집합을 갖는 것입니다. 집합에는 개체와 관련된 태그의 ID가 포함됩니다.

한 가지 예는 뉴스 기사에 태그를 지정하는 것입니다. 기사 ID 1000이 태그 1, 2, 5 및 77로 태그 된 경우 세트는 다음 태그 ID를 뉴스 항목과 연결할 수 있습니다.


> sadd news:1000:tags 1 2 5 77
(integer) 4


또한 역 관계를 원할 수도 있습니다 : 주어진 태그로 태그가 지정된 모든 뉴스 목록 :


> sadd tag:1:news 1000
(integer) 1
> sadd tag:2:news 1000
(integer) 1
> sadd tag:5:news 1000
(integer) 1
> sadd tag:77:news 1000
(integer) 1


주어진 객체에 대한 모든 태그를 얻는 것은 간단합니다.


> smembers news:1000:tags
1. 5
2. 1
3. 77
4. 2


참고 :이 예에서는 태그 ID를 태그 이름에 매핑하는 Redis 해시와 같은 다른 데이터 구조가 있다고 가정합니다.


올바른 Redis 명령을 사용하여 구현하기 쉬운 다른 사소하지 않은 작업이 있습니다. 예를 들어 태그 1, 2, 10 및 27이 함께 있는 모든 개체의 목록을 원할 수 있습니다. 다른 세트 간의 교차를 수행하는 SINTER 명령을 사용하여 이를 수행 할 수 있습니다. 다음을 사용할 수 있습니다.


> sinter tag:1:news tag:2:news tag:10:news tag:27:news
... results here ...


교차 외에도 합집합, 차이를 수행하고 임의의 요소를 추출하는 등의 작업을 수행 할 수 있습니다. 요소를 추출하는 명령을 SPOP라고 하며 특정 문제를 모델링 하는 데 유용합니다. 예를 들어 웹 기반 포커 게임을 구현하기 위해 세트로 덱을 표현할 수 있습니다. (C) lubs, (D) iamonds, (H) earts, (S) pades에 1 문자 접두사를 사용한다고 상상해보십시오.


>  sadd deck C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 CJ CQ CK
   D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 DJ DQ DK H1 H2 H3
   H4 H5 H6 H7 H8 H9 H10 HJ HQ HK S1 S2 S3 S4 S5 S6
   S7 S8 S9 S10 SJ SQ SK
   (integer) 52


이제 각 플레이어에게 5 장의 카드를 제공하려고 합니다. SPOP 명령은 임의의 요소를 제거하여 클라이언트에 반환하므로 이 경우 완벽한 작업입니다.

그러나 우리가 덱에 대해 직접 호출하면 다음 게임 플레이에서 카드 덱을 다시 채워야 하므로 이상적이지 않을 수 있습니다. 시작하려면 데크 키에 저장된 세트의 사본을 game : 1 : deck 키로 만들 수 있습니다.

이는 일반적으로 여러 세트 간의 통합을 수행하고 결과를 다른 세트에 저장하는 SUNIONSTORE를 사용하여 수행됩니다. 그러나 단일 세트의 합집합 자체이므로 다음을 사용하여 덱을 복사 할 수 있습니다.


> sunionstore game:1:deck deck
(integer) 52


이제 첫 번째 플레이어에게 5 장의 카드를 제공 할 준비가 되었습니다.


> spop game:1:deck
"C6"
> spop game:1:deck
"CQ"
> spop game:1:deck
"D1"
> spop game:1:deck
"CJ"
> spop game:1:deck
"SJ"


한 쌍의 잭, 좋지 않습니다 ...

세트 내부의 요소 수를 제공하는 set 명령을 도입하기에 좋은 시기입니다. 이것을 집합 이론의 맥락에서 집합의 카디널리티라고 부르기 때문에 Redis 명령을 SCARD라고 합니다.


> scard game:1:deck
(integer) 47


수학은 52-5 = 47입니다.

세트에서 요소를 제거하지 않고 임의의 요소 만 가져와야 하는 경우 작업에 적합한 SRANDMEMBER 명령이 있습니다. 또한 반복 및 비 반복 요소를 모두 반환하는 기능도 있습니다.


Redis Sorted sets 


정렬 된 세트는 세트와 해시의 혼합과 유사한 데이터 유형입니다. 집합과 마찬가지로 정렬 된 집합은 고유하고 반복되지 않는 문자열 요소로 구성되므로 어떤 의미에서는 정렬 된 집합도 집합입니다.

그러나 집합 내부의 요소는 정렬되지 않지만 정렬 된 집합의 모든 요소는 점수라고 하는 부동 소수점 값과 연결됩니다 (모든 요소가 값에 매핑 되기 때문에 유형도 해시와 유사합니다).

또한 정렬 된 세트의 요소는 순서대로 취해집니다 (따라서 요청시 정렬되지 않으므로 정렬 된 세트를 나타내는 데 사용되는 데이터 구조의 특성입니다). 다음 규칙에 따라 정렬됩니다.

  • A와 B가 점수가 다른 두 요소 인 경우 A.score가> B.score이면 A> B입니다.
  • A와 B의 점수가 정확히 같으면 A 문자열이 사 전적으로 B 문자열보다 크면 A> B입니다. 정렬 된 집합에는 고유 한 요소 만 있기 때문에 A와 B 문자열은 같을 수 없습니다.

간단한 예부터 시작해 보겠습니다. 선택한 해커 이름 몇 개를 생년월일이 "점수"인 정렬 된 집합 요소로 추가합니다.


> zadd hackers 1940 "Alan Kay"
(integer) 1
> zadd hackers 1957 "Sophie Wilson"
(integer) 1
> zadd hackers 1953 "Richard Stallman"
(integer) 1
> zadd hackers 1949 "Anita Borg"
(integer) 1
> zadd hackers 1965 "Yukihiro Matsumoto"
(integer) 1
> zadd hackers 1914 "Hedy Lamarr"
(integer) 1
> zadd hackers 1916 "Claude Shannon"
(integer) 1
> zadd hackers 1969 "Linus Torvalds"
(integer) 1
> zadd hackers 1912 "Alan Turing"
(integer) 1


보시다시피 ZADDSADD와 유사하지만 점수 인 하나의 추가 인수 (추가 할 요소 앞에 배치됨)를 사용합니다. ZADD도 가변적이므로 위의 예에서 사용되지 않더라도 여러 점수 값 쌍을 자유롭게 지정할 수 있습니다.

정렬 된 세트를 사용하면 실제로 이미 정렬되어 있기 때문에 출생 연도별로 정렬 된 해커 목록을 반환하는 것은 간단합니다.

구현 참고 사항 : 정렬 된 집합은 건너 뛰기 목록과 해시 테이블을 모두 포함하는 이중 포트 데이터 구조를 통해 구현되므로 Redis 요소를 추가 할 때마다 O (log (N)) 작업을 수행합니다. 좋습니다. 하지만 정렬 된 요소를 요청할 때 Redis는 작업을 전혀 수행 할 필요가 없습니다. 이미 모두 정렬되어 있습니다.


> zrange hackers 0 -1
1) "Alan Turing"
2) "Hedy Lamarr"
3) "Claude Shannon"
4) "Alan Kay"
5) "Anita Borg"
6) "Richard Stallman"
7) "Sophie Wilson"
8) "Yukihiro Matsumoto"
9) "Linus Torvalds"


참고 : 0 및 -1은 요소 인덱스 0에서 마지막 요소까지를 의미합니다 (-1은 LRANGE 명령의 경우와 마찬가지로 여기서 작동 함).

막내부터 나이까지 반대로 주문하려면 어떻게 해야 하나요? ZRANGE 대신 ZREVRANGE를 사용하십시오.


> zrevrange hackers 0 -1
1) "Linus Torvalds"
2) "Yukihiro Matsumoto"
3) "Sophie Wilson"
4) "Richard Stallman"
5) "Anita Borg"
6) "Alan Kay"
7) "Claude Shannon"
8) "Hedy Lamarr"
9) "Alan Turing"


WITHSCORES 인수를 사용하여 점수를 반환 할 수도 있습니다.


> zrange hackers 0 -1 withscores
1) "Alan Turing"
2) "1912"
3) "Hedy Lamarr"
4) "1914"
5) "Claude Shannon"
6) "1916"
7) "Alan Kay"
8) "1940"
9) "Anita Borg"
10) "1949"
11) "Richard Stallman"
12) "1953"
13) "Sophie Wilson"
14) "1957"
15) "Yukihiro Matsumoto"
16) "1965"
17) "Linus Torvalds"
18) "1969"


Operating on ranges 


정렬 된 세트는 이보다 더 강력합니다. 범위에서 작동 할 수 있습니다. 1950 년까지 태어난 모든 개인을 포함합시다. 이를 위해 ZRANGEBYSCORE 명령을 사용합니다.


> zrangebyscore hackers -inf 1950
1) "Alan Turing"
2) "Hedy Lamarr"
3) "Claude Shannon"
4) "Alan Kay"
5) "Anita Borg"


우리는 Redis에게 음의 무한대와 1950 사이의 점수를 가진 모든 요소를 ​​반환하도록 요청했습니다 (두 극단 모두 포함됨).

요소 범위를 제거 할 수도 있습니다. 정렬 된 집합에서 1940 년에서 1960 년 사이에 태어난 모든 해커를 제거해 보겠습니다.


> zremrangebyscore hackers 1940 1960
(integer) 4


ZREMRANGEBYSCORE는 아마도 최상의 명령 이름은 아니지만 매우 유용 할 수 있으며 제거 된 요소의 수를 반환합니다.

정렬 된 집합 요소에 대해 정의 된 또 다른 매우 유용한 작업은 순위 가져 오기 작업입니다. 정렬 된 요소 집합에서 요소의 위치가 무엇인지 물어볼 수 있습니다.


> zrank hackers "Anita Borg"
(integer) 4


내림차순으로 정렬 된 요소를 고려하여 순위를 얻기 위해 ZREVRANK 명령을 사용할 수도 있습니다.


Lexicographical scores 


최신 버전의 Redis 2.8에서는 정렬 된 집합의 요소가 모두 동일한 점수로 삽입된다고 가정하고 범위를 사전식으로 가져올 수 있는 새로운 기능이 도입되었습니다 (요소는 C memcmp 함수와 비교되므로 모든 Redis 인스턴스가 동일한 출력으로 응답합니다).

사전식 범위로 작동하는 주요 명령은 ZRANGEBYLEX, ZREVRANGEBYLEX, ZREMRANGEBYLEXZLEXCOUNT입니다.

예를 들어 유명한 해커 목록을 다시 추가해 보겠습니다.하지만 이번에는 모든 요소에 0 점을 사용합니다.


> zadd hackers 0 "Alan Kay" 0 "Sophie Wilson" 0 "Richard Stallman" 0
  "Anita Borg" 0 "Yukihiro Matsumoto" 0 "Hedy Lamarr" 0 "Claude Shannon"
  0 "Linus Torvalds" 0 "Alan Turing"


정렬 된 세트 순서 규칙으로 인해 이미 사전 순으로 정렬되어 있습니다.


> zrange hackers 0 -1
1) "Alan Kay"
2) "Alan Turing"
3) "Anita Borg"
4) "Claude Shannon"
5) "Hedy Lamarr"
6) "Linus Torvalds"
7) "Richard Stallman"
8) "Sophie Wilson"
9) "Yukihiro Matsumoto"


ZRANGEBYLEX를 사용하여 사전식 범위를 요청할 수 있습니다.


> zrangebylex hackers [B [P
1) "Claude Shannon"
2) "Hedy Lamarr"
3) "Linus Torvalds"


범위는 포함 또는 제외 (첫 번째 문자에 따라 다름)가 될 수 있으며, + 및 -문자열을 사용하여 무한 문자열과 무한 무한 문자열이 각각 지정됩니다. 자세한 내용은 설명서를 참조하십시오.


이 기능은 정렬 된 집합을 일반 색인으로 사용할 수 있기 때문에 중요합니다. 예를 들어 128 비트 부호 없는 정수 인수로 요소를 인덱싱 하려면 점수가 같지만 (예 : 0) 128 비트로 구성된 16 바이트 접두사가 있는 정렬 된 집합에 요소를 추가하기 만하면 됩니다. 빅 엔디안의 비트 수. 빅 엔디안의 숫자는 사전 식 (원시 바이트 순서)으로 정렬 될 때 실제로 숫자로도 정렬되므로 128 비트 공간의 범위를 요청하고 접두사를 버리는 요소의 값을 가져올 수 있습니다.


더 심각한 데모의 맥락에서 기능을 보려면 Redis 자동 완성 데모를 확인하십시오.


Updating the score: leader boards 


다음 주제로 전환하기 전에 정렬 된 세트에 대한 마지막 메모입니다. 정렬 된 세트의 점수는 언제든지 업데이트 할 수 있습니다. 정렬 된 세트에 이미 포함 된 요소에 대해 ZADD를 호출하면 O (log (N)) 시간 복잡도로 점수 (및 위치)가 업데이트 됩니다. 따라서 정렬 된 세트는 많은 업데이트가 있을 때 적합합니다.

이러한 특성 때문에 일반적인 사용 사례는 리더 보드입니다. 일반적인 응용 프로그램은 상위 N 명의 사용자와 리더 보드의 사용자 순위 (예 : " 당신은 여기에서 # 4932 최고 점수입니다. ").


Bitmaps 


비트 맵은 실제 데이터 유형이 아니라 문자열 유형에 정의 된 비트 지향 연산 집합입니다. 문자열은 이진 안전 Blob이고 최대 길이는 512MB이므로 최대 232 개의 서로 다른 비트를 설정하는 데 적합합니다.

비트 연산은 두 그룹으로 나뉩니다. 비트를 1 또는 0으로 설정하거나 값을 가져 오는 것과 같은 일정한 시간 단일 비트 연산과 비트 그룹에 대한 연산 (예 : 주어진 비트 범위에서 설정된 비트 수 계산) (예 : 인구 계산).

비트 맵의 ​​가장 큰 장점 중 하나는 정보를 저장할 때 종종 극도의 공간 절약을 제공한다는 것입니다. 예를 들어 여러 사용자가 증분 사용자 ID로 표시되는 시스템에서 512MB의 메모리를 사용하여 40 억 명의 사용자에 대한 단일 비트 정보 (예 : 사용자가 뉴스 레터 수신 여부를 알 수 있음)를 기억할 수 있습니다.

비트는 SETBITGETBIT 명령을 사용하여 설정 및 검색됩니다.


> setbit key 10 1
(integer) 1
> getbit key 10
(integer) 1
> getbit key 11
(integer) 0


SETBIT 명령은 첫 번째 인수로 비트 번호를, 두 번째 인수로 비트를 설정할 값 (1 또는 0)을 취합니다. 명령은 주소 지정된 비트가 현재 문자열 길이를 벗어나면 문자열을 자동으로 확대합니다.

GETBIT는 지정된 인덱스의 비트 값을 반환합니다. 범위를 벗어난 비트 (대상 키에 저장된 문자열 길이를 벗어난 비트 주소 지정)는 항상 0으로 간주됩니다.

비트 그룹에서 작동하는 세 가지 명령이 있습니다.

  1. BITOP는 서로 다른 문자열 간에 비트 단위 연산을 수행합니다. 제공된 연산은 AND, OR, XOR 및 NOT입니다.
  2. BITCOUNT는 모집단 계산을 수행하여 1로 설정된 비트 수를 보고 합니다.
  3. BITPOS는 지정된 값이 0 또는 1 인 첫 번째 비트를 찾습니다.

BITPOSBITCOUNT 모두 문자열의 전체 길이에 대해 실행하는 대신 문자열의 바이트 범위로 작동 할 수 있습니다. 다음은 BITCOUNT 호출의 간단한 예입니다.


> setbit key 0 1
(integer) 0
> setbit key 100 1
(integer) 0
> bitcount key
(integer) 2


비트 맵의 ​​일반적인 사용 사례는 다음과 같습니다.

  • 모든 종류의 실시간 분석.
  • 공간 효율적이지만 객체 ID와 관련된 고성능 부울 정보를 저장합니다.

예를 들어 웹 사이트 사용자의 일일 방문 중 가장 긴 연속을 알고 싶다고 가정 해보십시오. 0부터 시작하여 웹 사이트를 공개 한 날부터 시작하고 사용자가 웹 사이트를 방문 할 때마다 SETBIT로 조금 설정합니다. 비트 인덱스로 현재 유닉스 시간을 취하고 초기 오프셋을 뺀 다음 하루의 초 수 (일반적으로 3600 * 24)로 나눕니다.

이렇게 하면 각 사용자에 대해 매일의 방문 정보가 포함 된 작은 문자열이 생깁니다. BITCOUNT를 사용하면 주어진 사용자가 웹 사이트를 방문한 일수를 쉽게 얻을 수 있으며, 몇 번의 BITPOS 호출 또는 단순히 비트 맵 클라이언트 측을 가져와 분석하여 가장 긴 행진을 쉽게 계산할 수 있습니다.

비트 맵은 예를 들어 데이터 세트를 분할하기 위해 여러 키로 분할하기가 쉽지 않으며 일반적으로 큰 키로 작업하는 것을 피하는 것이 더 낫기 때문입니다. 모든 비트를 키로 설정하는 대신 비트 맵을 여러 키로 분할하려면 키당 M 비트를 저장하고 비트 번호 / M으로 키 이름을 얻고 비트로 키 내부에서 주소를 지정할 N 번째 비트를 얻는 것이 간단한 전략입니다. -번호 MOD M.


HyperLogLogs 


HyperLogLog는 고유 한 항목을 계산하는 데 사용되는 확률 적 데이터 구조입니다 (기술적으로 이것은 집합의 카디널리티 추정이라고 함). 일반적으로 고유 항목을 계산하려면 계산하려는 항목 수에 비례하는 메모리 양을 사용해야 합니다. 여러 번 계산하지 않으려면 과거에 이미 본 요소를 기억해야 하기 때문입니다. 그러나 정밀도를 위해 메모리를 교환하는 알고리즘 세트가 있습니다. 표준 오류로 추정 된 측정 값으로 끝납니다. 이는 Redis 구현의 경우 1 % 미만입니다. 이 알고리즘의 마법은 계산 된 항목 수에 비례하는 메모리 양을 더 이상 사용할 필요가 없고 대신 일정한 양의 메모리를 사용할 수 있다는 것입니다! 최악의 경우 12k 바이트 또는 HyperLogLog (지금부터 HLL이라고 부름)에 요소가 거의 없는 경우 훨씬 적습니다.


Redis의 HLL은 기술적으로 다른 데이터 구조이지만 Redis 문자열로 인코딩되므로 GET을 호출하여 HLL을 직렬화 하고 SET를 호출하여 다시 서버로 역 직렬화 할 수 있습니다.

개념적으로 HLL API는 세트를 사용하여 동일한 작업을 수행하는 것과 같습니다. 관찰 된 모든 요소를 ​​집합에 SADD하고 SCARD를 사용하여 집합 내의 요소 수를 확인합니다. 이는 SADD가 기존 요소를 다시 추가하지 않기 때문에 고유합니다.

실제로 HLL에 항목을 추가하지는 않지만 데이터 구조에는 실제 요소를 포함하지 않는 상태 만 포함되기 때문에 API는 동일합니다.

새 요소를 볼 때마다 PFADD를 사용하여 계수에 추가합니다.

지금까지 PFADD로 추가 된 고유 요소의 현재 근사치를 검색 할 때마다 PFCOUNT를 사용합니다.


> pfadd hll a b c d
(integer) 1
> pfcount hll
(integer) 4


이 데이터 구조에 대한 사용 사례의 예로는 매일 검색 양식에서 사용자가 수행하는 고유 한 쿼리를 계산합니다.

Redis는 HLL의 통합도 수행 할 수 있습니다. 자세한 내용은 전체 문서를 확인하세요.


Other notable features 


Redis API에는 이 문서의 맥락에서 살펴볼 수 없지만 주의 할 가치가 있는 다른 중요한 사항이 있습니다.

Learn more 


이 튜토리얼은 완전하지 않으며 API의 기초만 다루었습니다. 더 많은 정보를 얻으려면 명령 참조를 읽으십시오.

읽어 주셔서 감사합니다. Redis로 재미있게 해킹하세요!


https://redis.io/topics/data-types-intro