From ec0ee5ece69740d10ae5cd5d3950e080c59a156c Mon Sep 17 00:00:00 2001 From: JunSik Sim Date: Sun, 10 Dec 2023 19:14:56 +0900 Subject: [PATCH 1/2] week03_junsik --- redis/week03/junsik/use_redis.md | 77 ++++++++ redis/week03/junsik/use_redis_patterns.md | 216 ++++++++++++++++++++++ 2 files changed, 293 insertions(+) create mode 100644 redis/week03/junsik/use_redis.md create mode 100644 redis/week03/junsik/use_redis_patterns.md diff --git a/redis/week03/junsik/use_redis.md b/redis/week03/junsik/use_redis.md new file mode 100644 index 0000000..f2fc6a2 --- /dev/null +++ b/redis/week03/junsik/use_redis.md @@ -0,0 +1,77 @@ +# Keyspace +- Redis key 는 binary safe 하며 어떤 binary sequence 든 key 로 사용할 수 있다. empty string 도 valid key 다. +- 길이가 너무 긴 key 는 적합하지 않다. + - 쓸데없이 메모리만 많이 잡아먹기 때문 +- 길이가 너무 짧은 key 도 적합하지 않다. + - 너무 줄이느라 readable 하지 않을 수 있기 때문 +- `object-type:id` 의 형태가 가장 적합한 best practice +- maximum key size 는 512MB +- command + - set {key_name} {value} + - exists + - del + - type +- 만료기한 설정 가능(TTL: Time to live) + - expire {key_name} {expire_value} + - set {key_name} {value} ex {expire_value} + - ttl {key_name} +- 탐색 + - Scan + - Keys -> 사용 X + - 모든 key 를 return 할 때 까지 redis server 가 block 됨 + +# Client-side caching +- high performance 를 위해 redis 도 client-side caching 을 지원한다. +- DB resource 를 조회할 때 매번 DB 에 query 를 요청하지 않고, client application 메모리에 저장해두었다가 동일한 query 에 대한 결과가 저장되어 있다면 그걸 그대로 사용한다. +- client 측에서는 latency 가 줄어서 좋고, DB 는 query 요청을 덜 받아서 좋다. +- 하지만 DB 상에서는 데이터가 변경되었는데, client 측에서는 이전 데이터를 그대로 사용하는 문제가 발생한다. 그래서 redis 에서는 `Tracking` 을 제공한다. + - 두 가지 모드(`default`, `broadcasting`)가 존재한다. +``` +default mode protocol 예시 + +Client 1 -> Server: CLIENT TRACKING ON +Client 1 -> Server: GET foo +(The server remembers that Client 1 may have the key "foo" cached) +(Client 1 may remember the value of "foo" inside its local memory) +Client 2 -> Server: SET foo SomeOtherValue +Server -> Client 1: INVALIDATE "foo" +``` +- 위 방식은 client 가 많아질수록 server memory 사용량도 늘어나게 된다. 그래서 `Invalidation Table` 을 사용한다. + - 최대 entry 개수를 정해놓고, 새로운 key 가 들어왔을 때 공간이 충분하지 않다면 오래된 key 를 제거하는 방식이다. +- Two connections mode + - Redis 6에서 지원되는 새로운 버전의 Redis 프로토콜 RESP3을 사용하면 동일한 연결에서 데이터 쿼리를 실행하고 무효화 메시지를 수신할 수 있다. + - 두 개의 분리된 연결(하나는 데이터용, 다른 하나는 무효화 메시지용)을 사용하여 클라이언트측 캐싱을 구현할 수도 있다. +- 모든 key 에 대해 caching 하는 것은 부담이 될 수 있기 때문에, client 에서는 OPTION 명령어로 특정 key 만 caching 하게 할 수 있다. +- 아래 명령어는 기본적으로 읽기 쿼리에 언급된 키가 캐시되지 않는 것으로 간주하게 한다. +``` +CLIENT TRACKING on REDIRECT 1234 OPTIN +``` +- 그리고 client 측에서 caching 을 원한다면 아래처럼 특수 명령어를 먼저 실행해야 한다. +``` +CLIENT CACHING YES ++OK +GET foo +"bar" +``` +- `broadcasting` 모드는 server memory 사용량을 줄이는 데에 초점이 있다. + - 아래 명령어를 사용하면 모든 key 를 tracking 할 수 있다. + ``` + CLIENT TRACKING on BROADCAST + ``` + - 또한, prefix 를 사용하여 특정 패턴을 만족하는 것들만 tracking 할 수도 있다. +- `NOLOOP` 옵션을 사용하면 무효화 메시지를 무시할 수 있다. +- server 와 connection 이 끊어졌을 때 대처 방법 + - 연결이 끊어지면 local cache 가 flush 되는지 확인한다. + - Pub/Sub와 함께 RESP2를 사용하거나 RESP3을 사용하는 경우 둘 다 무효화 채널에 주기적으로 ping 을 보낸다. 연결이 끊어진 것으로 보이고 ping back 을 수신할 수 없는 경우 최대 시간이 지난 후 연결을 닫고 cache 를 flush 한다. +- 어떤 것들을 caching 할지 잘 정해야 한다. + - 변화가 많은 key 들은 지양 + - 요청이 드문 key 들은 지양 + - 잘 변하지 않지만 요청이 많다면 best + +# Pipelining +- Redis Pipelining 은 개별 명령에 대한 응답을 기다리지 않고 한 번에 여러 명령을 실행하여 성능을 향상시키는 기술이다. +- Pipelining 을 사용하면 server 에서는 해당 response 들을 queue 에 저장해두기 때문에 메모리 사용에 대해 주의해야한다. + +# Keyspace notifications +- 클라이언트가 어떤 방식으로든 Redis 데이터 세트에 영향을 미치는 이벤트를 수신하기 위해 Pub/Sub 채널을 구독할 수 있다. +- 모든 명령은 대상 키가 `실제로 수정된 경우에만` 이벤트를 생성한다. diff --git a/redis/week03/junsik/use_redis_patterns.md b/redis/week03/junsik/use_redis_patterns.md new file mode 100644 index 0000000..ac52274 --- /dev/null +++ b/redis/week03/junsik/use_redis_patterns.md @@ -0,0 +1,216 @@ +# Bulk Loading +- 대량의 기존 데이터를 Redis에 로딩하는 프로세스 +- 일반적인 Redis 클라이언트를 사용하여 대량 로드를 수행하는 것은 좋지 않다. +- `keyN -> ValueN' 형식의 수십억 개의 키가 있는 대규모 데이터 세트를 생성해야 하는 경우 Redis protocol 형식으로 다음 명령을 포함하는 파일을 생성하고 파이프 모드를 사용하여 명령을 실행한다 +``` +SET Key0 Value0 +SET Key1 Value1 +... +SET KeyN ValueN +``` +``` +* netcat 명령어로도 할 수 있지만, 모든 데이터가 언제 전송되었는지 실제로 알지 못하고 오류를 확인할 수 없기 때문에 --pipe 를 통해 하는 것을 권장 + +cat data.txt | redis-cli --pipe +``` +- Redis protocol 은 아래처럼 생성할 수 있다 +``` +* +$ + + +... + +``` +``` +예시 + +*3 +$3 +SET +$3 +key +$5 +value + +--- + +*3: 이 줄은 명령이 세 부분으로 구성되어 있음을 나타냅니다 (SET, key, value). *는 배열을 나타내며, 3은 배열의 요소 개수입니다. 는 캐리지 리턴(\r)과 라인 피드(\n)를 나타내며, 새로운 라인의 시작을 의미합니다. + +$3: 이 줄은 첫 번째 인자의 길이를 나타냅니다. 여기서 $는 문자열의 길이를 나타내는 데 사용되며, 3은 문자열 SET의 길이입니다. + +SET: 명령의 첫 번째 부분인 SET입니다. + +$3: 두 번째 인자의 길이를 나타냅니다. 이 경우 key라는 문자열의 길이는 3입니다. + +key: 명령의 두 번째 부분인 키 key입니다. + +$5: 세 번째 인자의 길이를 나타냅니다. 이 경우 value라는 문자열의 길이는 5입니다. + +value: 명령의 세 번째 부분인 값 value입니다. +``` +- `--pipe` 모드가 내부적으로 동작하는 방식 + - netcat만큼 빠르면서도 동시에 서버에서 마지막 응답이 언제 전송되었는지 이해할 수 있어야 한다는 것 + - 아래와 같은 방법으로 동작됨 + - redis-cli --pipe는 가능한 한 빨리 서버에 데이터를 전송하려고 시도한다. + - 동시에 가능한 경우 데이터를 읽고 구문 분석 시도한다. + - stdin에서 더 이상 읽을 데이터가 없으면 임의의 20바이트 문자열이 포함된 특별한 ECHO 명령을 보낸다. + - 바이트를 대량 응답으로 보낸다. + - 최종 명령이 전송되면 응답을 수신하는 코드는 응답을 이 20바이트와 일치시키기 시작한다. + - 일치하는 응답에 도달하면 성공하여 종료된다. + +# Distributed locks +- 분산락은 서로 다른 프로세스가 상호 배타적인 방식으로 공유 리소스를 사용하여 작동해야 하는 많은 환경에서 매우 유용한 기본 요소다. + +## Why Failover-based Implementations Are Not Enough +- Redis를 사용하여 리소스를 잠그는 가장 간단한 방법은 인스턴스에 키를 생성하는 것 +- replica 를 만들더라도 아래 문제가 발생할 수 있다. +``` +1. 클라이언트 A는 마스터에서 잠금을 획득합니다. +2. 키에 대한 쓰기가 복제본으로 전송되기 전에 마스터가 충돌합니다. +3. 복제본이 마스터로 승격됩니다. +4. 클라이언트 B는 A가 이미 잠금을 보유하고 있는 동일한 리소스에 대한 잠금을 획득합니다. +``` + +## Correct Implementation with a Single Instance +- 키가 존재하고 키에 저장된 값이 정확히 내가 기대하는 값인 경우에만 키를 제거하라는 스크립트를 사용하여 안전한 방법으로 잠금을 해제하게 한다. +- 이는 다른 클라이언트가 생성한 잠금이 제거되는 것을 방지하기 위해 중요하다. + +## The Redlock Algorithm +Redlock 알고리즘의 작동 원리 +- 여러 Redis 인스턴스 사용: Redlock 알고리즘은 여러 개의 독립적인 Redis 인스턴스 (일반적으로 5개)를 사용한다. 이 인스턴스들은 서로의 상태나 데이터를 공유하지 않는다. +- 락 시도: 클라이언트는 모든 Redis 인스턴스에 동시에 락을 획득하려고 시도한다. 각 인스턴스에서는 SET 명령어와 NX (키가 존재하지 않을 때만 설정), PX (락의 유효 시간을 밀리초 단위로 설정) 옵션을 사용한다. +- 다수결 원칙: 클라이언트는 전체 Redis 인스턴스 중 과반수 이상에서 락을 성공적으로 획득하고, 전체 시도 시간이 락의 유효 시간의 절반을 넘지 않았을 때만 락을 성공적으로 획득한 것으로 간주한다. +- 락 해제: 작업이 완료되면, 클라이언트는 모든 Redis 인스턴스에서 락을 해제한다. +- 락 획득 실패: 만약 과반수 이상에서 락을 획득하는 데 실패하거나, 락 획득 시간이 너무 길면 클라이언트는 모든 인스턴스에서 락 획득 시도를 취소(해제)하고 잠시 후 다시 시도한다. + +# Secondary indexing +- Redis는 데이터 구조 서버이므로 복합(다중 열) 인덱스를 포함하여 다양한 종류의 보조 인덱스를 생성하기 위해 해당 기능을 인덱싱에 사용할 수 있다. +## Simple numerical indexes with sorted sets +``` +ZADD myindex 25 Manuel +ZADD myindex 18 Anna +ZADD myindex 35 Jon +ZADD myindex 67 Helen + +ZRANGE myindex 20 40 BYSCORE +``` +## Using objects IDs as associated values +``` +HMSET user:1 id 1 username antirez ctime 1444809424 age 38 +HMSET user:2 id 2 username maria ctime 1444808132 age 42 +HMSET user:3 id 3 username jballard ctime 1443246218 age 33 + +ZADD user.age.index 38 1 +ZADD user.age.index 42 2 +ZADD user.age.index 33 3 +``` +## Updating simple sorted set indexes +- ZADD 은 다른 점수와 동일한 값을 가진 요소를 다시 추가하면 단순히 점수를 업데이트하고 요소를 올바른 위치로 이동하므로 간단한 인덱스 업데이트를 매우 간단히 할 수 있다. +``` +HSET user:1 age 39 +ZADD user.age.index 39 1 +``` +- 선형 방식으로 다차원적인 것을 효율적으로 표현할 수 있다면, 다차원 데이터도 인덱스로 쓸 수 있다. +## Lexicographical indexes +``` +ZADD myindex 0 baaa +ZADD myindex 0 abbb +ZADD myindex 0 aaaa +ZADD myindex 0 bbbb + +ZRANGE myindex 0 -1 +1) "aaaa" +2) "abbb" +3) "baaa" +4) "bbbb" + +ZRANGE myindex [a (b BYLEX +1) "aaaa" +2) "abbb" +``` +- 아래 명령어 예시처럼 자동완성을 구현할 수 있다 +``` +ZRANGE myindex "[bit" "[bit\xff" BYLEX +``` +- 위의 예시에서 아래처럼 빈도수 로직을 추가할 수도 있다 +``` +ZRANGE myindex "[banana:" + BYLEX LIMIT 0 1 + +ZREM myindex 0 banana:1 +ZADD myindex 0 banana:2 + +동시 업데이트가 있을 수 있기 때문에 위의 세 가지 명령은 대신 Lua 스크립트를 통해 전송되어야 한다. +``` +## Normalizing strings for case and accents +``` +ZADD myindex 0 {normalized}:{frequency}:{original} +``` + +## Adding auxiliary information in the index +``` +ZADD myindex 0 {mykey}:{myvalue} +``` +- 콜론 문자는 키 자체의 일부일 수 있으므로 추가한 키와 충돌하지 않도록 선택해야 한다. + +## Numerical padding +- 문자열 숫자에 패딩값을 넣음으로써 결과적으로 숫자 값을 기준으로 정렬되게 할 수 있다 +``` +ZADD myindex 0 00324823481:foo +ZADD myindex 0 12838349234:bar +ZADD myindex 0 00000000111:zap + +ZRANGE myindex 0 -1 +1) "00000000111:zap" +2) "00324823481:foo" +3) "12838349234:bar" +``` +``` +01000000000000.11000000000000 +01000000000000.02200000000000 +00000002121241.34893482930000 +00999999999999.00000000000000 +``` + +## Composite indexes +``` +ZADD myindex 0 0056:0028.44:90 +ZADD myindex 0 0034:0011.00:832 + +ZRANGE myindex [0056:0010.00 [0056:0030.00 BYLEX +``` + +## Updating lexicographical indexes +- 더 많은 메모리를 사용하면서 인덱스 처리를 단순화 하는 방법이다. +- 항상 필요한 것은 아니지만 인덱스 업데이트 작업을 단순화 할 수 있다. +``` +MULTI +ZADD myindex 0 0056:0028.44:90 +HSET index.content 90 0056:0028.44:90 +EXEC +``` + +## Representing and querying graphs using a hexastore +- `o`bjects, formed by a `s`ubject, a `p`redicate and an object +``` +antirez is-friend-of matteocollina + +ZADD myindex 0 spo:antirez:is-friend-of:matteocollina +ZADD myindex 0 sop:antirez:matteocollina:is-friend-of +ZADD myindex 0 ops:matteocollina:is-friend-of:antirez +ZADD myindex 0 osp:matteocollina:antirez:is-friend-of +ZADD myindex 0 pso:is-friend-of:antirez:matteocollina +ZADD myindex 0 pos:is-friend-of:matteocollina:antirez + +ZRANGE myindex "[spo:antirez:is-friend-of:" "[spo:antirez:is-friend-of:\xff" BYLEX +``` + +## Multi dimensional indexes + + +## Non range indexes + + + +# Patterns examples + From 60a2dadf120ad84eacb746aa0c39dbdea70608e8 Mon Sep 17 00:00:00 2001 From: JunSik Sim Date: Sun, 17 Dec 2023 18:01:33 +0900 Subject: [PATCH 2/2] week03_junsik --- redis/week03/junsik/use_redis_patterns.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/redis/week03/junsik/use_redis_patterns.md b/redis/week03/junsik/use_redis_patterns.md index ac52274..1d1369f 100644 --- a/redis/week03/junsik/use_redis_patterns.md +++ b/redis/week03/junsik/use_redis_patterns.md @@ -206,11 +206,3 @@ ZRANGE myindex "[spo:antirez:is-friend-of:" "[spo:antirez:is-friend-of:\xff" BYL ``` ## Multi dimensional indexes - - -## Non range indexes - - - -# Patterns examples -