HAYASHIER.COM - Private Page
Redis 公式ドキュメント まとめ

Redis の公式ドキュメントを一読しておこうと思い、読んでまとめてみました。( 2018/05/06 時点)

ただし、Redis 4.0 については、公式ドキュメントに一部内容が含まれますが、RDB, AOF や Docker/NAT サポート辺り等、その他複数機能強化や変更点がありますので、詳細は、下記リリースノートを参照していただければと思います。 https://raw.githubusercontent.com/antirez/redis/4.0/00-RELEASENOTES

別途、Redisの機能のまとめ記事等は以下のURLを参照いただければと存じます。 Dive Deep Redis ~ 入門から実装の確認まで ~ Redis / Memcached Source Code Reading – Overview – Dive Deep Redis Internals ~ GETコマンド実行時の動作 ~

Redis Documentation

Programming with Redis

Command reference

APPEND
AUTH
BGREWRITEAOF
BGSAVE
BITCOUNT
BITFIELD
BITOP
BITPOS
BLPOP
BRPOP
BRPOPLPUSH
CLIENT KILL
CLIENT LIST
CLIENT GETNAME
CLIENT PAUSE
CLIENT REPLY
CLIENT SETNAME
CLUSTER ADDSLOTS
CLUSTER COUNT-FAILURE-REPORTS
CLUSTER COUNTKEYSINSLOT
CLUSTER DELSLOTS
CLUSTER FAILOVER
CLUSTER FORGET
CLUSTER GETKEYSINSLOT
CLUSTER INFO
CLUSTER KEYSLOT
CLUSTER MEET
CLUSTER NODES
CLUSTER REPLICATE
CLUSTER RESET
CLUSTER SAVECONFIG
CLUSTER SET-CONFIG-EPOCH
CLUSTER SETSLOT
CLUSTER SLAVES
CLUSTER SLOTS
COMMAND
COMMAND COUNT
COMMAND GETKEYS
COMMAND INFO
CONFIG GET
CONFIG REWRITE
CONFIG SET
CONFIG RESETSTAT
DBSIZE
DEBUG OBJECT
DEBUG SEGFAULT
DECR
DECRBY
DEL
DISCARD
DUMP
ECHO
EVAL
EVALSHA
EXEC
EXISTS
EXPIRE
EXPIREAT
FLUSHALL
FLUSHDB
GEOADD
GEOHASH
GEOPOS
GEODIST
GEORADIUS
GEORADIUSBYMEMBER
GET
GETBIT
GETRANGE
GETSET
HDEL
HEXISTS
HGET
HGETALL
HINCRBY
HINCRBYFLOAT
HKEYS
HLEN
HMGET
HMSET
HSET
HSETNX
HSTRLEN
HVALS
INCR
INCRBY
INCRBYFLOAT
INFO
KEYS
LASTSAVE
LINDEX
LINSERT
LLEN
LPOP
LPUSH
LPUSHX
LRANGE
LREM
LSET
LTRIM
MEMORY DOCTOR
MEMORY HELP
MEMORY MALLOC-STATS
MEMORY PURGE
MEMORY STATS
MEMORY USAGE
MGET
MIGRATE
MONITOR
MOVE
MSET
MSETNX
MULTI
OBJECT
PERSIST
PEXPIRE
PEXPIREAT
PFADD
PFCOUNT
PFMERGE
PING
PSETEX
PSUBSCRIBE
PUBSUB
PTTL
PUBLISH
PUNSUBSCRIBE
QUIT
RANDOMKEY
READONLY
READWRITE
RENAME
RENAMENX
RESTORE
ROLE
RPOP
RPOPLPUSH
RPUSH
RPUSHX
SADD
SAVE
SCARD
SCRIPT DEBUG
SCRIPT EXISTS
SCRIPT FLUSH
SCRIPT KILL
SCRIPT LOAD
SDIFF
SDIFFSTORE
SELECT
SET
SETBIT
SETEX
SETNX
SETRANGE
SHUTDOWN
SINTER
SINTERSTORE
SISMEMBER
SLAVEOF
SLOWLOG
SMEMBERS
SMOVE
SORT
SPOP
SRANDMEMBER
SREM
STRLEN
SUBSCRIBE
SUNION
SUNIONSTORE
SWAPDB
SYNC
TIME
TOUCH
TTL
TYPE
UNSUBSCRIBE
UNLINK
UNWATCH
WAIT
WATCH
ZADD
ZCARD
ZCOUNT
ZINCRBY
ZINTERSTORE
ZLEXCOUNT
ZRANGE
ZRANGEBYLEX
ZREVRANGEBYLEX
ZRANGEBYSCORE
ZRANK
ZREM
ZREMRANGEBYLEX
ZREMRANGEBYRANK
ZREMRANGEBYSCORE
ZREVRANGE
ZREVRANGEBYSCORE
ZREVRANK
ZSCORE
ZUNIONSTORE
SCAN
SSCAN
HSCAN
ZSCAN

Using pipelining to speedup Redis queries

    r = Redis.new
    r.pipelined {
        10000.times {
            r.ping
        }
    }

Pub/Sub

EVAL

RandomPushScript = <<EOF
    local i = tonumber(ARGV[1])
    local res
    math.randomseed(tonumber(ARGV[2]))
    while (i > 0) do
        res = redis.call('lpush',KEYS[1],math.random())
        i = i-1
    end
    return res
EOF

r.del(:mylist)
puts r.eval(RandomPushScript,1,:mylist,10,rand(2**32))

Redis Lua scripts debugger – Redis

Special encoding of small aggregate data types

EXPIRE – Redis

Using Redis as an LRU cache

Transactions

Redis Mass Insertion – Redis

Partitioning: how to split data among multiple Redis instances.

Distributed locks with Redis

Redis Keyspace Notifications

PUBLISH __keyspace@0__:mykey del : Key-space 通知 -> チャンネルは、キーイベントを受信 
PUBLISH __keyevent@0__:del mykey : Key-event 通知 -> チャンネルは、キー名を受信
K     Keyspace events, published with __keyspace@<db>__ prefix.
E     Keyevent events, published with __keyevent@<db>__ prefix.
g     Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ...
$     String commands
l     List commands
s     Set commands
h     Hash commands
z     Sorted set commands
x     Expired events (events generated every time a key expires)
e     Evicted events (events generated when a key is evicted for maxmemory)
A     Alias for g$lshzxe, so that the "AKE" string means all the events.
$ redis-cli config set notify-keyspace-events KEA
$ redis-cli --csv psubscribe '__key*__:*'

Secondary indexing with Redis

