# Table of Contents

# Redis 정리

  • Key-Value 구조의 NoSQL 데이터베이스
  • 관계형 데이터베이스처럼 테이블을 설계하거나 외래키 설정이 불가능하다.
  • 디스크 기반이 아니라 인메모리 방식을 사용하기 때문에 입출력 적어 속도가 빠르다.
  • 인메모리 방식이기 때문에 애플리케이션이 다운되거나 재시작되면 데이터가 사라진다. 이 때문에 다음과 같은 방법으로 데이터를 백업한다.
    • 디스크에 데이터를 저장
    • 다른 노드에 복사본 저장
  • 세션 스토어, 데이터베이스 캐시, 공유 저장소 등에 사용된다.
  • Publish/Subscribe 모델을 지원하기 때문에 메시지 큐로도 사용할 수 있다.

# Redis 설정

Redis의 설치, 사용 방법에 대해 정리한다.

# Redis 설치

Mac OS에서는 Homebrew로 Redis를 설치할 수 있다.

$ brew install redis

설치 정보를 확인해보자.

$ brew info redis
redis: stable 7.0.0 (bottled), HEAD
Persistent key-value database, with built-in net interface
https://redis.io/
/usr/local/Cellar/redis/7.0.0 (14 files, 2.6MB) *
  Poured from bottle on 2022-05-04 at 12:55:03
From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/redis.rb
License: BSD-3-Clause
==> Dependencies
Required: openssl@1.1 ✔
==> Options
--HEAD
	Install HEAD version
==> Caveats
To restart redis after an upgrade:
  brew services restart redis
Or, if you don't want/need a background service you can just run:
  /usr/local/opt/redis/bin/redis-server /usr/local/etc/redis.conf
==> Analytics
install: 77,367 (30 days), 155,724 (90 days), 648,001 (365 days)
install-on-request: 76,894 (30 days), 154,909 (90 days), 643,952 (365 days)
build-error: 14 (30 days)

버전을 확인해보자.

$ redis-server --version
Redis server v=6.2.6 sha=00000000:0 malloc=libc bits=64 build=c6f3693d1aced7d9

# Redis 설치 위치

Homebrew로 Redis를 설치한 경우 /usr/local/opt/redis 경로에 Redis가 설치된다.

$ cd /usr/local/opt

$ ls | grep redis
redis
redis@6.2
redis@7.0

# Redis 서버 설정 파일

Redis 서버는 redis.confg로 환경설정을 할 수 있다. Homebrew로 Redis를 설치한 경우 /usr/local/etc에 위치한다.

$ cd /usr/local/etc  

$ ls | grep redis.conf
redis.conf
redis.conf.default

# Redis 시작

redis-server 명령어로 Redis를 시작할 수 있다.

$ redis-server
52712:C 15 Apr 2022 11:06:15.382 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
52712:C 15 Apr 2022 11:06:15.382 # Redis version=6.2.6, bits=64, commit=00000000, modified=0, pid=52712, just started
52712:C 15 Apr 2022 11:06:15.382 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
52712:M 15 Apr 2022 11:06:15.383 * Increased maximum number of open files to 10032 (it was originally set to 256).
52712:M 15 Apr 2022 11:06:15.383 * monotonic clock: POSIX clock_gettime
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 6.2.6 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                  
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 52712
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           https://redis.io       
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

52712:M 15 Apr 2022 11:06:15.385 # Server initialized
52712:M 15 Apr 2022 11:06:15.385 * Ready to accept connections

brew services start redis로 redis를 백그라운드로 실행할 수 있다.

$ brew services start redis

# Redis 상태 확인

$ brew services list
Name    Status  User     File             
redis   started yologger ~/Library/LaunchAgents/homebrew.mxcl.redis.plist

# Redis 재시작

$ brew services restart redis

# Redis 종료

$ brew services stop redis

# redis-cli

redis-cli는 커맨드라인 기반 데이터베이스 클라이언트다. redis-cli로 redis에 접속해보자.

$ redis-cli
> 

# Redis의 데이터 타입

Redis는 관계형 데이터베이스가 아니기 때문에 관계형 데이터베이스의 일반적인 데이터 타입을 지원하지 않는다. Redis가 지원하는 데이터 타입은 다음과 같다.

  • String
  • List
  • Set
  • Hash

# String

Redis는 대부분의 데이터를 문자열로 표현한다. 숫자, 날짜, 시간 등도 문자열로 저장된다.

> set name "Paul"
> set age 35
> set createdAt "2022-04-03 21:51:13.742000"

# String 저장

SET <KEY> <VALUE>로 문자열을 저장한다.

> SET name "paul"
OK

> SET email "yologger1013@gmail.com"
OK

> SET password "1234"
OK

> SET nation "Korea"
OK

> SET age 30
OK

