- 
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으로 락 사용하기?