ZADD myindex 0 banana:273:Banana
MULTI
ZADD myindex 0 0056:0028.44:90
HSET index.content 90 0056:0028.44:90
EXEC
def spacequery(x0,y0,x1,y1,exp)
    bits=exp*2
    x_start = x0/(2**exp)
    x_end = x1/(2**exp)
    y_start = y0/(2**exp)
    y_end = y1/(2**exp)
    (x_start..x_end).each{|x|
        (y_start..y_end).each{|y|
            x_range_start = x*(2**exp)
            x_range_end = x_range_start | ((2**exp)-1)
            y_range_start = y*(2**exp)
            y_range_end = y_range_start | ((2**exp)-1)
            puts "#{x},#{y} x from #{x_range_start} to #{x_range_end}, y from #{y_range_start} to #{y_range_end}"

            # Turn it into interleaved form for ZRANGEBYLEX query.
            # We assume we need 9 bits for each integer, so the final
            # interleaved representation will be 18 bits.
            xbin = x_range_start.to_s(2).rjust(9,'0')
            ybin = y_range_start.to_s(2).rjust(9,'0')
            s = xbin.split("").zip(ybin.split("")).flatten.compact.join("")
            # Now that we have the start of the range, calculate the end
            # by replacing the specified number of bits from 0 to 1.
            e = s[0..-(bits+1)]+("1"*bits)
            puts "ZRANGEBYLEX myindex [#{s} [#{e}"
        }
    }
end

spacequery(50,100,100,300,6)

Redis modules API

Redis 4.0からモジュールプログラミングの機能追加

Introduction to Redis modules

loadmodule /path/to/mymodule.so
MODULE LOAD /path/to/mymodule.so

ローディングされたモジュール

MODULE LIST

アンロード

MODULE UNLOAD mymodule
#include "redismodule.h"
#include <stdlib.h>

int HelloworldRand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
    RedisModule_ReplyWithLongLong(ctx,rand());
    return REDISMODULE_OK;
}

int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
    if (RedisModule_Init(ctx,"helloworld",1,REDISMODULE_APIVER_1)
        == REDISMODULE_ERR) return REDISMODULE_ERR;

    if (RedisModule_CreateCommand(ctx,"helloworld.rand",
        HelloworldRand_RedisCommand) == REDISMODULE_ERR)
        return REDISMODULE_ERR;

    return REDISMODULE_OK;
}
int RedisModule_Init(RedisModuleCtx *ctx, const char *modulename,
                     int module_version, int api_version);

int RedisModule_CreateCommand(RedisModuleCtx *ctx, const char *cmdname,
                              RedisModuleCmdFunc cmdfunc);

新規コマンド作成のためのプロトタイプ

int mycommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
loadmodule mymodule.so foo bar 1234
const char *RedisModule_StringPtrLen(RedisModuleString *string, size_t *len);
RedisModuleString *RedisModule_CreateString(RedisModuleCtx *ctx, const char *ptr, size_t len);
void RedisModule_FreeString(RedisModuleString *str);
# 文字列の例
RedisModuleString *mystr = RedisModule_CreateStringFromLongLong(ctx,10);


# 数の例
long long myval;
if (RedisModule_StringToLongLong(ctx,argv[1],&myval) == REDISMODULE_OK) {
    /* Do something with 'myval' */
}
RedisModuleCallReply *reply;
reply = RedisModule_Call(ctx,"INCR","sc",argv[1],"10");
- フォーマット指定子
    - c -- Null terminated C string pointer.
    - b -- C buffer, two arguments needed: C string pointer and size_t length.
    - s -- RedisModuleString as received in argv or by other Redis module APIs returning a RedisModuleString object.
    - l -- Long long integer.
    - v -- Array of RedisModuleString objects.
    - ! -- This modifier just tells the function to replicate the command to slaves and AOF. It is ignored from the point of view of arguments parsing.
reply = RedisModule_Call(ctx,"INCR","sc",argv[1],"10");
if (RedisModule_CallReplyType(reply) == REDISMODULE_REPLY_INTEGER) {
    long long myval = RedisModule_CallReplyInteger(reply);
    /* Do something with myval. */
}


size_t reply_len = RedisModule_CallReplyLength(reply);


long long reply_integer_val = RedisModule_CallReplyInteger(reply);


RedisModuleCallReply *subreply;
subreply = RedisModule_CallReplyArrayElement(reply,idx);


size_t len;
char *ptr = RedisModule_CallReplyStringPtr(reply,&len);


RedisModuleString *mystr = RedisModule_CreateStringFromCallReply(myreply);
- Valid reply types
    - REDISMODULE_REPLY_STRING Bulk string or status replies.
    - REDISMODULE_REPLY_ERROR Errors.
    - REDISMODULE_REPLY_INTEGER Signed 64 bit integers.
    - REDISMODULE_REPLY_ARRAY Array of replies.
    - REDISMODULE_REPLY_NULL NULL reply.
RedisModule_ReplyWithError(ctx,"ERR invalid arguments");

RedisModule_ReplyWithLongLong(ctx,12345);

RedisModule_ReplyWithSimpleString(ctx,"OK");

int RedisModule_ReplyWithStringBuffer(RedisModuleCtx *ctx, const char *buf, size_t len);
int RedisModule_ReplyWithString(RedisModuleCtx *ctx, RedisModuleString *str);

RedisModule_ReplyWithArray(ctx,2);
RedisModule_ReplyWithStringBuffer(ctx,"age",3);
RedisModule_ReplyWithLongLong(ctx,22);

RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN);

RedisModule_ReplySetArrayLength(ctx, number_of_items);
RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN);
number_of_factors = 0;
while(still_factors) {
    RedisModule_ReplyWithLongLong(ctx, some_factor);
    number_of_factors++;
}
RedisModule_ReplySetArrayLength(ctx, number_of_factors);
if (argc != 2) return RedisModule_WrongArity(ctx);
RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
    REDISMODULE_READ|REDISMODULE_WRITE);

int keytype = RedisModule_KeyType(key);
if (keytype != REDISMODULE_KEYTYPE_STRING &&
    keytype != REDISMODULE_KEYTYPE_EMPTY)
{
    RedisModule_CloseKey(key);
    return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
}
RedisModuleKey *key;
key = RedisModule_OpenKey(ctx,argv[1],REDISMODULE_READ);
RedisModule_CloseKey(key);
int keytype = RedisModule_KeyType(key);
- 返り値
    - REDISMODULE_KEYTYPE_EMPTY
    - REDISMODULE_KEYTYPE_STRING
    - REDISMODULE_KEYTYPE_LIST
    - REDISMODULE_KEYTYPE_HASH
    - REDISMODULE_KEYTYPE_SET
    - REDISMODULE_KEYTYPE_ZSET
