
1. Redis란?
Remote(원격의) Dictionary(key-value, 자바의 hash map) Server(서버)
Redis는 데이터베이스,캐시, 메시지 브로커 및 스트리밍 엔진으로 사용되는 오픈 소스(BSD 라이선스), 인메모리 데이터 구조 저장소 입니다.
Redis는 문자열 , 해시 , 목록 , 집합 , 범위 쿼리가 있는 정렬된 집합 , 비트맵 , 하이퍼로그 로그 , 지리 공간 인덱스 및 스트림 과 같은 컬렉션을 제공합니다 .
Redis에는 복제 , Lua 스크립팅 , LRU 축출 , 트랜잭션 및 다양한 수준의 디스크 지속성 이 내장되어 있으며 다음을 통해 고가용성을 제공합니다.
Redis Sentinel 및 Redis 클러스터를 통한 자동 파티셔닝을 제공합니다.
2. Redis Type
redis의 자료구조
Redis는 애플리케이션 성능을 향상시키는 데 도움이 되도록 MySQL 또는 PostgreSQL과 같은 다른 "실제" 데이터베이스 앞에서 캐시로 사용되는 인메모리 데이터베이스
메모리의 데이터를 읽고 조작하는 것이 SSD 또는 HDD를 사용하는 기존 데이터 저장소에서 가능한 것보다 훨씬 빠름
Redis 자료 저장 방식 - Key/Value 형태
2-1) String
String 자료형은 단순히 Key/Value 형태가 값을 저장.
# 데이터 저장 (set key value)
127.0.0.1:6379> set user ryan
# 데이터 조회 (get key)
127.0.0.1:6379> get user
2-2) Hashes
Redis Key 하나에 여러개의 Field와 Value가 존재
Hashes는 필드와 값의 쌍을 저장하는 데 사용되며, 주로 객체를 저장하는 데 적합
작은 해시는 ziplist로 저장되지만, 요소가 많아지면 hashtable로 전환되어 메모리 사용과 성능을 최적화
#데이터 저장(hset key field value)
127.0.0.1:6379> hset user name ryan
#데이터 조회(hget key field)
127.0.0.1:6379> hget user name
2-3) List
Redis의 Lists는 키에 연결된 문자열의 순서있는 컬렉션
메모리 내에서 양방향 연결 리스트로 구현
리스트가 작을 때는 ziplist라는 압축된 메모리 구조 사용, 커지면 연결 리스트로 전환하여 메모리 효율성과 성능을 균형있게 관리
#데이터 저장(lpush/rpush key value)
127.0.0.1:6379> lpush user ryan
127.0.0.1:6379> rpush user ryan2
#데이터 조회(lrange key 시작(인덱스) 끝(인덱스))
127.0.0.1:6379> lrange user 0 10
2-4) set
Set은 집합형 Data Type으로 정렬되지 않고 중복되지 않는 데이터를 저장
집합형 자료형이기 때문에 합집합, 교집합, 차집합과 같은 연산도 가능
# 데이터 저장 (sadd key value)
127.0.0.1:6379> sadd user ryan
# 다중 저장 (sadd key value value value)
127.0.0.1:6379> sadd user ryan ryan1 ryan2
# 데이터 조회 (smembers key)
127.0.0.1:6379> smembers user
# 임시 데이터
127.0.0.1:6379> sadd user1 ryan1 ryan2 ryan3 ryan4
127.0.0.1:6379> sadd user2 ryan2 ryan3 ryan4 ryan5 ryan6
#차집합 연산 (user1에만 포함된 정보를조회)
127.0.0.1:6379> sdiff user1 user2
ryan1
#차집합 연산 (user2에만 포함된 정보를조회)
127.0.0.1:6379> sdiff user2 user1
ryan5
ryan6
#교집합 (user1과 user2 공통정보 조회)
127.0.0.1:6379> sinter user1 user2
ryan3
ryan4
ryan2
#합집합 (user1과 user2 정보를 합친다.)
127.0.0.1:6379> sunion user1 user2
ryan2
ryan3
ryan5
ryan1
ryan4
ryan6
2-5) Sorted Sets
Set 자료구조는 value 값을 정렬하지 않는다.
Sorted Set 자료구조는 Set 자료구조와 다르게 value 값을 score 기준으로 정렬
#데이터 저장(zadd key score value)
127.0.0.1:6379> zadd user 0 ryan
127.0.0.1:6379> zadd user 1 ryan2
127.0.0.1:6379> zadd user 2 ryan5
127.0.0.1:6379> zadd user 5 ryan3
127.0.0.1:6379> zadd user 4 ryan4
#데이터 전체조회(zrange key 시작(score) 끝(score))
127.0.0.1:6379> zrange user 0 -1
ryan
ryan2
ryan5
ryan4
ryan3
2-6) Hyperloglogs
HyperLogLog는 매우 큰 데이터 세트에서 고유 요소의 개수(카디널리티)를 추정할 때 사용되는 효율적인 확률적 데이터 구조.
메모리 사용이 매우 적으며, 큰 데이터 세트의 카디널리티를 추정하는 데 유용하다.
- PFADD :HyperLogLog 데이터 구조에 새 요소를 추가하는 명령어. 요소가 이미 존재한다면 추가되지 않지만, 새로운 요소인 경우 HyperLogLog 구조를 업데이트하여 이 요소를 고려한다.
- PFCOUNT : HyperLogLog 데이터 구조에 저장된 고유 요소의 근사 개수를 반환한다. PFADD를 통해 요소를 추가한 후, 얼마나 많은 고유 요소가 있을지 추정하고자 할 때 사용한다.
- PFMERGE : 이 명령은 여러 HyperLogLog 구조를 하나로 합치는 데 사용된다. 이렇게 하면 복수의 데이터 세트에서 고유 요소의 개수를 통합하여 추정할 수 있다.
2-7) Geospatial indexes
Redis Geospatial indexes를 사용하여 좌표를 저장하고 검색
이 데이터 구조는 주어진 반경이나 경계 상자 내에서 가까운 지점을 찾는 데 유용
2-8) Streams
스트림은 추가 전용 데이터 구조
기본 쓰기 명령은 XADD 지정된 스트림에 새 항목을 추가
각 스트림 항목은 사전이나 Redis 해시와 유사한 하나 이상의 필드-값 쌍으로 구성
참고: https://redis.io/docs/latest/develop/data-types/
3. Redis-Server
3-1 aeMain()위주의 레디스 서버 전반적인 동작 과정
aeMain()
레디스 서버는 이벤트 루프의 형식으로 내/외부 요청을 처리한다. 이벤트 루프는 특정 시 간 동안 이벤트를 감지하고 처리하는 과정을 반복하는 것이다. 레디스 서버의 이벤트 루프 는 aeMain()/aeProcessEvents()에 의해 구현된다.
-이벤트 루프의 동작 과정

