Redis, Commands
Introduction
집에만 박혀있는 설날에 포스트를 작성한다.
지금 듣고 있는 노래 새소년 (SE SO NEON) ‘자유(Jayu)’
다.
Commands
레디스에서는 네트워크 통신 지연을 줄여 줄 여러가지 기능을 제공한다. 이와 관련된 커맨드를 알아보도록 하자.
Pub/Sub
Pub/Sub
는 수신자에게 메세지를 직접적으로 전달하지 않는 패턴이다. Publisher
는 채널에 메세지를 보내고 Subscriber
는 채널을 구독 중이라면 메세지를 전달받는다.
아래는 Pub/Sub
패턴을 사용하는 예다.
- 뉴스와 날씨 정보
- 채팅 서비스
- 푸쉬메세지
- SaltStack과 같은 Remote code excution
PUBLISH
PUBLISH
를 통해 레디스 채널에 메세지를 보낸다.
var redis = require("redis");
var client = redis.createClient();
var channel = process.argv[2]; // 1
var command = process.argv[3]; // 2
client.publish(channel, command); // 3
client.quit();
SUBSCRIBE, UNSUBSCRIBE
한 개 이상의 채널을 구독 혹은 구독취소한다.
PSUBSCRIBE
, PUNSUBSCRIBE
도 비슷한 동작을 하지만 glob-style pattern
을 지원한다.
glob-style pattern
- h?llo subscribes to hello, hallo and hxllo
- h*llo subscribes to hllo and heeeello
- h[ae]llo subscribes to hello and hallo, but not hillo
var os = require("os"); // 1
var redis = require("redis");
var client = redis.createClient();
var COMMANDS = {}; // 2
COMMANDS.DATE = function() { // 3
var now = new Date();
console.log("DATE " + now.toISOString());
};
COMMANDS.PING = function() { // 4
console.log("PONG");
};
COMMANDS.HOSTNAME = function() { // 5
console.log("HOSTNAME " + os.hostname());
};
client.on("message", function(channel, commandName) { // 6
if (COMMANDS.hasOwnProperty(commandName)) { // 7
var commandFunction = COMMANDS[commandName]; // 8
commandFunction(); // 9
} else { // 10
console.log("Unknown command: " + commandName);
}
});
client.subscribe("global", process.argv[2]); // 11
Transcations
Transcation
은 atomic
하게 레디스 커맨드를 실행시킬 수 있다.
MULTI, EXEC
MULTI
는 트랜잭션에 시작을, EXEC
가 끝을 나타낸다.
모든 커맨드는 대기열에 올라 EXEC
커맨드가 동작할 때 서버로 보내진다.
EXEC
커맨드 대신 DISCARD
를 사용하여 트랜잭션이 실행되지 않도록 할 수 있다.
Flushes all previously queued commands in a transaction and restores the connection state to normal.
SQL
과는 달리 트랜잭션으로 발생한 변경사항은 트랜잭션이 도중에 오류를 일으켜도 롤백이 불가능하다.
중간에 실패한 커맨드는 무시한 체 다음 명령어를 계속해서 실행한다.
모든 커맨드가 한 번에 대기열에 올라가기 때문에 트랜잭션 내부 상태에 따라 다른 결정이 불가능하다.
아래는 통장에 상태에 따라 DISCARD
혹은 입금을 진행하는 예제코드다.
var redis = require("redis");
var client = redis.createClient();
function transfer(from, to, value, callback) { // 1
client.get(from, function(err, balance) { // 2
var multi = client.multi(); // 3
multi.decrby(from, value); // 4
multi.incrby(to, value); // 5
if (balance >= value) { // 6
multi.exec(function(err, reply) { // 7
callback(null, reply[0]); // 8
});
} else {
multi.discard(); // 9
callback(new Error("Insufficient funds"), null); // 10
}
});
}
WATCH, UNWATCH
WATCH
는 트랜잭션에서 현재 등록된 키들이 모두 변경되지 않았다는 것을 확인할 수 있다.
EXEC
가 호출되면, 트랜잭션의 동작여부와 상관없이 등록된 모든 키는 UNWATCH
상태가 된다.
클라이언트 연결이 종료될 경우에도 이와 같은 동작을 한다.
UNWATCH
는 인자 없이 호출 시 등록된 모든 키를 제거한다. 이는 트랜잭션 내부에서 해당 키들이 필요없어지고 새로운 키들을 이용할 경우에 매우 유용하다.
Optimistic lockgin using check-and-set
WATCH
는 트랜잭션에서 check-and-set(CAS)
동작을 제공한다.
위 커맨드는 특정 키가 변경되는 것을 감시하며, EXEC
커맨드가 시작되기 전에 만약 하나의 키 값이라도 변경될 시, 현재 트랜잭션을 기각한다.
var redis = require("redis");
var client = redis.createClient();
function zpop(key, callback) { // 1
client.watch(key, function(watchErr, watchReply) { // 2
client.zrange(key, 0, 0, function(zrangeErr, zrangeReply) { // 3
var multi = client.multi(); // 4
multi.zrem(key, zrangeReply); // 5
multi.exec(function(transactionErr, transactionReply) { // 6
if (transactionReply) {
callback(zrangeReply[0]); // 7
} else {
zpop(key, callback); // 8
}
});
});
});
}
client.zadd("presidents", 1732, "George Washington");
client.zadd("presidents", 1809, "Abraham Lincoln");
client.zadd("presidents", 1858, "Theodore Roosevelt");
zpop("presidents", function(member) {
console.log("The first president in the group is:", member);
client.quit();
});
Pipelines
파이프라인은 레디스 서버에 복수의 요청을 한 번에 처리할 수 있도록 한다.
Redis는 고성능의 key-value store지만, TCP 기반의 클라이언트-서버 모델을 따르기에 아래와 같이 동작하고, 네트워크 IO에 대한 병목이 존재할 수밖에 없습니다.(물리적인 네트워크 지연, 3-way handshake 등의 이유로)
PlanB의 백엔드 엔지니어링
Round Trip Time(RTT)
란 커맨드를 전송하고 그에 대한 응답을 레디스 서버로부터 얻는데까지 걸리는 시간을 의미한다. 예를들면 \(n\)개의 커맨드를 전송할 경우 \(n\)개 RTT
를 받게 되는 것이다.
레디스는 이러한 병목 현상 완화를 위해 파이프라인을 제공한다. 자세한 코드는 5장에서 볼 수 있지만, 파이프라인을 사용한다면 아래와 같이 통신이 이루어질 것이다.
마치며,
오늘은 레디스가 제공하는 여러 커맨드를 알아보았다.
현 챕터에서는 자료구조에 대한 튜닝 혹은 기본설정 또한 다룬다. 꼭 다루고 싶은 주제가 아니기에 위 포스트에선 생략했다. 나중에 기회가 되면 다루고 싶긴 하다
Refernce
- Maxwell Dayvson Da Silva, Redis Essentials
- https://redis.io/topics