RedisModuleKey *key;
key = RedisModule_OpenKey(ctx,argv[1],REDISMODULE_READ);
if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_EMPTY) {
    RedisModule_StringSet(key,argv[2]);
}
mstime_t RedisModule_GetExpire(RedisModuleKey *key);

int RedisModule_SetExpire(RedisModuleKey *key, mstime_t expire);

既存の文字列へのアクセスは、DMA (direct memory access)を使用

size_t len, j;
char *myptr = RedisModule_StringDMA(key,&len,REDISMODULE_WRITE);
for (j = 0; j < len; j++) myptr[j] = 'A';
RedisModule_StringTruncate(mykey,1024);
int RedisModule_ListPush(RedisModuleKey *key, int where, RedisModuleString *ele);
RedisModuleString *RedisModule_ListPop(RedisModuleKey *key, int where);
- where で以下のマクロを指定
    - REDISMODULE_LIST_HEAD
    - REDISMODULE_LIST_TAIL

Replicating commands

reply = RedisModule_Call(ctx,"INCR","!sc",argv[1],"10");

影響がないか確認

RedisModule_ReplicateVerbatim(ctx);

どのコマンドをレプリケーションするべきか指定可能

RedisModule_Replicate(ctx,"INCRBY","cl","foo",my_increment);
void *RedisModule_Alloc(size_t bytes);
void* RedisModule_Realloc(void *ptr, size_t bytes);
void RedisModule_Free(void *ptr);
void RedisModule_Calloc(size_t nmemb, size_t size);
char *RedisModule_Strdup(const char *str);
void *RedisModule_PoolAlloc(RedisModuleCtx *ctx, size_t bytes);
RedisModule_IsKeysPositionRequest(ctx);
RedisModule_KeyAtPos(ctx,pos);

Implementing native data types

static RedisModuleType *MyType;
#define MYTYPE_ENCODING_VERSION 0

int RedisModule_OnLoad(RedisModuleCtx *ctx) {
RedisModuleTypeMethods tm = {
    .version = REDISMODULE_TYPE_METHOD_VERSION,
    .rdb_load = MyTypeRDBLoad,
    .rdb_save = MyTypeRDBSave,
    .aof_rewrite = MyTypeAOFRewrite,
    .free = MyTypeFree
};

    MyType = RedisModule_CreateDataType(ctx, "MyType-AZ",
    MYTYPE_ENCODING_VERSION, &tm);
    if (MyType == NULL) return REDISMODULE_ERR;
}
typedef void *(*RedisModuleTypeLoadFunc)(RedisModuleIO *rdb, int encver);
typedef void (*RedisModuleTypeSaveFunc)(RedisModuleIO *rdb, void *value);
typedef void (*RedisModuleTypeRewriteFunc)(RedisModuleIO *aof, RedisModuleString *key, void *value);
typedef size_t (*RedisModuleTypeMemUsageFunc)(void *value);
typedef void (*RedisModuleTypeDigestFunc)(RedisModuleDigest *digest, void *value);
typedef void (*RedisModuleTypeFreeFunc)(void *value);
RedisModuleKey *key = RedisModule_OpenKey(ctx,keyname,REDISMODULE_WRITE);
struct some_private_struct *data = createMyDataStructure();
RedisModule_ModuleTypeSetValue(key,MyType,data);
struct some_private_struct *data;
data = RedisModule_ModuleTypeGetValue(key);
:
:
if (RedisModule_ModuleTypeGetType(key) == MyType) {
    /* ... do something ... */
}

鍵が空かといったチェックも必要

RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
    REDISMODULE_READ|REDISMODULE_WRITE);
int type = RedisModule_KeyType(key);
if (type != REDISMODULE_KEYTYPE_EMPTY &&
    RedisModule_ModuleTypeGetType(key) != MyType)
{
    return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
}

鍵の型が正しいことを確認できた場合、以下のように処理

/* Create an empty value object if the key is currently empty. */
struct some_private_struct *data;
if (type == REDISMODULE_KEYTYPE_EMPTY) {
    data = createMyDataStructure();
    RedisModule_ModuleTypeSetValue(key,MyTyke,data);
} else {
    data = RedisModule_ModuleTypeGetValue(key);
}
/* Do something with 'data'... */
typedef void (*RedisModuleTypeFreeFunc)(void *value);

実装例

void MyTypeFreeCallback(void *value) {
    RedisModule_Free(value);
}
void RedisModule_SaveUnsigned(RedisModuleIO *io, uint64_t value);
uint64_t RedisModule_LoadUnsigned(RedisModuleIO *io);
void RedisModule_SaveSigned(RedisModuleIO *io, int64_t value);
int64_t RedisModule_LoadSigned(RedisModuleIO *io);
void RedisModule_SaveString(RedisModuleIO *io, RedisModuleString *s);
void RedisModule_SaveStringBuffer(RedisModuleIO *io, const char *str, size_t len);
RedisModuleString *RedisModule_LoadString(RedisModuleIO *io);
char *RedisModule_LoadStringBuffer(RedisModuleIO *io, size_t *lenptr);
void RedisModule_SaveDouble(RedisModuleIO *io, double value);
double RedisModule_LoadDouble(RedisModuleIO *io);

以下のような独自の型の場合

struct double_array {
    size_t count;
    double *values;
};

以下のようにラッパー関数を実装

void DoubleArrayRDBSave(RedisModuleIO *io, void *ptr) {
    struct dobule_array *da = ptr;
    RedisModule_SaveUnsigned(io,da->count);
    for (size_t j = 0; j < da->count; j++)
        RedisModule_SaveDouble(io,da->values[j]);
}
void *DoubleArrayRDBLoad(RedisModuleIO *io, int encver) {
    if (encver != DOUBLE_ARRAY_ENC_VER) {
        /* We should actually log an error here, or try to implement
           the ability to load older versions of our data structure. */
        return NULL;
    }

    struct double_array *da;
    da = RedisModule_Alloc(sizeof(*da));
    da->count = RedisModule_LoadUnsigned(io);
    da->values = RedisModule_Alloc(da->count * sizeof(double));
    for (size_t j = 0; j < da->count; j++)
        da->values = RedisModule_LoadDouble(io);
    return da;
}
#define malloc RedisModule_Alloc
#define realloc RedisModule_Realloc
#define free RedisModule_Free
#define strdup RedisModule_Strdup

Blocking operations

#define REDISMODULE_EXPERIMENTAL_API
#include "redismodule.h"
RedisModuleBlockedClient *RedisModule_BlockClient(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(void*), long long timeout_ms);

int RedisModule_UnblockClient(RedisModuleBlockedClient *bc, void *privdata);
void *RedisModule_GetBlockedClientPrivateData(RedisModuleCtx *ctx);

コマンド実装例

