Redis에는 많은 데이터 타입이 존재하며 각각의 데이터 타입마다 명령어가 다르다.
- Strings
- Lists
- Sets
- Hashes
- Sorted Sets
- Streams
- Geospatial
- Bitmaps
- HyperLogLog
- BloomFilter
Strings
- Strings 문자열, 숫자, serialized object(JSON String)등 저장
- 명령어
- $ SET lecture tistroy → lecture라는 키로 String 타입의 값을 저장
- $ MSET price 100 language ko → 멀티셋의 약자로 다수의 String 값을 한번에 저장
- $ MGET lecture price language → 멀티겟은 다수의 키를 한번에 반환
- $ INCR price → increase의 약자로 숫자형 스트링 값을 1로 올릴 때 사용
- $ INCRBY price 10 → increaseby는 숫자형 스트링의 값에 특정 값을 더할 때 사용
- $ SET tistory '{"price": 100, "language": "ko"}' → JSON Object 형태의 키 벨류 데이터를 String으로 직접 저장 가능하며 사용할 때는 JSON 형태로 파싱해야함.
- $ SET tistory:ko:price 200 → 티스토리의 한국어버전의 가격을 200으로 하겠다. → 콜론을 사용하여 의미 단위별로 구분하여 키를 만든다.
List
- String을 Linked List로 저장하며 양쪽에서 데이터를 추가하거나 삭제하는 오퍼레이션에 최적화 되있다.
- Queue나 Stack 구현에 사용된다.
- 명령어
- 다음과 같이 Queue를 구현할 수 있다.
- $ LPUSH queue job1 jo2 job3
- $ RPOP queue
- 다음과 같이 Stack을 구현할 수 있다.
- $ LPUSH stack job1 jo2 job3
- $ LPOP stack
- 다수의 아이템을 조회할 때 인덱스로 조회 가능
- $ LPUSH stack job1 jo2 job3
- $ LRANGE queue -2 -1 → 뒤에서 두번째 아이템부터 뒤에서 첫번째 아이템 까지 아이템을 조회한다.
- $ LTIRM queue 0 0 → 0번 인덱스부터 0번 인덱스까지 남기고 나머지를 모두 삭제한다.
Sets
- Unique String을 저장하는 정렬되지 않은 집합
- Set Operation 사용 가능(e.g intersction, union, difference)
- 명령어
- $ SADD user:1 fruits apple banana orange orange
- SADD 명령어로 set에 아이템을 추가할 수 있다. orange가 중복이여도 set에 넣기 때문에 중복이 제거된다.
- $ SMEMBERS user:1 fruits → 모든 멤버를 출력한다.
- $ SCARD user:1:fruits → 셋의 카디널리티를 출력한다.
- $ SISMEMBER user:1 fruits banana → 특정 아이템이 set에 포함되어 있는지 확인한다.
- $ SADD user:2:fruits apple lemon
- $ SINTER user1:fruits user:2:fruits → User1과 User2의 공집합 조회
- $ SDIFF user1:fruits user:2:fruits → User1과 User2의 차집합 조회
- $ SUNION user1:fruits user:2:fruits → User1과 User2의 합집합 조회
- $ SADD user:1 fruits apple banana orange orange
Hashes
- field-value 구조를 갖는 데이터 타입으로 다양한 속성을 갖는 객체의 데이터를 저장할 때 유용하다.
- 명령어
- $ HSET lecture name tistory price 100 language ko → 다수의 field와 value를 한번에 지정
- $ HGET lecture name → 하나의 필드를 조회
- $ HMGET lecture price language invalid → 다수의 필드를 조회(없는 필드를 조회하면 nil 반환)
- $ HINCRBY lecture price 10 → 숫자형으로 저장된 필드의 String 값을 특정 값 만큼 더한다.
Sorted Sets
- 중복없이 유니크한 String value를 저장하는 Set과 유사하지만 Score 라는 추가적인 Field를 가지고 있어서 해당 Score를 통해 데이터를 미리 정렬하는 데이터 타입이다.
- 내부적으로 Skip List + Hash Table로 이루어져 있고, score 값에 따라 정렬 유지
- Score가 동일하다면 lexicographically(사전 편찬 순) 정렬 됨
- 명령어
- $ ZADD points 10 TeamA 10 TeamB 50 TeamC → Sorted Sets을 생성하고 각 팀의 점수를 저장
- $ ZRANGE point 0 -1 → 순서를 갖기 때문에 리스트와 마찬가지로 인덱스를 통해 접근 가능하며 0 -1로 처음부터 가장 마지막 아이템을 조회함
- $ ZRANGE point 0 -1 REV WITHSCORES → reverse with score 옵션을 주면 역순으로 반환하고 score와 함께 반환.
- $ZRANK points TeamA → 해당 아이템의 랭크를 반환.(Sorted Sets의 인덱스를 반환)
Streams
- append-only log에 consumer groups과 같은 기능을 더한 자료구조
- append-only log : 데이터베이스나 분산 시스템에 주로 사용되는 데이저 저장 알고리즘으로 데이터가 수정되거나 삭제되지 않고 항상 추가만 되는 구조를 갖는다.
- stream에 추가되는 이벤트 또는 메시지는 고유 아이디를 갖는다. 고유아이디는 스트림에 추가되는 시간과 순서를 기준으로 레디스에서 자동으로 할당되는데 이러한 고유한 아이디를 통해서 해당 entry를 읽을 때 상수의 시간 복잡도를 갖게된다.
- 분산 시스템의 다수 consumer에서 안전하게 메시지를 consuming할 수 있도록 consumer group 기능이 포함되있다.
- 앞서 List를 통해 구현했던 메시지 브로커 경우 다수의 consumer에서 메세지를 consuming할 때 동일한 메시지를 어려 번 처리하는 경우가 발생할 수 있는데 레디스 stream의 consumer group을 이용하면 다수의 consumer가 메시지를 처리하면서도 동일한 메시지를 중복 처리하는 문제를 쉽게 해결할 수 있다.
- 명령어
- $ XADD events * action like user_id 1 prodect_id 1 → 스트림의 entry를 추가하고 key 이후에 star(*) 옵션을 주면 레디슴의 자동으로 유니크한 읽기가 할당된다. 이후 해쉬와 같은 필드 밸류 형태로 메시지를 구성할 수 있다.
- $ XRANGE events - + → List와 유사하게 다수의 메시지를 조회할 수 있고 처리가 완료된 메시지는 x-명령어를 이용하여 streamd에서 제거할 수 있다
Geospatials
- 좌표를 저장하고 검색하는 데이터 타입. 거리 계산, 범위 탐색 등 지원
- 명령어
- $ GEOADD seoul:station 126.923917 37.556844 hong-dea 127.027583 37.497928 gang-nam
→ 강남역과 홍대역의 좌표를 저장(레디스에서는 경도를 앞에 위도를 뒤에 붙여줘야한다.) - $ GEODIST seoul:station hong-dae gang-nam KM
→ 강남역과 홍대역의 거리를 구할 수 있다. 옵션을 통해 출력하는 단위도 조정할 수 있음
- $ GEOADD seoul:station 126.923917 37.556844 hong-dea 127.027583 37.497928 gang-nam
Bitmaps
- 실제 데이터타입은 아니고 String에 binary operation을 적용한 것
- 최대 42억개 binary 데이터를 표현할 수 있음(2^32 = 4_294_967_296)
- 명령어
- $ SETBIT user:log-in:23-01-01 123 1
→ 유저가 식별할 수 있는 아이디를 가지고 있다고 할 때 하나의 유저를 하나의 offset으로 표현할 수 있다. - $ SETBIT user:log-in:23-01-01 456 1 → 456번 유저가 1일에 로그인 했다고 가정
- $ SETBIT user:log-in:23-01-02 123 1 → 123번 유저가 2일에 로그인 했다고 가정
- $ BITCOUNT user:log-in:23-01-01 → 1일에 로그인한 유저 수 확인 가능
- $ BITOP AND result user:log-in:23-01-01 user:log-in:23-01-02
→ BITOP명령어에 AND를 이용하면 1일과 2일에 모두 로그인한 유저를 확인할 수 있다. 결과는 result에 저장된다 - $ GETBIT result 123 → result의 123번 offset 데이터를 출력한다.
- $ SETBIT user:log-in:23-01-01 123 1
HyperLogLog
- 집합의 cardinality를 추정할 수 있는 확률형 자료구조
- 정확성을 일부 포기하는 대신 저장공간을 효율적으로 사용(평균 에러 0.81%)
- 따라서 매우 정확한 값을 알 필요가 없고 근삿값만 알아도 되는 경우 사용할 수 있다.
HyperLogLog의 원리
HyperLogLog는 멤버의 값을 해싱하여 bucket이라는 단위로 분류해서 해시 값에 맞게 표시한다. 동일한 아이템이 추가된 경우 동일한 해시를 갖기 때문에 카디널리티를 일정하게 계산할 수 있다. 다만 HyperLogLog는 근간에 확률적인 계산식이 들어가 있기 때문에 최종 결과 값은 실제와 다를 수 있다.
예를 들어 해시 충돌이 발생하는 경우 HyperLogLog는 정확하지 않은 카디널리티를 반환한다.
Set을 이용해서도 카디널리티를 계산할 수 있는데 Set은 중복 검사를 위해 실제 값을 메모리에 모두 저장한다. 만약 10000개의 아이템이 있다면 10000개를 다 메모리에 저장한다는 것.
HyperLogLog를 이용하면 상대적으로 매우 적은 메모리로 카디널리티를 계산할 수 있다. HyperLogLog는 실제 값을 저장하는 것이 아니기 때문에 모든 아이템을 다시 출력해야 하는 경우에는 활용할 수 없다.
명령어
- $ PFADD fruits apple orange grape kiwi → HyperLogLog에 멤버를 추가한다.
- $ PFCOUNT fruits → HyperLogLog의 카디널리티를 출력한다.
BloomFilter
- element가 집합 안에 포함되있는지 확인할 수 있는 확률형 자료구조. 흔히 멤버쉽 테스트라고 불리는 기능을 구현할 때 자주 사용된다.
- HyperLogLog와 마찬가지로 정확성을 일부 포기하는 대신 저장공간을 효율적으로 사용한다.
- false positive :
- BloomFilter는 값이 집합에 포함되지 않는다는 사실은 정확하게 확인할 수 있지만, 집합에 포함되지 않는 값이 존재한다고 잘못 말하는 false positive를 반환하는 경우가 있다.
- 실제 값을 저장하지 않기 때문에 매우 적은 메모리 사용
BloomFilter가 데이터를 저장하는 원리
BloomFilter는 값을 해싱하여 여러개의 해시 키를 만들어낸다. 여러 개의 키에 해당하는 위치를 블룸필터에 표시하게된다.
이후에 어떤 아이템이 존재하는 여부를 확인할 때는 다시 해시키를 생성하여 해당 위치를 확인하면 된다.
예를들어 사과와 오렌지를 BloomFilter에 먼저 추가한 다음 포도라는 값이 BloomFilter에 이미 있는지 확인해본다고 가정하면 포도의 경우 왼쪽 하나의 해시키는 표시가 되어 있지만 오른쪽 다른 해시키는 표시되어 있지 않기 때문에 해당 값이 집합에 포함되지 않았다는 것을 확인할 수 있다.
대신 문제가 되는 부분은 키위의 예시처럼 다른 해시키의 조합으로 키위에 해당하는 해당 키가 이미 표시된 경우이다.
이때는 fasle positive로 실제로는 해당 아이템이 집합에 포함되지 않지만 BloomFilter는 해당 아이템을 포함하고 있다고 잘못된 결과를 반환한다.
따라서 BloomFilter를 사용하는 경우 반드시 fasle positive에 대해서 알고 있어야 한다.
명령어
- $ BF.MADD fruits apple orange → 집합에 아이템을 추가한다.
- $ BF.EXIST fruits apple → 집에 포함되어 있는지 확인한다.
BloomFilter를 사용하기 위해서는 별도의 모듈을 설치 해야한다. 하지만 별도 설치 없이 redis-stack-server 이미지 안에 BloomFilter가 포함되어 있기 때문에 도커를 이용해서 실습을 진행했다.(6379 포트를 사용중이라 63790 포트 사용)
$ BF.EXISTS로 집합에 존재하는 apple을 입력하면 1, 존재하지 않는 banana를 입력하면 0이 반환되는 것을 확인할 수 있는데 지금은 아이템이 많지 않기 때문에 잘 판별하지만 아이템이 많다면 false positive가 발생할 수 있다.
항상 false positive가 발생할 수 있다는 것을 염두하고 별도의 대처를 해야한다.
false positive가 발생할 때, 없는 아이템을 있다고할 순 있지만 반대의 상황으로 반드시 있는 아이템이 없다고 반환하는 경우는 없다. 이 부분은 기억해두자.
'Redis' 카테고리의 다른 글
[Redis] Redis 설치 & 실행 (0) | 2023.11.05 |
---|
댓글