# String 조회

GET <KEY>로 문자열을 조회한다.

> GET name
"paul"

> GET email
"Korea"

# 키 이름 변경

RENAME <OLD KEY> <NEY KEY>로 키 이름을 변경한다.

> RENAME name nickname
OK

# 키 개수 조회

DBSIZE로 데이터 개수를 조회한다.

> DBSIZE
(integer) 3

# 키 조회

KEYS <PATTERN> 형태로 키를 조회한다.

> KEYS *
1) "name"
2) "password"
3) "age"
4) "email"
5) "nation"
> KEYS n*
1) "name"
2) "nation"

# String 삭제

DEL <KEY>로 문자열을 삭제한다.

> DEL name
(integer) 1

여러 데이터를 동시에 삭제할 수 있다.

> DEL email password
(integer) 2

FLUSHALL로 모든 데이터를 삭제할 수 있다.

> DBSIZE
(integer) 3

> FLUSHALL
OK

> DBSIZE
(integer) 0

String과 관련된 모든 명령어는 이 곳 (opens new window)에서 확인할 수 있다.

# List

List를 사용하면 하나의 키에 여러 값을 저장할 수 있다. List와 관련된 모든 명령어는 이 곳 (opens new window)에서 확인할 수 있다.

# RPUSH

RPUSH <key> <element element ..>형태로 리스트의 오른쪽에 요소를 추가한다.

> RPUSH score 1 2 3 4 5
(integer) 5

> RPUSH score 6 7
(integer) 7

> LRANGE score 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
7) "7"

# LPUSH

리스트의 왼쪽에 값을 추가한다.

> RPUSH score 1 2 3 4 5
(integer) 5

> LPUSH score 6 7
(integer) 7

> LRANGE score 0 -1
1) "7"
2) "6"
3) "1"
4) "2"
5) "3"
6) "4"
7) "5"

# LRANGE

LRANGE <START> <STOP>형태로 사용하며 범위에 해당하는 요소들을 조회한다.

> RPUSH score 1 2 3 4 5
(integer) 5

> LRANGE score 0 2
1) "1"
2) "2"
3) "3"

-1은 List의 끝을 가리킨다.

> LRANGE score 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"

# LPOP

LPOP <KEY> <COUNT> 형태로 사용하며, 리스트의 첫 번째 요소를 제거한 후 반환한다.

> LRANGE score 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"

> LPOP score
"1"

> LPOP score 2
1) "2"
2) "3"

> LRANGE score 0 -1
1) "4"
2) "5"

# RPOP

RPOP <KEY> <COUNT> 형태로 사용하며, 리스트의 마지막 요소를 제거한 후 반환한다.

> LRANGE score 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"

> RPOP score
"5"

> RPOP score 2
1) "4"
2) "3"

> LRANGE score 0 -1
1) "1"
2) "2"

# LINDEX

LINDEX [KEY] [INDEX]형태로 사용하며 해당 인덱스의 요소를 반환한다.

> LRANGE players 0 -1
1) "paul"
2) "john"
3) "kane"
4) "monica"

> LINDEX players 0
"paul"

> LINDEX players 1
"john"

# Set

List와 유사하지만 중복을 허용하지 않으며, 순서가 없다. 모든 명령어는 이 곳 (opens new window)에서 확인할 수 있다.

# SADD

SADD [KEY] [ELEMENT] [ELEMENT].. 형태로 데이터를 추가한다.

> SADD players "paul" "smith"
(integer) 2

> SMEMBERS players
1) "paul"
2) "smith"

> SADD players "paul" "john"
(integer) 1

> SMEMBERS players
1) "john"
2) "smith"
3) "paul"

# SMEMBERS

SMEMBERS [KEY] 형태로 사용하며 모든 요소를 조회한다.

> SMEMBERS players
1) "john"
2) "smith"
3) "paul"

# SREM

SREM [KEY] [ELEMENT]형태로 사용하며 요소를 삭제한다.

> SMEMBERS players
1) "john"
2) "smtih"
3) "paul"

> SREM players "paul"
(integer) 1

> SMEMBERS players
1) "john"
2) "smtih"

# SUNION

SUNION [KEY 1] [KEY 2]형태로 사용하며 합집합을 반환한다.

> SMEMBERS players
1) "john"
2) "smtih"
3) "lampard"

> SMEMBERS managers
1) "gerrard"
2) "lampard"
3) "ferguson"

> SUNION players managers
1) "gerrard"
2) "lampard"
3) "john"
4) "smtih"
5) "ferguson"

# SINTER

SINTER [KEY 1] [KEY 2]형태로 사용하며 교집합을 반환한다.

> SMEMBERS players
1) "john"
2) "smtih"
3) "lampard"

> SMEMBERS managers
1) "gerrard"
2) "lampard"
3) "ferguson"