int Example_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
                         int argc)
{
    RedisModuleBlockedClient *bc =
        RedisModule_BlockClient(ctx,reply_func,timeout_func,NULL,0);

    pthread_t tid;
    pthread_create(&tid,NULL,threadmain,bc);

    return REDISMODULE_OK;
}

void *threadmain(void *arg) {
    RedisModuleBlockedClient *bc = arg;

    sleep(1); /* Wait one second and unblock. */
    RedisModule_UnblockClient(bc,NULL);
}
int reply_func(RedisModuleCtx *ctx, RedisModuleString **argv,
               int argc)
{
    return RedisModule_ReplyWithSimpleString(ctx,"Hello!");
}

int timeout_func(RedisModuleCtx *ctx, RedisModuleString **argv,
               int argc)
{
    return RedisModule_ReplyWithNull(ctx);
}

クライアントへ何を返すべきか分かるように、threadmain 関数を以下のように改良

void *threadmain(void *arg) {
    RedisModuleBlockedClient *bc = arg;

    sleep(1); /* Wait one second and unblock. */

    long *mynumber = RedisModule_Alloc(sizeof(long));
    *mynumber = rand();
    RedisModule_UnblockClient(bc,mynumber);
}
int reply_func(RedisModuleCtx *ctx, RedisModuleString **argv,
               int argc)
{
    long *mynumber = RedisModule_GetBlockedClientPrivateData(ctx);
    /* IMPORTANT: don't free mynumber here, but in the
     * free privdata callback. */
    return RedisModule_ReplyWithLongLong(ctx,mynumber);
}
void free_privdata(void *privdata) {
    RedisModule_Free(privdata);
}
int Example_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
                         int argc)
{
    RedisModuleBlockedClient *bc =
        RedisModule_BlockClient(ctx,reply_func,timeout_func,NULL,0);

    pthread_t tid;
    if (pthread_create(&tid,NULL,threadmain,bc) != 0) {
        RedisModule_AbortBlock(bc);
        RedisModule_ReplyWithError(ctx,"Sorry can't create a thread");
    }

    return REDISMODULE_OK;
}
int RedisModule_IsBlockedReplyRequest(RedisModuleCtx *ctx);
int RedisModule_IsBlockedTimeoutRequest(RedisModuleCtx *ctx);

実装例

int Example_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
                         int argc)
{
    if (RedisModule_IsBlockedReplyRequest(ctx)) {
        long *mynumber = RedisModule_GetBlockedClientPrivateData(ctx);
        return RedisModule_ReplyWithLongLong(ctx,mynumber);
    } else if (RedisModule_IsBlockedTimeoutRequest) {
        return RedisModule_ReplyWithNull(ctx);
    }

    RedisModuleBlockedClient *bc =
        RedisModule_BlockClient(ctx,reply_func,timeout_func,NULL,0);

    pthread_t tid;
    if (pthread_create(&tid,NULL,threadmain,bc) != 0) {
        RedisModule_AbortBlock(bc);
        RedisModule_ReplyWithError(ctx,"Sorry can't create a thread");
    }

    return REDISMODULE_OK;
}

Redis modules API reference

void *RedisModule_ lloc(size_t bytes)         
void *RedisModule_Calloc(size_t nmemb, size_t size)         
void* RedisModule_Realloc(void *ptr, size_t bytes)         
void RedisModule_Free(void *ptr)         
char *RedisModule_Strdup(const char *str)         
void *RedisModule_Pool lloc(RedisModuleCt  *ct , size_t bytes)         
int RedisModule_Get pi(const char *funcname, void **targetPtrPtr)         
int RedisModule_IsKeysPositionRequest(RedisModuleCt  *ct )         
void RedisModule_Key tPos(RedisModuleCt  *ct , int pos)         
if (RedisModule_IsKeysPositionRequest(ct )) {            RedisModule_Key tPos(ct ,1)             RedisModule_Key tPos(ct ,2)         }        
int RedisModule_CreateCommand(RedisModuleCt  *ct , const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep)         
 int MyCommand_RedisCommand(RedisModuleCt  *ct , RedisModuleString **argv, int argc)         
void RedisModule_SetModule ttribs(RedisModuleCt  *ct , const char *name, int ver, int apiver)         
long long RedisModule_Milliseconds(void)         
void RedisModule_ utoMemory(RedisModuleCt  *ct )         
RedisModuleString *RedisModule_CreateString(RedisModuleCt  *ct , const char *ptr, size_t len)         
RedisModuleString *RedisModule_CreateStringPrintf(RedisModuleCt  *ct , const char *fmt, ...)         
RedisModuleString *RedisModule_CreateStringFromLongLong(RedisModuleCt  *ct , long long ll)         
RedisModuleString *RedisModule_CreateStringFromString(RedisModuleCt  *ct , const RedisModuleString *str)         
void RedisModule_FreeString(RedisModuleCt  *ct , RedisModuleString *str)         
void RedisModule_RetainString(RedisModuleCt  *ct , RedisModuleString *str)         
const char *RedisModule_StringPtrLen(const RedisModuleString *str, size_t *len)         
int RedisModule_StringToLongLong(const RedisModuleString *str, long long *ll)         
int RedisModule_StringToDouble(const RedisModuleString *str, double *d)         
int RedisModule_StringCompare(RedisModuleString *a, RedisModuleString *b)         
int RedisModule_String ppendBuffer(RedisModuleCt  *ct , RedisModuleString *str, const char *buf, size_t len)         
int RedisModule_Wrong rity(RedisModuleCt  *ct )         
if (argc != 3) return RedisModule_Wrong rity(ct )         
int RedisModule_ReplyWithLongLong(RedisModuleCt  *ct , long long ll)         
int RedisModule_ReplyWithError(RedisModuleCt  *ct , const char *err)         
RedisModule_ReplyWithError(ct , quot ERR Wrong Type quot )         
RedisModule_ReplyWithError(ct , quot Wrong Type quot )         
int RedisModule_ReplyWithSimpleString(RedisModuleCt  *ct , const char *msg)         
int RedisModule_ReplyWith rray(RedisModuleCt  *ct , long len)         
void RedisModule_ReplySet rrayLength(RedisModuleCt  *ct , long len)         
 RedisModule_ReplyWith rray(ct ,REDISMODULE_POSTPONED_ RR Y_LEN)          RedisModule_ReplyWithLongLong(ct ,1)          RedisModule_ReplyWith rray(ct ,REDISMODULE_POSTPONED_ RR Y_LEN)          RedisModule_ReplyWithLongLong(ct ,1 )          RedisModule_ReplyWithLongLong(ct ,2 )          RedisModule_ReplyWithLongLong(ct ,3 )          RedisModule_ReplySet rrayLength(ct ,3)  // Set len of 1 ,2 ,3  array.         RedisModule_ReplySet rrayLength(ct ,2)  // Set len of top array        