aeProcessEvents()
대부분, aeProcessEvents()에서 이벤트를 감지하고 처리하는 이벤트 루프가 구현된다.
beforesleep()
레디스 서버는 이벤트 루프에서 처리할 작업을 준비한다.
usUntilEarliestTimer()
레디스 서버는 가장 일찍 예약된 시간 이벤트의 경과 시간을 계산한다. 이 시간 동안 이벤 트 루프는 file event만 처리한다.
aeApiPoll()
주기적으로 file event를 monitoring하여, select라는 system call을 통해 이벤트 발생을 감지한다. system call은 user와 kernel의 인터페이스이고, kernel를 호출하기 때문에 같은 운영체제를 사용하는 다양한 플랫폼을 지원할 수 있다. 그리고 발생한 이벤트를 처리하고 그 개수를 반환한다.
aftersleep()
본격적인 이벤트 루프가 시작된다. 이 이후에는 readable, writable, barrier file들을 순 서대로 처리하고, processTimeEvents()를 통해 time event를 처리한다. 그리고 총 처리한 이벤트의 개수를 반환한다.
3-2 Redis의 명령어 처리
SET 명령어의 전반적인 동작 과정
-Call graph

위의 Call graph는 SET 명령어가 어떻게 만들어지고, redis-cli에서 SET 명령어를 입력했 을 때 어떤 식으로 redis-server에 전송되어 inmemoryDB에 저장되는지 전반적인 SET 명령어 처리 과정을 보여준다.
SET 명령어는 어떻게 만들어지는가?
-commands.def
{MAKE_CMD(“set”, ......)}이라는 코드를 통해 set.json 파일을 읽는다.
-(redis-cli)commands.def, (redis-server)commands.c
redis-cli에서는 cli-commands.c를, redis-server에서는 commands.c를 통해 commands.def 를 읽는다.
-(redis-cli)redis-cli.c, (redis-server)server.c
redis-cli.c에서는 cliAddCommandDocArg()를, server.c에서는 populateCommandTable()을 통해 각자의 Command table에 SET 명령어를 추가한다.
SET 명령어 입력했을 때 inmemoryDB에 저장되는 과정
(redis-cli)redis-cli.c
redis-cli.c에서는 cliWaitForMessagesOrStdin()에서 SET 명령어를 대기한다. 만약, SET 명령어의 입력이 들어오면, cliConnect()를 통해 socket(tcp)연결을 시도하고, cliSendAsking()을 통해 server의 응답을 기다린다.
networking.c, (redis-server)server.c
networking.c의 acceptCommonHandler()를 통해 전달되어, server.c의 createSocketAcceptHandler()에서 연결을 허락하고 , networking.c의 writeToClient()에서 redis-cli에게 응답한다.
(redis-cli)redis-cli.c
matchArgs()에서 SET 명령어를 Command table과 비교한 후 cliSendCommand()를 통해 set 명령어를 redis-server에게 보낸다.
(redis-server)server.c
call()을 통해 Command table 내에서 SET 명령어를 찾아 실행한다.
t_string.c/db.c
t_string.c의 setCommand(), setGenericCommand() 그리고 db.c의 setKey()를 통해 key와 value를 나누어 inmemoryDB에 저장하는 파싱 과정을 거친다.