-
Redis는 싱글 스레드다
-
싱글 스레드 기반이므로 하나의 명령어가 오래걸린다면 이는 적합하지 않다.
-
서버에서
keys
명령을 사용하지 말자 -
flushall
,flushdb
명령을 주의하자-
flushdb
: Redis는 db란느 가상의 공간을 분리할 수 있는 개념을 제공. 이를 모두 지우는 행위 -
flushall
: 모든 db를 지우는 행위 -
flushall
은 지우는데keys
명령어와 같이 많이 시간이 걸린다. 실제로 모든 데이터를 지우므로O(n)
이 걸린다.
-
-
-
-
Redis Persistent
-
Memcached와 달리 Redis는 디스크에 저장된 데이터를 기반으로 다시 복구하는게 가능하다. 이런 Persistent 기능은 데이터 스토어로서 장점이 있지만 이 기능 때문에 장애의 주 원인이 되기도 한다.
-
RDB, AOF 가능을 알아보로 장애를 피하자
-
RDB: RDBMS라는 오해를 많이 받지만, 단순히 메모리의 스냅샷을 파일 형태로 저장할 때 쓰는 파일의 확장자명이다
-
SAVE 방식은 모든 작업을 멈추로 RDB 파일을 생성. 50GB 메모리 저장한다면 7~8분 소요
-
BGSAVE 방식은 fork() 명령을 통해 자식 프로세스를 생성하고 현재 매모리의 상태가 복제된 상태에서 데이터를 저장
-
-
AOF(Append Only File): 데이터를 저장하기 전에 AOF 파일에 현재 수행할 명령어를 저장해놓고 장애가 발생하면 AOF를 기반으로 복구하는 것
-
-
BGSAVE 방식에서 fork() 를 사용하면 COW(Copy On Write) 기술을 이용해서 부모 프로세스의 메모리에서 실제로 변경이 필요한 부분을 복사한다. 이때 메모리를 두 배로 사용하는 경우가 발생할 수 있다.
-
메모리 정책
-
volatile-lru: EXPIRE SET 안에 있는 키를 LRU 알고리즘 기반으로 키 삭제
-
allkeys-lru: LRU 알고리즘 기반으로 키 삭제
Note
|
LRU
least recently used algorithm. 가장 오랫동안 참조되지 않는 페이지를 교체하는 방식 |
pub/sub
-
Redis는 Key-Value 기능 외에도 pub/sub을 지원한다.
redis > SUBSCRIBE channel [channel ...] redis > PUBLISH channel message
-
pub/sub는 키 공간과 관련이 없으며 DB 번호를 포함하여 어떤 레벨에서도 방해받지 않도록 설계
-
패턴매칭도 가능:
PSUBSCRIBE news.*
-
다른 메세지 브로커와는 다르게 메시지 지속성이 없음. 메세지 전송후 삭제.
클라이언트
-
redis-cli
$ brew install redis $ redis-cli -h $host -p $port > config get *
자료구조
String
> set hello world
OK
> get hello
"world"
> exists hello
(integer) 1
> del hello
(integer) 1
> exists hello
(integer) 0
> set counter 100
OK
> get counter
"100"
> incr counter
(integer) 101
> incr counter
(integer) 102
> incrby counter 50
(integer) 152
> incr count
1
> getset count 0
"1"
> get count
"0"
> del count
(integer) 1
> get count
(nil)
-
SET
으로 값을 넣을 때 이미 값이 있으면 덮어씀 - docIf key already holds a value, it is overwritten, regardless of its type.
List
-
linked list
-
pub-sub 패턴으로 활용
Hash
> hmget user2 email country
1) "id@domain.com"
2) "Korea"
Set
> sadd partner:visa:merchants 3212 1231 (1)
(integer) 2
> smembers partner:visa:merchants (2)
1) "3212"
2) "1231"
> scard partner:visa:merchants (3)
(integer) 2
> sismember partner:visa:merchants 3212 (4)
(integer) 1
> srandmember partner:visa:merchants (5)
"1231"
> srandmember partner:visa:merchants 2 (6)
1) "1231"
2) "3212"
> srandmember partner:visa:merchants -2 (7)
1) "1231"
2) "1231"
> spop
> stem
> smove
> sinter
> sinterstore
> sdiff
> sdiffstore
> sunion
> sunionstore
-
set add
-
set members
-
set cardinality
-
if is member, return 1(
true
). otherwise, return 0(false
) -
set random member
-
multiple random
-
multiple random(duplicate)
The max number of members in a set is 232 - 1 (4294967295, more than 4 billion of members per set).
Sorted Set
key naming
object-type#id:data
partner:user#123:name
partner:merchant#123:bno
partner:merchant#123
merchant#121231:base-url
users // (1)
-
`users`에 user 키를 모두 저장하는 list 혹은 set
batch
bulk insert cat data.txt | redis-cli --pipe
using pipe mode
transaction
MULTI
INCR id:users
SET user:{id} '{"name": "yj","age": 30}'
SADD users {id}
EXEC
-
https://dark0096.github.io/redis/2018/10/27/redis-transaction.html
-
cluster 모드에서는 multi, exec 안됨..
데이터 설계
-
모든 데이터를 키에 저장할 수 있는가?
-
키만 조회하여 업무를 처리할 수 있도록 구성
-
-
자료구조로 구현이 가능한가
-
여러개의 명령어를 사용해도 실행시간이 O(1)인지
-
우리에겐 lua가 있다
-
-
데이터 사용 성향에 따라 다른 데이터 구조 선택 필요
-
빠른 쓰기가 필요한지 빠른 읽기가 필요한지
-
-
단순한 데이터 조회 패턴을 가지는가?
-
where 절 없음
-
-
숫자 데이터가 많은가?
-
카운터와 같은 숫자 데이터 저장에 강함
-
-
lua 사용시 전체 시간 복잡도는 O(log n)을 초과하지 않도록 하라
lua 사용시 주의 사항
-
예측 불가능한 loop 사용하지 말것
-
루아 스크립트의 실행을 원자성을 가짐
-
-
에러 처리에 신경쓸것
-
조회한 데이터가 존재하는지 확인
-
delete vs. unlink
-
UNLINK
-
Redis 4.0에서 추가
-
DEL
과 다른 점은 비동기로 별도 스레드에서 백그라운드로 실행됨 -
컬렉션에 데이터가 많은
DEL
보다 빠름 -
키 삭제는 sync로 하고, 값 삭제를 별도 쓰레드에서 async로 처리.
(맴버수가 64개 이하일 경우DEL
과 같이 sync로 처리) -
메인 스레드는 백그라운드 스레드와 동기화를 해야하며 이것도 비용으로 볼 수 있음
-
-
DEL
-
블록킹 모드에서 값을 제거함
-
제거할 값이 클 경우(큰 리스트나 해시에 할당이 많을 경우) redis가 오랫동안 블락킹됨
-
이를 해결하기 위해 redis는 non-blocking' delete 로
UNLINK
를 제공함
-
-
UPDATE:
-
Redis 6.0부터 신규 설정이 추가됨 → lazyfree-lazy-user-del
-
해당 값을 true로 설정시
DEL
을UNLINK
와 같이 실행함
-
-
version check
$ telnet <ip> <port>
Trying <ip>...
Connected to <ip> (<ip>).
Escape character is '^]'.
info
$3506
# Server
redis_version:5.0.5
...
# Cluster
cluster_enabled:1
...
락
-
공유된 자원을 여러 스레드가 접근하는 것을 피하고자할 때 락을 사용함
-
분산락: 데이터베이스 등 공통된 저장소를 이용하여 자원이 사용 중인지 체크하는 것. 전체 서버에서 동기화된 처리를 가능하게 함
-
스핀락(spinlock): 임계 구역(critical section)에 진입이 불가능할 때 진입이 가능할 때까지 루프를 돌면서 재시도하는 방식으로 구현된 락
-
락을 획득한다는 것. "(1) 락이 존재하는지 확인, (2) 존재하지 않으면 락 획득." 이것을 atomic하게 처리.
분산 락
-
setnx
: 값이 존재하지 않으면 생성하는 연산자fun doProcess() { val lockKey = "lock" try { while (!tryLock(lockKey)) { // (2) try { Thread.sleep(50) } catch (e: InterruptedException) { throw RuntimeException(e) } } } finally { unlock(lockKey) } } fun tryLock(String key): Boolean { return command.setnx(key, "1") // (1) } fun unlock(key: String) { command.del(key) }
-
락의 타임아웃이 지정되지 않음
-
락을 획득하지 못하면 Excpetion이 발생하는데 무시됨
-
스핀락을 사용하면 레디스에 부담이 큼
-
-
-
Redisson으로 락 사용하기?