int RedisModule_ReplyWithStringBuffer(RedisModuleCt  *ct , const char *buf, size_t len)         
int RedisModule_ReplyWithString(RedisModuleCt  *ct , RedisModuleString *str)         
int RedisModule_ReplyWithNull(RedisModuleCt  *ct )         
int RedisModule_ReplyWithCallReply(RedisModuleCt  *ct , RedisModuleCallReply *reply)         
int RedisModule_ReplyWithDouble(RedisModuleCt  *ct , double d)         
int RedisModule_Replicate(RedisModuleCt  *ct , const char *cmdname, const char *fmt, ...)         
int RedisModule_ReplicateVerbatim(RedisModuleCt  *ct )         
unsigned long long RedisModule_GetClientId(RedisModuleCt  *ct )         
int RedisModule_GetSelectedDb(RedisModuleCt  *ct )         
int RedisModule_SelectDb(RedisModuleCt  *ct , int newid)         
void *RedisModule_OpenKey(RedisModuleCt  *ct , robj *keyname, int mode)         
void RedisModule_CloseKey(RedisModuleKey *key)         
int RedisModule_KeyType(RedisModuleKey *key)         
size_t RedisModule_ValueLength(RedisModuleKey *key)         
int RedisModule_DeleteKey(RedisModuleKey *key)         
mstime_t RedisModule_GetE pire(RedisModuleKey *key)         
int RedisModule_SetE pire(RedisModuleKey *key, mstime_t e pire)         
int RedisModule_StringSet(RedisModuleKey *key, RedisModuleString *str)         
char *RedisModule_StringDM (RedisModuleKey *key, size_t *len, int mode)         
REDISMODULE_RE D -- Read access        REDISMODULE_WRITE -- Write access        
int RedisModule_StringTruncate(RedisModuleKey *key, size_t newlen)         
int RedisModule_ListPush(RedisModuleKey *key, int where, RedisModuleString *ele)         
RedisModuleString *RedisModule_ListPop(RedisModuleKey *key, int where)         
int RedisModule_Zset ddFlagsToCoreFlags(int flags)         
int RedisModule_Zset ddFlagsFromCoreFlags(int flags)         
int RedisModule_Zset dd(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr)         
REDISMODULE_Z DD_XX: Element must already e ist. Do nothing otherwise.        REDISMODULE_Z DD_NX: Element must not e ist. Do nothing otherwise.        
REDISMODULE_Z DD_ DDED: The new element was added to the sorted set.        REDISMODULE_Z DD_UPD TED: The score of the element was updated.        REDISMODULE_Z DD_NOP: No operation was performed because XX or NX flags.        
int RedisModule_ZsetIncrby(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr, double *newscore)         
int RedisModule_ZsetRem(RedisModuleKey *key, RedisModuleString *ele, int *deleted)         
int RedisModule_ZsetScore(RedisModuleKey *key, RedisModuleString *ele, double *score)         
void RedisModule_ZsetRangeStop(RedisModuleKey *key)         
int RedisModule_ZsetRangeEndReached(RedisModuleKey *key)         
int RedisModule_ZsetFirstInScoreRange(RedisModuleKey *key, double min, double ma , int mine , int ma e )         
int RedisModule_ZsetLastInScoreRange(RedisModuleKey *key, double min, double ma , int mine , int ma e )         
int RedisModule_ZsetFirstInLe Range(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *ma )         
int RedisModule_ZsetLastInLe Range(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *ma )         
RedisModuleString *RedisModule_ZsetRangeCurrentElement(RedisModuleKey *key, double *score)         
int RedisModule_ZsetRangeNe t(RedisModuleKey *key)         
int RedisModule_ZsetRangePrev(RedisModuleKey *key)         
int RedisModule_HashSet(RedisModuleKey *key, int flags, ...)         
 RedisModule_HashSet(key,REDISMODULE_H SH_NONE,argv[1],argv[2],NULL)         
 RedisModule_HashSet(key,REDISMODULE_H SH_NONE,argv[1],                             REDISMODULE_H SH_DELETE,NULL)         
REDISMODULE_H SH_NX: The operation is performed only if the field was not                             already e isting in the hash.        REDISMODULE_H SH_XX: The operation is performed only if the field was                             already e isting, so that a new value could be                             associated to an e isting filed, but no new fields                             are created.        REDISMODULE_H SH_CFIELDS: The field names passed are null terminated C                                  strings instead of RedisModuleString objects.        
 RedisModule_HashSet(key,REDISMODULE_H SH_CFIELDS, quot foo quot ,                             REDISMODULE_H SH_DELETE,NULL)         
int RedisModule_HashGet(RedisModuleKey *key, int flags, ...)         
 RedisModuleString *first, *second          RedisModule_HashGet(mykey,REDISMODULE_H SH_NONE,argv[1], amp first,                         argv[2], amp second,NULL)         
 RedisModuleString *username, *hashedpass          RedisModule_HashGet(mykey, quot username quot , amp username, quot hp quot , amp hashedpass, NULL)         
 int e ists          RedisModule_HashGet(mykey,argv[1], amp e ists,NULL)         