> SINTER players managers
1) "lampard"

# SDIFF

SDIFF [KEY 1] [KEY 2]형태로 사용하며 차집합을 반환한다.

> SMEMBERS players
1) "john"
2) "smtih"
3) "lampard"

> SMEMBERS managers
1) "gerrard"
2) "lampard"
3) "ferguson"

> SDIFF players managers
1) "smtih"
2) "john"

# Hash

Hash는 하나의 Key에 여러 Field:Value를 저장할 수 있다.

# HSET

HSET <KEY> <FIELD VALUE> 형태로 사용한다.

> HSET paul email "paul@gmail.com"
(integer) 1

# HGET

HGET <KEY> <FIELD>형태로 사용한다.

HGET paul email
"paul@gmail.com"

# HMSET

hmset <KEY> <FIELD VALUE> <FIELD VALUE> <FIELD VALUE> ... 형태로 사용한다

> HMSET paul email "paul@gmail.com" nation "Korea" age 30
OK

# HMGET

HMGET <KEY> <FIELD> <FIELD> ... 형태로 사용한다.

> HMGET paul email nation age
1) "paul@gmail.com"
2) "Korea"
3) "30"
> HMGET paul email nation
1) "paul@gmail.com"
2) "Korea"

# HGETALL

HGETALL [KEY]형태로 사용하며 모든 field와 value를 조회한다.

> HMSET paul email "paul@gmail.com" nation "Korea" age 30
OK

> HGETALL paul
1) "email"
2) "paul@gmail.com"
3) "nation"
4) "Korea"
5) "age"
6) "30"

# HDEL

HDEL [KEY] [FIELD] [FIELD] ...형태로 사용하며 지정한 Field와 Value를 삭제한다.

> HMSET paul email "paul@gmail.com" nation "Korea" age 30
OK

> HGETALL paul
1) "email"
2) "paul@gmail.com"
3) "nation"
4) "Korea"
5) "age"
6) "30"

> HDEL paul nation age
(integer) 2

> HGETALL paul
1) "email"
2) "paul@gmail.com"

# TYPE 명령어

TYPE <KEY> 명령어로 타입을 확인할 수 있다.

> TYPE person
set

> TYPE person:a4be06b1-b445-42fe-a70f-0fdee111cf55
hash

# FLUSHALL 명령어

FLUSHALL 명령어로 모든 키를 삭제할 수 있다.

> FLUSHALL
OK

> KEYS *
(empty array)

# Redis Expire

Redis는 저장한 데이터에 expire를 설정할 수 있다. EXPIRE [KEY] [SECOND] 명령어를 사용하면 된다.

// 데이터 저장
> SET name "Paul"

// 만료 시간 설정 
> EXPIRE name 10
// 10초가 지나기 전
> GET name
"Paul"

// 10초가 지난 후
> GET name
(nil)

TTL 명령어로 남은 시간 또는 만료 후 지나간 시간을 조회할 수 있다.

> TTL name  
5   // 만료되기 5초 전

> TTL name
-5  // 만료된 후 5초

PERSIST 명령어로 expire를 해제할 수 있다.

> PERSIST name

# Redis Namespace

Namespace를 활용하면 데이터를 체계적으로 데이터베이스에 저장할 수 있다.

> SET programmer:android:name "Paul"
> SET programmer:spring:name "John"
> SET designer:name "Monica"
> SET marketer:name "Rachel"
GET programmer:android:name
GET programmer:spring:name
GET designer:name
GET marketer:name

위 예제에서는 Namespace로 콜론(:)을 사용했다. 하지만 스택 오버플로우를 검색해보니 슬래시(/)를 사용하기도 한다. 구분자의 제한이 없는 것 같다.

# Namespace를 Redis 데이터베이스 설계에 활용하기

관계형 데이터베이스Redis는 구조적으로 다르기 때문에 데이터베이스 설계방식도 다르다.

우선 관계형 데이터베이스를 살펴보자. 애플리케이션의 회원 정보를 담는 member테이블과 회원의 게시글 정보를 담는 post테이블이 있다. 회원은 여러 게시글을 가질 수 있으므로 member 테이블과 post 테이블은 일대다(One to Many)관계이며, 이를 다이어그램으로 표현하면 다음과 같다.

테이블을 생성하는 DDL은 다음과 같을 것이다.

CREATE TABLE member (
    member_id BIGINT PRIMARY KEY NOT NULL AUTO_INCREMENT,
    email VARCHAR(255) 
);
CREATE TABLE post (
    post_id BIGINT PRIMARY KEY NOT NULL AUTO_INCREMENT,
    member_id BIGINT,
    title VARCHAR(255),
    content TEXT,
    FOREIGN KEY member_id REFERENCES member(member_id)
);

