레디스 개요
앞서 NoSQL에 대해 알아 보았겠지만, RDBMS와 비교해 다시 한 번 정리해 보자. NoSQL은 관계형 데이터베이스(RDB)의 표 형태 관계들이 아닌, 다른 형태로 데이터를 저장ㆍ조회할 수 있는 메커니즘을 제공하는 모든 데이터베이스를 칭한다(http://en.wikipedia.org/wiki/NoSQL). 아래 표와 같이 크게 4가지 형태로 분류할 수 있다. 이중 레디스(Redis: Remote Dictionary Server)는 Key-Value Store로 분류된다.
[표 Ⅱ-3-1] NoSQL의 유형과 특징
Column-Oriented Store |
|
Document-Oriented Store |
|
Key-value Store |
|
Graph Database |
|
Ben Scofield의 데이터베이스 유형 간 비기능 품질 특성 비교를 따르면, Key-Value Store는 기능ㆍ확장성ㆍ유연성 면에서 매우 높은 품질을 가지면서도 구성 복잡도가 매우 낮음을 알 수 있다.
[표 Ⅱ-3-2] NoSQL 유형 간 비교
데이터 모델 |
성능 |
확장성 |
유연성 |
복잡도 |
기능성 |
Key–Value Store |
hjgh |
hjgh |
high |
none |
variable (none) |
Column-Oriented Store |
high |
high |
moderate |
low |
minimal |
Document-Oriented Store |
high |
variable (high) |
high |
low |
variable (low) |
Graph Database |
variable |
variable |
high |
high |
graph theory |
Relational Database |
variable |
variable |
low |
moderate |
relational algebra |
반면 Eric Brewer의 CAP 원칙(1999)을 따르면, 분산 컴퓨터 시스템은 아래 세 가지의 비기능 품질 속성을 동시에 모두 만족시킬 수 없으며, 최대 두 가지만 동시에 만족시킬 수 있다.
- Consistency: 모든 노드들에게 동시에 동일한 데이터 조회 보장
- Availability: 성공 또는 실패를 막론하고 모든 요청에 대한 응답을 보장
- Partition Tolerance: 미리 예측할 수 없는 데이터 손실 또는 시스템 구성 요소 중 일부의 실패(Failure)에도 전체 시스템은 항상 정상 작동함을 보장
또한 Nathan Hurst의 ‘Visual Guide of NoSQL System’에서 확인해 보면 레디스는 데이터 일관성(Consistency)과 Partition Tolerance를 동시에 만족시키지만, 데이터 가용성(Availability)에 대한 지원은 부족해 구성 및 운영 시 이에 대한 고려가 필요함을 알 수 있다.
[그림 Ⅱ-3-1] Visual Guide to NoSQL Systems
Redis(Remote Dictionary Server)를 간단히 살펴보면 다음 표와 같다.
[표 Ⅱ-3-3] 레디스 개요
구분 |
내용 |
개발자(사) |
Salvatore Sanfilippo |
최초 배포일 |
2009년 04월 10일 |
최신 안정 배포판 |
2.8.14(2014년09월 10일 기준) |
구현 언어 |
ANSI C |
지원 운영체계 |
크로스플랫폼 |
유형 |
Key–value stores |
라이선스 |
BSD |
웹 사이트 |
- 선택적인 데이터 지속성과 함께 메모리 상에 키-값 쌍을 저장하는 오픈소스 소프트웨어다.
(www.wikipedia.org) - ANSI C로 작성돼 ANSI C 컴파일러가 동작하는 다양한 환경에 설치와 실행이 가능하다.
- 트위터, 기트허브, 핀터레스트, 인스타그램, 텀플러, 네이버 라인 등 대용량 서비스 레퍼런스가 있다.
- 인메모리 자료구조(Data Structure) 저장소다. 즉 기본 데이터 저장소는 메모리다.
- – Simple String: Key-Value Store
- – List
- – Set
- – Sorted Set
- – Hash
- 읽기 성능 향상을 위한 마스터/슬레이브 서버 복제(Replication)를 지원한다.
- 쓰기 성능 향상을 위한 데이터 샤딩(Sharding)을 지원한다.
- 데이터 영속성(Persistency)을 지원한다.
- – 스냅샷: 특정 시점의 메모리의 내용을 *.rdb 파일로 디스크에 저장
- – AOF(Append Only File): 수행된 레디스 명령어들을 파일 형태로 디스크에 저장
- 서버 간 이벤트 공유를 위한 Publish/Subscribe 기능 지원
- 마스터/슬레이브 불문하고 레디스 서버는 단일 스레드로 동작(1개의 CPU에서 동작)
- 100,000QPS(Query Per Second)의 고성능
다음은 배치도(Deployment Diagram)형태의 레디스 기반 시스템 아키텍처 사례다.
[그림 Ⅱ-3-2] 레디스 아키텍처 사례(배치도)
위 아키텍처 배치도 사례 내 주요 구성요소들은 다음과 같다.
구성요소 |
설명 |
Web app |
Web Browser 기반 사용자 클라이언트 |
Load Balancer |
Web 서버로 향하는 워크로드를 분산시켜 성능/안정성을 향상시키는 네트워크 장비(L4 Switch) |
Web Socket Server |
Web Server |
Redis |
메시징 및 데이터 캐싱 용도로 사용 |
Data Layer |
RDBMS로의 접근 경로 제공 (ODBC, JDBC, ADO .NET 등) |
Application Layer |
Business Application 탑재 및 접근 경로 제공 |
Persistent RDBMS |
하드디스크 기반의 관계형 데이터베이스 (Oracle, MySQL, 등) |
레디스 설치
가상머신 소프트웨어는 Virtual Box를 사용해도 무방하고 게스트 OS는 레디스가 공식 지원하는 Unix, Linux, MacOS 중에서 하나를 선택해 설치한다. 윈도우용 레디스는 https://github.com/dmajkic/redis/downloads에서 내려 받아 설치할 수 있지만, 개발 또는 학습 용으로만 사용하기를 권장한다.
Ubuntu의 터미널에서 아래의 명령을 수행해 레디스 설치 파일을 다운로드하고 설치한다.
다음 명령으로 레디스 서버를 기동한다.
[그림 Ⅱ-3-3] 레디스 서버 기동화면
레디스가 기동 되면 [그림 Ⅱ-3-3]과 같은 화면이 출력된다 레디스는 기본적으로 싱글스레드를 사용하기 때문에 PID 하나를 사용하고 6379포트를 사용하여 클라이언트와 통신한다. 레디스를 설치하고 서버를 기동한 후 내장된 명령행(CLI) 클라이언트를 통해 다음과 같이 정상적으로 동작하는지를 확인할 수 있다.
[Note]
레디스 명령행은 대소문자를 구분하지 않으며 명령행에 사용하는 명령어들의 인자값들은 정규표현식을 인식한다.
레디스 환경 설정
레디스 환경설정에는 다음 두 가지 방법이 있다.
[표 Ⅱ-3-4] 레디스 환경 설정 방법
환경설정방법 |
형식 |
설명 |
redis.conf |
설정 파일 |
|
config set |
런타임 명령어 |
|
2014년 9월10일 현재, 주석을 제거한 redis.conf 파일의 내용은 다음과 같다. 해당 설정 항목을 일일이 소개하기에는 지면 제한으로 어려워, 레디스 기반 시스템 아키텍처 설계 시 필수적으로 고려해야 하는 복제(Replication), 샤딩(Sharding), 스냅샷(Snapshot), AOF를 위주로 살펴본다. 독자가 향후 이 파일의 주석과 함께 관련 문서들을 더 폭넓게 살펴봄으로써 레디스 구성 및 설정에 대한 지식을 넓힐 수 있다. redis.conf 파일의 최신본은 http://download.redis.io/redis-stable/redis.conf에서 내려 받을 수 있다.
레디스 설정 파일 내 메모리 크기 등을 지정하기 위한 단위 사용 시 대소문자를 구분하지 않는다.
[표 Ⅱ-3-5] 레디스 환경설정 파일 숫자 단위
표현 |
설명 |
1k |
1,000바이트 |
1kb |
1,024바이트 |
1m |
1,000,000바이트 |
1mb |
1,024×1,024바이트 |
1g |
1,000,000,000바이트 |
1gb |
1,024× 1,024×1,024바이트 |
레디스 설정 항목
레디스 설정 및 관리 작업에 참조할 수 있도록, 설정 파일 내 각 설정 항목들을 알아보자. 해당 내용은 본 교재의 내용을 충분히 살펴보고, 레디스의 기본 구조와 메커니즘에 대해 이해한 후 다시 살펴볼 필요가 있다.
General
설정항목 |
설정사례 |
설명 |
daemonize |
No |
레디스는 기본적으로 daemon으로 동작하지 않음 |
pidfile |
/var/run/redis.pid |
레디스가 daemon으로 동작할 때, pid[process id] 파일을 기록하는 기본 위치(별도 파일 지정 가능) |
port |
6379 |
기본 포트 번호(별도 포트번호 지정 가능) |
bind |
127.0.0.1 |
특정 네트워크 I/F 지정(미지정 시 모든 네트워크 I/F 사용) |
unixsocket |
/tmp/redis.sock |
특정 UNIX 소켓 지정(미지정 시 UNIX 소켓을사용 안함) |
unixsocketperm |
755 |
|
timeout |
0 |
|
tcp-keepalive |
60 |
특정 시간(Second) 클라이언트 미동작 시 TCP ACK 전송 |
loglevel |
notice |
debug(가장 상세한 로그) / verbose / notice / warning(중요 또는 심각한 메시지만 기록) |
logfile |
stdout |
|
syslog-enabled |
no |
yes로 지정한 경우 OS syslog 활용(syslog 설정 조정 필요) |
syslog-ident |
redis |
syslog 사용 시 log identity 지정 |
syslog-facility |
local0 |
User 또는 local0 ~ local7 사이의 값을 지정해야 함 |
Snapshotting
설정항목 |
설정사례 |
설명 |
save |
900 1 |
900초(15분) 이후 1개의 쓰기 발생 |
save |
300 10 |
300초(3분) 이후 10개의 쓰기 발생 |
save |
60 10000 |
60초 이후 10,000개의 쓰기 발생 시 디스크에 데이터 복제 |
stop-writes-onbgsave-error |
yes |
|
rdbcompression |
yes |
dump.rdb 파일을 LZF로 압축 |
rdbchecksum |
yes |
|
dbfilename |
dump.rdb |
변경 지정 가능 |
dir |
./ |
dump.rdb 파일과 AOF 파일 생성 위치 |
Replication
설정항목 |
설정사례 |
설명 |
slaveof |
<masterip> <masterport> |
복제 대상 마스터 노드의 IP 주소와 포트 번호를 명기 |
slave-serve-stale-data |
yes |
|
slave-read-only |
yes |
권장 기본 값 |
repl-ping-slaveperiod |
10 |
슬레이브 노드가 마스터 노드에게 정기적으로 ping을 보내는 간격 (초) |
repl-timeout |
60 |
Bulk transfer I/O, Master Data, Ping Response에 대한 timeout 값(부적절한 timeout 발생을 방지하기 위해 repl-ping-slave-period보다 반드시 큰 값이어야 함) |
repl-disable-tcp-nodelay |
no |
|
repl-backlog-size |
1mb |
|
repl-backlog-ttl |
3600 |
|
slave-priority |
100 |
Redis Sentinel이 마스터 노드 장애 시 슬레이브 노드들 중 마스터 노드 선택 기준 값(적은 숫자가 더 높은 우선 순위를 가짐) |
min-slaves-to-write |
0 |
마스터 노드가 쓰기 요청을 수용하기 위한 최소 연결 슬레이브 노드개수(충분한 개수의 슬레이브 노드가 없어서 데이터 복제본을 유지할 수 없으면 쓰기 결과가 유실될 수 있음) |
min-slaves-max-lag |
10 |
최소 연결 슬레이브 노드 개수에 모자랄 때 슬레이브 노드들의 연결을 기다리는 시간(Second) |
Security
설정항목 |
설정사례 |
설명 |
requirepass |
foobared |
클라이언트에게 명령 요청 시 패스워드를 입력하도록 강제 |
renamecommand |
CONFIG b840fc02d524045429941cc15f59e41cb7be6c52 |
명령어 치환. 다음의 경우 해당 명령어 사용을 거부하게 됨 (예: rename-command CONFIG “”) |
LIMITS
설정항목 |
설정사례 |
설명 |
maxclients |
10000 |
접속 클라이언트 개수 제한 |
maxmemory |
<bytes> |
메모리 사용 제한 |
maxmemory-policy |
volatile-lru |
사용 메모리가 maxmemory에서 지정한 값을 넘을 때 메모리 관리 정책
|
maxmemory-samples |
3 |
LRU 또는 최소 TTL 정책을 적용해 Key 삭제 전에 유사한 조건을 가진 Key들을 선택한 뒤, 최종적으로 삭제 대상 Key를 선정하기 위한 선택 범위 지정(샘플링 개수) |
APPEND ONLY MODE
설정항목 |
설정사례 |
설명 |
appendonly |
yes |
AOF 파일 사용 여부 |
appendfilename |
“appendonly.aof” |
AOF 파일 이름 지정 |
appendfsync |
always |
운영체제의 fsync( )에 의한 지연된 쓰기 옵션 (메모리 -> Disk)
|
no-appendfsync-on-rewrite |
no |
쓰기 명령에 대한 fsync( ) 리턴 지연 시간이 너무 길어지면 운영체제의 간섭이 있을 수 있으므로, 스냅샷 생성을 위한 BGSAVE 또는 AOF 파일 기록을 위한 BGREWRITEAOF가 실행되고 있을 때는 fsync( )의 호출을 블록킹 함 |
auto-aof-rewrite-percentage |
100 |
AOF 파일 초기화 및 재기록 시작을 위해 최초 AOF 파일의 크기를 기준으로 사용 비율을 지정(‘0’으로 지정 시 AOF 파일 초기화 없음) |
auto-aof-rewrite-min-size |
64mb |
AOF 파일 초기화 및 재기록 시작을 위해 파일 크기를 지정(‘0’으로 지정 시 AOF 파일 초기화 없음) |
LUA SCRIPTING
설정항목 |
설정사례 |
설명 |
lua-time-limit |
5000 |
5,000초까지 Lua 스크립트 실행 미종료 시 에러 메시지 리턴(‘0’으로 지정 시 실행 시간 무제한) |
SLOW LOG
설정항목 |
설정사례 |
설명 |
slowlog-log-slower-than |
10000 |
지정된 시간을 넘어 실행이 완료되지 않은 명령들을 Slow Log에 기록함.micro second 단위(1,000,000 = 1 초) |
LATENCY MONITOR
설정항목 |
설정사례 |
설명 |
latency-monitor-threshold |
0 |
|
EVENT NOTIFICATION
설정항목 |
설정사례 |
설명 |
notify-keyspace-events |
“” |
(레디스의 이벤트 notification 옵션을 아래의 키워드로 설정)
|
LUA SCRIPTING
설정항목 |
설정사례 |
설명 |
lua-time-limit |
5000 |
5,000초까지 Lua 스크립트 실행 미종료 시 에러 메시지 리턴(‘0’으로 지정 시 실행 시간 무제한) |
ADVANCED CONFIG
설정항목 |
설정사례 |
설명 |
hash-max-ziplist-entries |
512 |
해시 값이 512개를 넘지 않고 그 중 가장 큰 해시 값의 크기가 64바이트를 넘지 않을 경우, ziplist 형식으로 압축해 메모리 절약 |
hash-max-ziplist-value |
64 |
|
list-max-ziplist-entries |
512 |
List가 512개를 넘지 않고 그 중 가장 큰 List의 크기가 64바이트를 넘지 않을 경우, ziplist 형식으로 압축해 메모리 절약 |
list-max-ziplist-value |
64 |
|
set-max-intset-entries |
512 |
Set의 크기가 512를 넘지 않는 경우 intset 형식으로 압축해 메모리 절약 |
zset-max-ziplist-entries |
128 |
정렬된 Set 내 항목 개수가 128개를 넘지 않고 각 항목의 크기가 64바이트를 넘지 않을 경우, ziplist 형식으로 압축해 메모리 절약 |
zset-max-ziplist-value |
64 |
|
hll-sparse-max-bytes |
3000 |
|
activerehashing |
yes |
|
hz |
10 |
|
aof-rewrite-incremental-fsync |
yes |
“yes”로 지정 시, AOF 파일 기록을 위한 Redis child 프로세스가 fsync( )를 활용 데이터를 32 MB 단위로 전송 반영 함. |
client-output-buffer-limit |
<class> <hardlimit> <softlimit> <softseconds> |
클라이언트 time out 시간 지정
hardlimit/softlimit/softsecond의 값을 “0”으로 지정하는 경우 클라이언트 out buffer 크기에 제한을 두지 않음
[지정 사례] client-output-buffer-limitnormal000 client-output-buffer-limitslave256mb64mb60 client-output-buffer-limitpubsub32mb8mb60 |
레디스 실습
레디스는 스토리지에 저장되는 일반 RDB와 두 가지 차이점이 있다. 첫 번째 NoSQL을 지원하는 key-value로 데이터를 관리하며. 두 번째 스토리지가 아닌 메모리에서 데이터를 관리한다는 점이다.
이 두 가지 차이점을 생각하며 레디스에서 데이터를 다루는 방법을 실습해 보도록 하자.
레디스의 기본사용법
데이터 입력
데이터를 레디스에 입력하기 위해서는 set명령어를 사용한다. 레디스 클라이언트 명령행에서 데이터 입력을 실습해 보자.
- ① set 명령어를 사용해 data1을 “key”로 mydata1을 “value”로 레디스에 저장한다.
- ② keys 명령어를 사용하여 data1 key가 있는지 확인한다.
- ③ get 명령어를 사용하여 data1의 value값을 확인한다. mydata1 value가 저장 되어 있는 것을 확인할 수 있다.
여러 건의 데이터 입력
mset을 사용하여 여러 건의 데이터를 입력해보자.
- ① mset은 여러 건의 데이터를 입력하는 명령어이다. mset으로 입력시 항상 key1 value1 key2 value2 형식으로 입력하며 key와 value의 쌍이 맞아야 한다.
- ② keys명령어를 사용하여 key가 정확히 저장되었는지 확인할 수 있다.
- ③ get명령어를 사용하여 data1의 value를 확인할 수 있다. 그런데 data1의 value값이 변한 것을 볼 수 있다. 레디스는 초기에 multi 선언을 하지 않으면 트랜잭션을 일으켜 데이터를 commit한다.
- ④ mget 명령어로 여러 개의 키에 할당된 값을 조회할 수 있다.
트랜잭션관리
레디스에서 트랜잭션을 관리하기 위해서는 multi 명령어를 사용한다.
multi 명령어를 사용하고 data1에 mydata4를 저장하면 “QUEUED” 상태가 된다.
현재 세션에서 get 명령어로 데이터를 조회해 보고 터미널을 새로 열어 데이터를 조회해 보자
현재 세션에서는 상태가 “QUEUED”로 조회되고 새로운 터미널에서 조회하면 이전에 입력한 데이터인 mydata0이 조회된다.
레디스의 트랜잭션 실행 명령어인 exec를 사용하여 데이터를 commit하고 현재 세션의 데이터와 다른 터미널에서도 데이터가 변했는지 확인해 보자.
두 개의 세션에서 모두 데이터가 commit 된 것을 확인할 수 있다.
데이터 타입
레디스는 String, List, Hash, Set, Stored set의 데이터타입을 지원한다.
String
String 타입은 레디스의 가장 기본적인 데이터타입이다. binary형태로 저장되기 때문에 데이터의 포맷에 관계없이 (동영상이나 이미지 같은 데이터도 상관없이) 저장할 수 있다. 또한 문자열과 정수형을 자동으로 구분하기 때문에 해당 key에 대한 데이터 형태는 각 key의 value를 판단하여 레디스에서 자동으로 인식하게 된다.
String 타입의 데이터를 추가하는 것에 대하여 알아보자. key-value 데이터베이스에서 데이터를 추가하는 것은 스키마를 가지고 있는 RDB와는 다르게 데이터 건수를 늘리는 것이 아니라 특정 key에 대한 value의 값을 더하는 것이다.
append명령어를 사용하여 데이터를 추가 하였다. 결과는 이전에 입력한 “11a”에 입력한 문자열 “12a”가 추가 되어 “11a12a”로 값이 저장된 것을 확인 하였다.
List
List 타입은 String 타입의 데이터가 생성된 순서대로 정렬된 리스트라 이해하면 된다.
List 타입으로 데이터를 생성해 보자. List 타입의 데이터 입력은 push명령어를 사용한다. rpush는 오른쪽으로 데이터를 추가하고 lpush명령은 왼쪽으로 데이터를 추가한다. 데이터를 조회하기 위해서는lrange 명령어를 사용하여 범위 값을 지정하여 지정된 범위내의 데이터 값을 조회할 수 있다.
리스트의 데이터를 삭제해 보자. lrem 명령어를 사용하여 “d”를 삭제하였다.
Set
Set 타입은 먼저 String 타입에서 데이터 입력을 위해 사용하는 set명령과는 상관이 없다. Set 타입은 String 타입의 데이터가 순서에 관계없이 집합된 형태이다. Set 타입에서는 Set내에서 데이터의 추가, 삭제 및 테스트가 가능하다.
Set 타입으로 데이터를 입력해 보자. sadd 명령어를 사용하여 데이터를 입력한다. smembers 명령어를 사용하여 데이터를 조회한다. Set 데이터타입의 특이한 점은 key에 대하여 중복된 값을 입력하여도 데이터의 값을 중복으로 저장하지 않는다는 것이다.
Sinter를 사용하여 교집합을 구해보자. Sinter는 RDB의 조인과 유사하게 사용할 수 있다.
Hash
Hash 타입은 여러 개의 key들과 value들이 구조화 된 타입이라 할 수 있다. 레디스에서 Hash 데타입은 RDB의 테이블과 가장 유사한 데이터처럼 보이지만 RDB 측면에서 보면 단 하나의 로우만 갖는 테이블처럼 보일 것이다.
Hash 타입으로 이름, 나이, 성별을 가지고 있는 데이터 오브젝트를 만들어보자. hmset 명령어를 사용하여 emp라는 오브젝트에 name, age, gender의 필드를 만들고 데이터를 입력하였다. hmget명령어를 사용하여 데이터를 조회한다.
Sorted Set
Sorted Set 타입은 Set 타입과 유사하지만 각각의 값에 스코어가 추가 된다. 스코어를 이용하기 때문에 Set 타입보다 데이터의 입력, 추가, 삭제에서 훨씬 좋은 성능을 보장한다.
Sorted Set 타입으로 데이터를 입력해 보자. zadd 명령어를 사용하여 데이터를 입력한다. zrange 명령어를 사용하여 입력한 데이터를 조회한다. zrange명령어에 withscores 옵션을 사용하면 입력시 저장한 score값도 함께 조회할 수 있다.