void RedisModule_FreeCallReply_Rec(RedisModuleCallReply *reply, int freenested)         
void RedisModule_FreeCallReply(RedisModuleCallReply *reply)         
int RedisModule_CallReplyType(RedisModuleCallReply *reply)         
size_t RedisModule_CallReplyLength(RedisModuleCallReply *reply)         
RedisModuleCallReply *RedisModule_CallReply rrayElement(RedisModuleCallReply *reply, size_t id )         
long long RedisModule_CallReplyInteger(RedisModuleCallReply *reply)         
const char *RedisModule_CallReplyStringPtr(RedisModuleCallReply *reply, size_t *len)         
RedisModuleString *RedisModule_CreateStringFromCallReply(RedisModuleCallReply *reply)         
RedisModuleCallReply *RedisModule_Call(RedisModuleCt  *ct , const char *cmdname, const char *fmt, ...)         
const char *RedisModule_CallReplyProto(RedisModuleCallReply *reply, size_t *len)         
moduleType *RedisModule_CreateDataType(RedisModuleCt  *ct , const char *name, int encver, void *typemethods_ptr)         
 // Optional fields         .digest = myType_DigestCallBack,         .mem_usage = myType_MemUsageCallBack,        
 static RedisModuleType *BalancedTreeType                  int RedisModule_OnLoad(RedisModuleCt  *ct ) {             // some code here ...             BalancedTreeType = RM_CreateDataType(...)          }        
int RedisModule_ModuleTypeSetValue(RedisModuleKey *key, moduleType *mt, void *value)         
moduleType *RedisModule_ModuleTypeGetType(RedisModuleKey *key)         
void *RedisModule_ModuleTypeGetValue(RedisModuleKey *key)         
void RedisModule_SaveUnsigned(RedisModuleIO *io, uint64_t value)         
uint64_t RedisModule_LoadUnsigned(RedisModuleIO *io)         
void RedisModule_SaveSigned(RedisModuleIO *io, int64_t value)         
int64_t RedisModule_LoadSigned(RedisModuleIO *io)         
void RedisModule_SaveString(RedisModuleIO *io, RedisModuleString *s)         
void RedisModule_SaveStringBuffer(RedisModuleIO *io, const char *str, size_t len)         
RedisModuleString *RedisModule_LoadString(RedisModuleIO *io)         
char *RedisModule_LoadStringBuffer(RedisModuleIO *io, size_t *lenptr)         
void RedisModule_SaveDouble(RedisModuleIO *io, double value)         
double RedisModule_LoadDouble(RedisModuleIO *io)         
void RedisModule_SaveFloat(RedisModuleIO *io, float value)         
float RedisModule_LoadFloat(RedisModuleIO *io)         
void RedisModule_Digest ddStringBuffer(RedisModuleDigest *md, unsigned char *ele, size_t len)         
foreach element {             ddElement(element)             EndSequence()         }        
foreach key,value {             ddElement(key)              ddElement(value)             EndSquence()         }        
foreach element {             ddElement(element)         }        EndSequence()         
void RedisModule_Digest ddLongLong(RedisModuleDigest *md, long long ll)         
void RedisModule_DigestEndSequence(RedisModuleDigest *md)         
void RedisModule_Emit OF(RedisModuleIO *io, const char *cmdname, const char *fmt, ...)         
void RedisModule_LogRaw(RedisModule *module, const char *levelstr, const char *fmt, va_list ap)         
 RM_Log()         RM_LogIOError()        
void RedisModule_Log(RedisModuleCt  *ct , const char *levelstr, const char *fmt, ...)         
void RedisModule_LogIOError(RedisModuleIO *io, const char *levelstr, const char *fmt, ...)         
RedisModuleBlockedClient *RedisModule_BlockClient(RedisModuleCt  *ct , RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(void*), long long timeout_ms)         
reply_callback:  called after a successful RedisModule_UnblockClient()                         call in order to reply to the client and unblock it.                reply_timeout:   called when the timeout is reached in order to send an                         error to the client.                free_privdata:   called in order to free the privata data that is passed                         by RedisModule_UnblockClient() call.        
int RedisModule_UnblockClient(RedisModuleBlockedClient *bc, void *privdata)         
int RedisModule_ bortBlock(RedisModuleBlockedClient *bc)         
int RedisModule_IsBlockedReplyRequest(RedisModuleCt  *ct )         
int RedisModule_IsBlockedTimeoutRequest(RedisModuleCt  *ct )         
void *RedisModule_GetBlockedClientPrivateData(RedisModuleCt  *ct )         
RedisModuleCt  *RedisModule_GetThreadSafeConte t(RedisModuleBlockedClient *bc)         
RedisModule_ThreadSafeCallStart(ct )         ... make your call here ...        RedisModule_ThreadSafeCallStop(ct )         
void RedisModule_FreeThreadSafeConte t(RedisModuleCt  *ct )         
void RedisModule_ThreadSafeConte tLock(RedisModuleCt  *ct )         
void RedisModule_ThreadSafeConte tUnlock(RedisModuleCt  *ct )         

Tutorials & FAQ

Introduction to Redis data types

Writing a simple Twitter clone with PHP and Redis

function isLoggedIn() {
    global $User, $_COOKIE;

    if (isset($User)) return true;

    if (isset($_COOKIE['auth'])) {
        $r = redisLink();
        $authcookie = $_COOKIE['auth'];
        if ($userid = $r->hget("auths",$authcookie)) {
            if ($r->hget("user:$userid","auth") != $authcookie) return false;
            loadUserInfo($userid);
            return true;
        }
    }
    return false;
}

function loadUserInfo($userid) {
    global $User;

    $r = redisLink();
    $User['id'] = $userid;
    $User['username'] = $r->hget("user:$userid","username");
    return true;
}
include("retwis.php");

if (!isLoggedIn()) {
    header("Location: index.php");
    exit;
}

$r = redisLink();
$newauthsecret = getrand();
$userid = $User['id'];
$oldauthsecret = $r->hget("user:$userid","auth");

$r->hset("user:$userid","auth",$newauthsecret);
$r->hset("auths",$newauthsecret,$userid);
$r->hdel("auths",$oldauthsecret);

header("Location: index.php");
include("retwis.php");

if (!isLoggedIn() || !gt("status")) {
    header("Location:index.php");
    exit;
}

$r = redisLink();
$postid = $r->incr("next_post_id");
$status = str_replace("\n"," ",gt("status"));
$r->hmset("post:$postid","user_id",$User['id'],"time",time(),"body",$status);
$followers = $r->zrange("followers:".$User['id'],0,-1);
$followers[] = $User['id']; /* Add the post to our own posts too */

foreach($followers as $fid) {
    $r->lpush("posts:$fid",$postid);
}
# Push the post on the timeline, and trim the timeline to the
# newest 1000 elements.
$r->lpush("timeline",$postid);
$r->ltrim("timeline",0,1000);

header("Location: index.php");

Auto complete with Redis

Data types short summary

FAQ

Administration

Redis-cli

コマンドライン使用方法

$ redis-cli incr mycounter
(integer) 7

$ redis-cli incr mycounter > /tmp/output.txt
$ cat /tmp/output.txt
8

$ redis-cli --raw incr mycounter
9
# Cf. --no-raw


$ redis-cli -h redis15.localnet.org -p 6390 ping
PONG
# デフォルト: 127.0.0.1 port 6379

$ redis-cli -a myUnguessablePazzzzzword123 ping
PONG


# データベース番号を指定して実行
$ redis-cli flushall
OK
$ redis-cli -n 1 incr a
(integer) 1
$ redis-cli -n 1 incr a
(integer) 2
$ redis-cli -n 2 incr a
(integer) 1

$ redis-cli -u redis://p%40ssw0rd@redis-16379.hosted.com:16379/0 ping
PONG
$ redis-cli -r 5 incr foo
(integer) 1
(integer) 2
(integer) 3
(integer) 4
(integer) 5

$ redis-cli -r -1 -i 1 INFO | grep rss_human
used_memory_rss_human:1.38M
used_memory_rss_human:1.38M
used_memory_rss_human:1.38M
... a new line will be printed each second ...
$ cat /tmp/script.lua
return redis.call('set',KEYS[1],ARGV[1])
$ redis-cli --eval /tmp/script.lua foo , bar
OK

インタラクティブモード

$ redis-cli
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> connect metal 6379
metal:6379> ping
PONG
127.0.0.1:6379> 5 incr mycounter
(integer) 1
(integer) 2
(integer) 3
(integer) 4
(integer) 5
help @<category>
help <commandname>

操作の特殊モード

$ redis-cli --stat
------- data ------ --------------------- load -------------------- - child -
keys       mem      clients blocked requests            connections
506        1015.00K 1       0       24 (+0)             7
506        1015.00K 1       0       25 (+1)             7
506        3.40M    51      0       60461 (+60436)      57
$ redis-cli --bigkeys

# Scanning the entire keyspace to find biggest keys as well as
# average sizes per key type.  You can use -i 0.1 to sleep 0.1 sec
# per 100 SCAN commands (not usually needed).

[00.00%] Biggest string found so far 'key-419' with 3 bytes
[05.14%] Biggest list   found so far 'mylist' with 100004 items
[35.77%] Biggest string found so far 'counter:__rand_int__' with 6 bytes
[73.91%] Biggest hash   found so far 'myobject' with 3 fields

-------- summary -------

Sampled 506 keys in the keyspace!
Total key length in bytes is 3452 (avg len 6.82)
:
$ redis-cli --scan | head -10
key-419
key-71
key-236
$ redis-cli psubscribe '*'
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "*"
3) (integer) 1
$ redis-cli monitor
OK
1460100081.165665 [0 127.0.0.1:51706] "set" "foo" "bar"
1460100083.053365 [0 127.0.0.1:51707] "get" "foo"
$ redis-cli --latency
min: 0, max: 1, avg: 0.19 (427 samples)