반면 Redis는 테이블이라는 개념 자체가 없다. 또한 외래키라는 개념도 없으므로 테이블 간 연관관계를 표현할 수도 없다. 따라서 마치 리눅스의 디렉토리 구조처럼 Namespace를 활용하여 데이터를 저장해야한다.

> HMSET post:1:1 title "programming" content "I love programming"
> HMGET post:1:1 title content
1) "programming"
2) "I love programming"

# Redis Multi Database

Redis는 여러 데이터베이스를 가질 수 있으며 기본적으로 16개의 데이터베이스가 제공된다. 설정파일에서 개수를 변경할 수 있으며, Homebrew로 Redis를 설치한 경우 /usr/local/etc/redis.conf에 위치한다.




 

# Set the number of databases. The default database is DB 0, you can select
# a different one on a per-connection basis using SELECT <dbid> where
# dbid is a number between 0 and 'databases'-1
databases 16

redis-cli로 Redis에 연결할 때 -n 옵션으로 데이터베이스를 선택할 수 있다.

$ redis-cli -n 3
redis-cli[3] > 

옵션을 지정하지 않으면 0번 데이터베이스를 사용하게 된다.

$ redis-cli
redis-cli > 

Redis에 이미 연결된 상태에서는 SELECT 명령어로 데이터베이스를 변경할 수 있다.

redis-cli[3]> SELECT 5
OK

redis-cli[5]> 

Medis 2를 사용하는 경우 화면 하단에서 데이터베이스를 선택할 수 있다.

# Redis Persistent

인메모리 방식이기 때문에 애플리케이션이 다운되거나 재시작되면 데이터가 사라진다. 이 때문에 다음과 같은 방법으로 데이터를 백업할 수 있다.

# RDB

현재 메모리 상태에 대한 스냅샷을 디스크에 저장한다.

# redis.conf
dbfilename dump.rdb

# AOF

AOF(Append Only File)은 추가, 수정, 삭제 명령이 실행될 때 마다 파일에 기록한다.

# redis.conf
appendonly yes # default no
appendfilename appendonly.aof # 파일이름 설정
appendfsync everysec # 디스크 동기화를 얼마나 자주할 것인가 (always, everysec, no)

# HA와 Redis Replication

Redis Replication을 사용하면 데이터를 이중화하여 HA(High Availability, 고가용성)을 확보할 수 있다. 쉽게 설명하면 다른 레디스 노드에 데이터를 복사하는 것이다.

레디스는 Master/Slave 모델의 Replication을 제공한다. 여러 레디스 노드 중 하나를 Master로 선정하여 사용하고, 데이터를 다른 노드에 저장한다. Master 노드에 장애가 발생하면 다른 Slave 노드를 Master로 승격하며, 특정 Slave 노드를 Master 노드로 변경할 수도 있다.

# Redis PUB/SUB

Redis PUB/SUB를 사용하면 Redis를 메시지 브로커로 사용할 수 있다. Redis PUB/SUB은 채널(Channel)을 통해 메시지를 전달한다.

메시지 수신 역할을 할 터미널을 오픈하고 subscribe <CHANNEL_NAME> 명령어를 입력하면 채널을 구독하게 된다. 채널이 없다면 자동으로 생성한다.

$ redis-cli

> subscribe channel1
Reading messages... (press Ctrl-C to quit)

메시지 전송 역할을 할 터미널을 오픈하고 publish <CHANNEL_NAME> <MESSAGE> 명령어를 입력하면 채널로 메시지를 보내게 된다.

$ redis-cli

> publish channel1 "message1"

수신 측 터미널에 메시지가 출력된다.

> subscribe test_channel
Reading messages... (press Ctrl-C to quit)
1) "message1"

pubsub channels 명령어로 채널 목록을 확인할 수 있다.

> pubsub channels

다음과 같이 여러 채널을 동시에 구독할 수도 있다.

> subscribe channel1 channel2

한 채널을 여러 구독자가 구독할 수도 있으며, 구독자 하나도 없는 채널은 자동으로 삭제된다.

Redis는 Kafka와 달리 메시지를 디스크에 저장(Queuing)하지 않기 때문에 구독 전 전송된 메시지를 읽을 수 없으며, 메시지가 유실될 수도 있다. 따라서 메시지 영속성을 보장하려면 별도의 데이터베이스에 메시지를 저장해야한다.

# Redis Stream

레디스 5.0부터 도입된 Redis Stream은 Redis Channel과 다르게 메시지를 저장(Queing)한다. 이 때문에 메시지 브로커보단 메시지 큐에 가깝다.

information

정리 중인 컨텐츠 입니다.

# Medis 2

Medis 2는 Redis를 위한 GUI Database Client다. Medis는 애플 앱 스토어에서 설치할 수 있다.

# Redis Labs

Redis 클라우드 서비스를 제공한다.