$ redis-cli --latency-history
min: 0, max: 1, avg: 0.14 (1314 samples) -- 15.01 seconds range
min: 0, max: 1, avg: 0.18 (1299 samples) -- 15.00 seconds range
min: 0, max: 1, avg: 0.20 (113 samples)^C

$ redis-cli --latency-dist
(output not displayed, requires a color terminal, try it!)
$ redis-cli --rdb /tmp/dump.rdb
SYNC sent to master, writing 13256 bytes to '/tmp/dump.rdb'
Transfer finished with success.
$ echo $?
0
$ ./redis-cli --lru-test 10000000
156000 Gets/sec | Hits: 4552 (2.92%) | Misses: 151448 (97.08%)
153750 Gets/sec | Hits: 12906 (8.39%) | Misses: 140844 (91.61%)
159250 Gets/sec | Hits: 21811 (13.70%) | Misses: 137439 (86.30%)
151000 Gets/sec | Hits: 27615 (18.29%) | Misses: 123385 (81.71%)

Configuration

slaveof 127.0.0.1 6380
./redis-server --port 6380 --slaveof 127.0.0.1 6379
maxmemory 2mb
maxmemory-policy allkeys-lru

Replication

Persistence

Redis Administration

Security

Encryption

Signals Handling

Connections Handling

$ ulimit -Sn 100000 # This will only work if hard limit is big enough.
$ sysctl -w fs.file-max=100000

High Availability

port 5000
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1
127.0.0.1:5000> SENTINEL get-master-addr-by-name mymaster
1) "127.0.0.1"
2) "6379"
$ redis-cli -p 6379 DEBUG sleep 30

Sentinel API

発展

Benchmarks

Usage: redis-benchmark [-h <host>] [-p <port>] [-c <clients>] [-n <requests]> [-k <boolean>]

 -h <hostname>      Server hostname (default 127.0.0.1)
 -p <port>          Server port (default 6379)
 -s <socket>        Server socket (overrides host and port)
 -a <password>      Password for Redis Auth
 -c <clients>       Number of parallel connections (default 50)
 -n <requests>      Total number of requests (default 100000)
 -d <size>          Data size of SET/GET value in bytes (default 2)
 --dbnum <db>       SELECT the specified db number (default 0)
 -k <boolean>       1=keep alive 0=reconnect (default 1)
 -r <keyspacelen>   Use random keys for SET/GET/INCR, random values for SADD
  Using this option the benchmark will expand the string __rand_int__
  inside an argument with a 12 digits number in the specified range
  from 0 to keyspacelen-1. The substitution changes every time a command
  is executed. Default tests use this to hit random keys in the
  specified range.
 -P <numreq>        Pipeline <numreq> requests. Default 1 (no pipeline).
 -q                 Quiet. Just show query/sec values
 --csv              Output in CSV format
 -l                 Loop. Run the tests forever
 -t <tests>         Only run the comma separated list of tests. The test
                    names are the same as the ones produced as output.
 -I                 Idle mode. Just open N idle connections and wait.
$ redis-benchmark -q -n 100000

# テストするコマンド指定
$ redis-benchmark -t set,lpush -n 100000 -q
SET: 74239.05 requests per second
LPUSH: 79239.30 requests per second

# 具体的なスクリプト指定
$ redis-benchmark -n 100000 -q script load "redis.call('set','foo','bar')"
script load redis.call('set','foo','bar'): 69881.20 requests per second




# キー空間のサイズ指定: -r オプション
$ redis-cli flushall
OK

$ redis-benchmark -t set -r 100000 -n 1000000
====== SET ======
  1000000 requests completed in 13.86 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1

99.76% `<=` 1 milliseconds
99.98% `<=` 2 milliseconds
100.00% `<=` 3 milliseconds
100.00% `<=` 3 milliseconds
72144.87 requests per second

$ redis-cli dbsize
(integer) 99993



# パイプライン : -P オプション
$ redis-benchmark -n 1000000 -t set,get -P 16 -q
SET: 403063.28 requests per second
GET: 508388.41 requests per second
# unix domain socket を使用
$ numactl -C 6 ./redis-benchmark -q -n 100000 -s /tmp/redis.sock -d 256

# TCP loopback を使用
$ numactl -C 6 ./redis-benchmark -q -n 100000 -d 256

Redis Releases

Embedded and IoT

Redis on ARM and Raspberry Pi

Troubleshooting

Redis problems?

Redis latency problems troubleshooting

> INFO Stats
# Stats
total_connections_received:10
total_commands_processed:796661
instantaneous_ops_per_sec:3
total_net_input_bytes:28671383
total_net_output_bytes:505517208
instantaneous_input_kbps:0.14
instantaneous_output_kbps:4.63
rejected_connections:0
sync_full:2
sync_partial_ok:0
sync_partial_err:0
expired_keys:0
evicted_keys:0
keyspace_hits:0
keyspace_misses:0
pubsub_channels:0
pubsub_patterns:0
latest_fork_usec:135
migrate_cached_sockets:0
$ redis-cli -h testredis.meriux.ng.0001.usw2.cache.amazonaws.com info | grep process_id
process_id:1
$ cd /proc/1
$ ls
ls: cannot read symbolic link cwd: Permission denied
ls: cannot read symbolic link root: Permission denied
ls: cannot read symbolic link exe: Permission denied
attr        cmdline          cwd      fdinfo   limits     mem         net        oom_score      projid_map  sessionid  stat     task
auxv        comm             environ  gid_map  loginuid   mountinfo   ns         oom_score_adj  root        setgroups  statm    timerslack_ns
cgroup      coredump_filter  exe      io       map_files  mounts      numa_maps  pagemap        sched       smaps      status   uid_map
clear_refs  cpuset           fd       latency  maps       mountstats  oom_adj    personality    schedstat   stack      syscall  wchan
- /proc/<プロセスID>以下のsmapsというファイルでメモリのレイアウトの詳細が見られる。

```
[ec2-user@ip-172-31-19-138 1]$ sudo cat smaps | grep Swap:
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
```

- メモリマップのサイズも出力してみる。

```
$ cat smaps | egrep '^(Swap|Size)'
```

- vmstatやiostatでも裏取り。

```
$ vmstat 1
$ iostat -xk 1
```
$ sudo strace -f -p $(pidof redis-server) -T -e trace=fdatasync,write 2>&1 | grep -v '0.0' | grep -v unfinished

SLOWLOG – Redis

Redis latency monitoring framework – Redis

CONFIG SET latency-monitor-threshold 100

Redis debugging guide – Redis

$ redis-cli info | grep process_id
process_id:58414
$ gdb /usr/local/bin/redis-server 58414
(gdb) bt
(gdb) info registers

Others

Redis Cluster

Redis Cluster tutorial

$ ./redis-trib.rb reshard --from <node-id> --to <node-id> --slots <number of slots> --yes <host>:<port>
$ ruby consistency-test.rb
925 R (0 err) | 925 W (0 err) |
5030 R (0 err) | 5030 W (0 err) |
9261 R (0 err) | 9261 W (0 err) |
13517 R (0 err) | 13517 W (0 err) |

Redis Cluster specification

Redisクラスターの主な構成概要

Ruby

def HASH_SLOT(key)
    s = key.index "{"
    if s
        e = key.index "}",s+1
        if e && e != s+1
            key = key[s+1..e-1]
        end
    end
    crc16(key) % 16384
end

C

unsigned int HASH_SLOT(char *key, int keylen) {
    int s, e; /* start-end indexes of { and } */

    /* Search the first occurrence of '{'. */
    for (s = 0; s < keylen; s++)
        if (key[s] == '{') break;

    /* No '{' ? Hash the whole key. This is the base case. */
    if (s == keylen) return crc16(key,keylen) & 16383;

    /* '{' found? Check if we have the corresponding '}'. */
    for (e = s+1; e < keylen; e++)
        if (key[e] == '}') break;

    /* No '}' or nothing between {} ? Hash the whole key. */
    if (e == keylen || e == s+1) return crc16(key,keylen) & 16383;

    /* If we are here there is both a { and a } on its right. Hash
     * what is in the middle between { and }. */
    return crc16(key+s+1,e-s-1) & 16383;
}

リダイレクションとリシャーディング

障害耐性(フォールトトレランス)

再構成の挙動、伝播、およびフェイルオーバについて

/*
 * Copyright 2001-2010 Georges Menie (www.menie.org)
 * Copyright 2010 Salvatore Sanfilippo (adapted to Redis coding style)
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the University of California, Berkeley nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/* CRC16 implementation according to CCITT standards.
 *
 * Note by @antirez: this is actually the XMODEM CRC 16 algorithm, using the
 * following parameters:
 *
 * Name                       : "XMODEM", also known as "ZMODEM", "CRC-16/ACORN"
 * Width                      : 16 bit
 * Poly                       : 1021 (That is actually x^16 + x^12 + x^5 + 1)
 * Initialization             : 0000
 * Reflect Input byte         : False
 * Reflect Output CRC         : False
 * Xor constant to output CRC : 0000
 * Output for "123456789"     : 31C3
 */

static const uint16_t crc16tab[256]= {
    0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,
    0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,
    0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6,
    0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de,
    0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485,
    0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d,
    0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4,
    0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc,
    0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823,
    0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b,
    0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12,
    0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a,
    0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41,
    0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49,
    0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70,
    0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78,
    0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f,
    0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067,
    0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e,
    0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256,
    0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d,
    0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405,
    0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c,
    0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634,
    0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab,
    0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3,
    0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a,
    0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92,
    0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9,
    0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1,
    0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8,
    0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0
};

uint16_t crc16(const char *buf, int len) {
    int counter;
    uint16_t crc = 0;
    for (counter = 0; counter < len; counter++)
            crc = (crc<<8) ^ crc16tab[((crc>>8) ^ *buf++)&0x00FF];
    return crc;
}

Other distributed systems based on Redis

Roshi

Specifications

Redis Design Drafts

Redis Protocol specification

C: *2\r\n
C: $4\r\n
C: LLEN\r\n
C: $6\r\n
C: mylist\r\n

S: :48293\r\n

Redis RDB format

----------------------------# RDB is a binary format. There are no new lines or spaces in the file.
52 45 44 49 53              # Magic String "REDIS"
00 00 00 03                 # RDB Version Number in big endian. In this case, version = 0003 = 3
----------------------------
FE 00                       # FE = code that indicates database selector. db number = 00
----------------------------# Key-Value pair starts
FD $unsigned int            # FD indicates "expiry time in seconds". After that, expiry time is read as a 4 byte unsigned int
$value-type                 # 1 byte flag indicating the type of value - set, map, sorted set etc.
$string-encoded-key         # The key, encoded as a redis string
$encoded-value              # The value. Encoding depends on $value-type
----------------------------
FC $unsigned long           # FC indicates "expiry time in ms". After that, expiry time is read as a 8 byte unsigned long
$value-type                 # 1 byte flag indicating the type of value - set, map, sorted set etc.
$string-encoded-key         # The key, encoded as a redis string
$encoded-value              # The value. Encoding depends on $value-type
----------------------------
$value-type                 # This key value pair doesn't have an expiry. $value_type guaranteed != to FD, FC, FE and FF
$string-encoded-key
$encoded-value
----------------------------
FE $length-encoding         # Previous db ends, next db starts. Database number read using length encoding.
----------------------------
...                         # Key value pairs for this database, additonal database

FF                          ## End of RDB file indicator
8 byte checksum             ## CRC 64 checksum of the entire file.

Internals

Resources

Redis Cheat Sheet by tasjaevan - Download free from Cheatography - Cheatography.com: Cheat Sheets For Every Occasion

Use cases

Who is using Redis

Useful Links