redis集群搭建及管理命令

文章目录

1.集群的搭建

1.1 快速搭建集群

  • Redis在它的源码中附带了集群自动搭建程序create-cluster,这个程序可以快速构建起一个完整可用的集群以供用户测试
  • create-cluster程序位于源码的utils/create-cluster/create-cluster位置,通过不给定任何参数来执行它,我们可以看到该程序的具体用法:
root@iZbp1do67v9l7zwkcs2b22Z:~/redis-6.0.8/utils/create-cluster# ./create-cluster 
Usage: ./create-cluster [start|create|stop|watch|tail|clean|call]
start       -- Launch Redis Cluster instances.
create [-f] -- Create a cluster using redis-cli --cluster create.
stop        -- Stop Redis Cluster instances.
watch       -- Show CLUSTER NODES output (first 30 lines) of first node.
tail <id>   -- Run tail -f of instance at base port + ID.
tailall     -- Run tail -f for all the log files at once.
clean       -- Remove all instances data, logs, configs.
clean-logs  -- Remove just instances logs.
call <cmd>  -- Call a command (up to 7 arguments) on all nodes.
1) 首先,我们可以通过执行start命令来创建出6个节点,这6个节点的IP地址都为本机,而端口号则为30001~30006:
root@iZbp1do67v9l7zwkcs2b22Z:~/redis-6.0.8/utils/create-cluster# ./create-cluster start 6
Starting 30001
Starting 30002
Starting 30003
Starting 30004
Starting 30005
Starting 30006
2)接着,我们需要使用create命令,把上述6个节点组合成一个集群,其中包含3个主节点和3个从节点:
root@iZbp1do67v9l7zwkcs2b22Z:~/redis-6.0.8/utils/create-cluster# ./create-cluster create
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 127.0.0.1:30005 to 127.0.0.1:30001
Adding replica 127.0.0.1:30006 to 127.0.0.1:30002
Adding replica 127.0.0.1:30004 to 127.0.0.1:30003
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: b1f69cd169cc261f1fa751a79cbeb68fe9b76182 127.0.0.1:30001
   slots:[0-5460] (5461 slots) master
M: b7e0cd010c8d71460e01906b82cbe55869577b15 127.0.0.1:30002
   slots:[5461-10922] (5462 slots) master
M: 9f208f9b12071fa4ab9da6e97811053aa298ecae 127.0.0.1:30003
   slots:[10923-16383] (5461 slots) master
S: 1df0db35e6399a6bf167a841ccf554c5603d740a 127.0.0.1:30004
   replicates b1f69cd169cc261f1fa751a79cbeb68fe9b76182
S: 98ea1b40bd2b7f2b72caa28fa015a2b3b15931ec 127.0.0.1:30005
   replicates b7e0cd010c8d71460e01906b82cbe55869577b15
S: b8fc0917a0a4e3833392eab6d5bc6ee7aa6a4df4 127.0.0.1:30006
   replicates 9f208f9b12071fa4ab9da6e97811053aa298ecae
Can I set the above configuration? (type 'yes' to accept): 
  • create命令会根据现有的节点制定出一个相应的角色和槽分配计划,然后询问你的意见。以上面打印出的计划为例:

●节点30001、30002和30003将被设置为主节点,并且分别负责槽0~5460、槽5461~10922和槽10923~16383。
●节点30004、30005和30006分别被设置为以上3个主节点的从节点。

  • 如果你同意程序给出的这个分配计划,那么只需要输入yes并按下Enter键,程序就会按计划组建集群了:
  • 输入yes后
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join

>>> Performing Cluster Check (using node 127.0.0.1:30001)
M: b1f69cd169cc261f1fa751a79cbeb68fe9b76182 127.0.0.1:30001
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
M: 9f208f9b12071fa4ab9da6e97811053aa298ecae 127.0.0.1:30003
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: 98ea1b40bd2b7f2b72caa28fa015a2b3b15931ec 127.0.0.1:30005
   slots: (0 slots) slave
   replicates b7e0cd010c8d71460e01906b82cbe55869577b15
M: b7e0cd010c8d71460e01906b82cbe55869577b15 127.0.0.1:30002
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: 1df0db35e6399a6bf167a841ccf554c5603d740a 127.0.0.1:30004
   slots: (0 slots) slave
   replicates b1f69cd169cc261f1fa751a79cbeb68fe9b76182
S: b8fc0917a0a4e3833392eab6d5bc6ee7aa6a4df4 127.0.0.1:30006
   slots: (0 slots) slave
   replicates 9f208f9b12071fa4ab9da6e97811053aa298ecae
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
root@iZbp1do67v9l7zwkcs2b22Z:~/redis-6.0.8/utils/create-cluster# 

在这里插入图片描述

3)在成功构建起集群之后,我们就可以使用客户端来连接并使用集群了.

要做到这一点,最简单的就是使用Redis附带的redis-cli客户端。在连接集群节点而不是单机Redis服务器时,我们需要向redis-cli提供c(cluster,集群)参数以指示客户端进入集群模式,并通过h(host,主机地址)参数或**p(port,端口号)**参数指定集群中的某个节点作为入口:

root@iZbp1do67v9l7zwkcs2b22Z:~/redis-6.0.8/utils/create-cluster# redis-cli -c -p 30001
127.0.0.1:30001> ping
PONG
127.0.0.1:30001> 
4)转向

如果接收到命令请求的节点并非负责处理命令所指键的节点,那么客户端将根据节点提示的转向信息再次向正确的节点发送命令请求,Redis集群把这个动作称为“转向”(redirect)。举个例子,如果我们向节点30001发送一个指向键msg的命令请求,但是由于该键所属的槽6257并不是由节点30001负责处理,而是由节点30002负责处理,所以节点30001将向客户端返回一个转向提示,而收到提示的客户端将向节点30002重新发送命令请求,最终使命令得到正确的处理

127.0.0.1:30001> set msg "hi"
-> Redirected to slot [6257] located at 127.0.0.1:30002
OK

在这里插入图片描述

  • 如果客户端发送的命令请求正好是由接收命令请求的节点负责处理,那么节点将直接向客户端返回命令执行结果,就像平时向单机服务器发送命令请求一样:
127.0.0.1:30002> set number 10086
OK
5)最后,在使用完这个测试集群之后,我们可以通过以下命令关闭集群并清理各个集群节点的相关信息:
root@iZbp1do67v9l7zwkcs2b22Z:~/redis-6.0.8/utils/create-cluster# ./create-cluster stop
Stopping 30001
Stopping 30002
Stopping 30003
Stopping 30004
Stopping 30005
Stopping 30006
root@iZbp1do67v9l7zwkcs2b22Z:~/redis-6.0.8/utils/create-cluster# ./create-cluster clean

1.2 手动搭建集群

1.2.1 自动和手动搭建集群的优缺点
  • 自动搭建集群:

优点:快速搭建Redis集群虽然非常方便
缺点:但是由于该程序搭建的Redis集群不具备配置文件、主从节点数量固定以及槽分配模式固定等原因,这些快速搭建的集群通常只能够用于测试,但是无法应用在实际的生产环境中

  • 手动搭建集群:

优点:为了搭建真正能够在生产环境中使用的Redis集群,我们需要创建相应的配置文件,并使用集群管理命令对集群进行配置和管理。

  • 为了保证集群的各项功能可以正常运转,一个集群至少需要3个主节点和3个从节点。不过为了与之前使用create-cluster程序搭建的集群区别开来,这次我们将搭建一个由5个主节点和5个从节点组成的Redis集群
1.2.2 手动搭建集群教程

1)创建10个文件夹,每个文件夹加一个redis.conf,并在尾部增加

cluster-enabled yes 
#表示将redis实例设置成集群节点而不是单机服务器
port 30001          
#为每个节点设置不同的端口号

2)接着,我们需要在每个节点文件夹中创建一个包含以下内容的redis.conf配置文件:
3)redis-server redis.conf 启动一个集群

root@iZbp1do67v9l7zwkcs2b22Z:~/kunmk/test/testForRedis/my-cluster/node1# redis-server redis.conf 
5341:C 04 Aug 2021 16:57:25.757 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
5341:C 04 Aug 2021 16:57:25.758 # Redis version=6.0.8, bits=64, commit=00000000, modified=0, pid=5341, just started
5341:C 04 Aug 2021 16:57:25.759 # Configuration loaded
5341:M 04 Aug 2021 16:57:25.761 * No cluster configuration found, I'm cb8f6fd1676f6ddbfa0eb7a67ab45d52e7bd6709
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 6.0.8 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in cluster mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 30001
 |    `-._   `._    /     _.-'    |     PID: 5341
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

5341:M 04 Aug 2021 16:57:25.773 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
5341:M 04 Aug 2021 16:57:25.773 # Server initialized
5341:M 04 Aug 2021 16:57:25.773 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
5341:M 04 Aug 2021 16:57:25.774 * Ready to accept connections

4)将集群按照主从配置

root@iZbp1do67v9l7zwkcs2b22Z:~/redis-6.0.8/src# redis-cli --cluster create 127.0.0.1:30001 127.0.0.1:30002 127.0.0.1:30003 127.0.0.1:30004 127.0.0.1:30005 127.0.0.1:30006 --cluster-replicas 1
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 127.0.0.1:30005 to 127.0.0.1:30001
Adding replica 127.0.0.1:30006 to 127.0.0.1:30002
Adding replica 127.0.0.1:30004 to 127.0.0.1:30003
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: cb8f6fd1676f6ddbfa0eb7a67ab45d52e7bd6709 127.0.0.1:30001
   slots:[0-5460] (5461 slots) master
M: e61668a9235f08029ef9a78a2314c581b14f58c8 127.0.0.1:30002
   slots:[5461-10922] (5462 slots) master
M: 71e409685a62c8a45cfaf9bdb39af128e2ae4671 127.0.0.1:30003
   slots:[10923-16383] (5461 slots) master
S: 3d43b8cffa4934f2b04b507d6918448e0b179010 127.0.0.1:30004
   replicates cb8f6fd1676f6ddbfa0eb7a67ab45d52e7bd6709
S: 0e634d160d5cd6ca261af2b3c3fadfb9d71b8ea8 127.0.0.1:30005
   replicates e61668a9235f08029ef9a78a2314c581b14f58c8
S: 4a6b040bb361d182a705f224b6db28a0e237d62c 127.0.0.1:30006
   replicates 71e409685a62c8a45cfaf9bdb39af128e2ae4671
Can I set the above configuration? (type 'yes' to accept): 
  • 从这份计划可以看出,命令打算把节点30001~30003设置为主节点,并把16384个槽平均分配给这3个节点负责,至于节点30004~30006则分别指派给了3个主节点作为从节点。在输入yes并按下Enter键之后,create命令就会执行实际的分配和指派工作:
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join

>>> Performing Cluster Check (using node 127.0.0.1:30001)
M: cb8f6fd1676f6ddbfa0eb7a67ab45d52e7bd6709 127.0.0.1:30001
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
M: 71e409685a62c8a45cfaf9bdb39af128e2ae4671 127.0.0.1:30003
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: 3d43b8cffa4934f2b04b507d6918448e0b179010 127.0.0.1:30004
   slots: (0 slots) slave
   replicates cb8f6fd1676f6ddbfa0eb7a67ab45d52e7bd6709
M: e61668a9235f08029ef9a78a2314c581b14f58c8 127.0.0.1:30002
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: 0e634d160d5cd6ca261af2b3c3fadfb9d71b8ea8 127.0.0.1:30005
   slots: (0 slots) slave
   replicates e61668a9235f08029ef9a78a2314c581b14f58c8
S: 4a6b040bb361d182a705f224b6db28a0e237d62c 127.0.0.1:30006
   slots: (0 slots) slave
   replicates 71e409685a62c8a45cfaf9bdb39af128e2ae4671
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
  • 我的服务器顶不住那么多集群,所以只做了三对主从节点
    在这里插入图片描述

2.使用客户端连接集群(hiredis库)

  • redis在C++、C上用hiredis连接集群,相关代码如下

1)简单连接的,没封装好函数

#include <hiredis/hiredis.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

// gcc -o redis-test redis-test.c -I /usr/local/include/hiredis -L /usr/local/lib -lhiredis 

int main() {

    // 连接Redis服务
    redisContext *context = redisConnect("127.0.0.1", 6379);
    if (context == NULL || context->err) {
        if (context) {
            printf("%s\n", context->errstr);
        } else {
            printf("redisConnect error\n");
        }
        exit(EXIT_FAILURE);
    }
    printf("-----------------connect success--------------------\n");

    //链接不上需要更新系统动态库配置
    //sudo /sbin/ldconfig

    redisReply *reply = (redisReply *)(redisCommand(context, "auth 0voice"));
    printf("type : %d\n", reply->type);
    // // if (reply->type == REDIS_REPLY_STATUS) {
    // //     /*SET str Hello World*/
    // //     printf("auth ok\n");
    // // }
    // // freeReplyObject(reply);

    // // Set Key Value
    // char *key = "str";
    // char *val = "Hello World";
    // /*SET key value */
    // reply = redisCommand(context, "SET %s %s", key, val);
    // printf("type : %d\n", reply->type);
    // if (reply->type == REDIS_REPLY_STATUS) {//REDIS_REPLY_STATUS ==5:返回命令执行的 状态
    //     /*SET str Hello World*/
    //     printf("SET %s %s\n", key, val);
    // }
    // freeReplyObject(reply);

    // GET Key
    int key_1 = 1;
    reply = (redisReply *)(redisCommand(context, "GET %d", key_1));
    if (reply->type == REDIS_REPLY_STRING) {//REDIS_REPLY_STRING == 1:返回值是 字符串
        /*GET str Hello World*/
        printf("GET str %s\n", reply->str); //字符串储存在 redis->str 当中
        /*GET len 11*/
        printf("GET len %ld\n", reply->len);//字符串长度为 redis->len
    }
    freeReplyObject(reply);


    // APPEND key value
    // char *append = " I am your GOD";
    // //如果 key 已经存在并且是一个字符串, APPEND 命令将指定的 value 追加到该 key 原来值(value)的末尾
    // reply = redisCommand(context, "APPEND %s %s", key, append);
    // if (reply->type == REDIS_REPLY_INTEGER) {//REDIS_REPLY_INTEGER == 3:返回值为 整数 long long。
    //     printf("APPEND %s %s \n", key, append);
    // }
    // freeReplyObject(reply);
    // /*GET key*/
    // reply = redisCommand(context, "GET %s", key);
    // if (reply->type == REDIS_REPLY_STRING) {//REDIS_REPLY_STRING == 1:返回值是 字符串
    //     //GET Hello World I am your GOD
    //     printf("GET %s\n", reply->str);//string str(reply->str);
    // }
    // freeReplyObject(reply);

    // // INCR key
    // reply = redisCommand(context, "INCR counter");//将 key 中储存的数字值增一
    // if (reply->type == REDIS_REPLY_INTEGER) {//REDIS_REPLY_INTEGER == 3:返回值为 整数 long long
    //     printf("INCR counter %lld\n", reply->integer);
    // }
    // freeReplyObject(reply);
    // reply = redisCommand(context, "INCR counter");
    // if (reply->type == REDIS_REPLY_INTEGER) {
    //     printf("INCR counter %lld\n", reply->integer);
    // }
    // freeReplyObject(reply);

    // // DECR key
    // reply = redisCommand(context, "DECR counter");
    // if (reply->type == REDIS_REPLY_INTEGER) {
    //     printf("DECR counter %lld\n", reply->integer);
    // }
    // freeReplyObject(reply);
    // reply = redisCommand(context, "DECR counter");
    // if (reply->type == REDIS_REPLY_INTEGER) {
    //     printf("DECR counter %lld\n", reply->integer);
    // }
    // freeReplyObject(reply);


    // // DECRBY key decrement
    // reply = redisCommand(context, "DECRBY counter 5");
    // if (reply->type == REDIS_REPLY_INTEGER) {
    //     printf("DECRBY counter %lld\n", reply->integer);
    // }
    // freeReplyObject(reply);
    // reply = redisCommand(context, "DECRBY counter 5");
    // if (reply->type == REDIS_REPLY_INTEGER) {
    //     printf("DECRBY counter %lld\n", reply->integer);
    // }
    // freeReplyObject(reply);

    // // INCRBY key increment
    // reply = redisCommand(context, "INCRBY counter 5");
    // if (reply->type == REDIS_REPLY_INTEGER) {
    //     printf("INCRBY counter %lld\n", reply->integer);
    // }
    // freeReplyObject(reply);

    // reply = redisCommand(context, "INCRBY counter 5");
    // if (reply->type == REDIS_REPLY_INTEGER) {
    //     printf("INCRBY counter %lld\n", reply->integer);
    // }
    // freeReplyObject(reply);

    // // GETRANGE key start end
    // reply = redisCommand(context, "GETRANGE str 0 5");
    // if (reply->type == REDIS_REPLY_STRING) {
    //     /*GETRANGE str Hello*/
    //     printf("GETRANGE %s %s\n", key, reply->str);
    // }
    // freeReplyObject(reply);

    // // GETSET key value
    // reply = redisCommand(context, "GETSET %s %s", key, val);
    // if (reply->type == REDIS_REPLY_STRING) {
    //     /*GETSET str Hello World I am your GOD*/
    //     printf("GETSET %s %s\n", key, reply->str);
    // }
    // freeReplyObject(reply);

    // /*INCRBYFLOAT key increment*/
    // reply = redisCommand(context, "INCRBYFLOAT f 2.1");
    // if (reply->type == REDIS_REPLY_STRING) {
    //     printf("INCRBYFLOAT counter %s\n", reply->str);
    // }
    // freeReplyObject(reply);

    // /*MSET key value [key value ...]*/
    // reply = redisCommand(context, "MSET k1 hello k2 world k3 good");
    // if (reply->type == REDIS_REPLY_STATUS) {
    //     printf("MSET k1 hello k2 world k3 good\n");
    // }
    // freeReplyObject(reply);

    // /*MGET key [key ...]*/
    // reply = redisCommand(context, "MGET k1 k2 k3");
    // if (reply->type == REDIS_REPLY_ARRAY) {
    //     printf("MGET k1  k2  k3 \n");
    //     redisReply **pReply = reply->element;
    //     int i = 0;
    //     size_t len = reply->elements;
    //     //hello world good
    //     for (; i < len; ++i) {
    //         printf("%s ", pReply[i]->str);
    //     }
    //     printf("\n");
    // }
    // freeReplyObject(reply);

    // /*STRLEN key*/
    // reply = redisCommand(context, "STRLEN str");
    // if (reply->type == REDIS_REPLY_INTEGER) {
    //     //1
    //     printf("STRLEN str %lld \n", reply->integer);
    // }
    // freeReplyObject(reply);

    // /*SETEX key seconds value*/
    // reply = redisCommand(context, "SETEX s 30 30seconds");
    // if (reply->type == REDIS_REPLY_STATUS) {
    //     printf("SETEX s 30 30seconds\n");
    //     freeReplyObject(reply);
    //     int i = 0;
    //     while (i++ < 32) {
    //         reply = redisCommand(context, "GET s");
    //         if (reply->type == REDIS_REPLY_STRING) {
    //             printf("%d s %s\n", i, reply->str);
    //         } else if (reply->type == REDIS_REPLY_NIL) {
    //             printf("%d s nil\n", i);
    //         }
    //         freeReplyObject(reply);
    //         sleep(1);
    //         /*
    //          * 29 s 30seconds
    //          * 30 s 30seconds
    //          * 31 s nil
    //          * 32 s nil
    //          */
    //     }
    // }

    redisFree(context);
    return EXIT_SUCCESS;
}


2)包装好的函数(没用libevent)

#include <hiredis/hiredis.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>


// gcc -o redis-protocol redis-protocol.c -I /usr/local/include/hiredis -L /usr/local/lib -lhiredis 

//清空数据库
void flushdb(redisContext *context)
{
    redisReply *reply = (redisReply *)redisCommand(context, "FLUSHDB");
    printf("reply->type: %d\n", reply->type);
    if (reply->type == REDIS_REPLY_STATUS)
    {
        /*SET str Hello World*/
        printf("FLUSHDB OK\n");
    }
    printf("reply->str: %s\n", reply->str);

    freeReplyObject(reply);
}

//蛇者简单的键值,键和值都是字符串
void testSimpleString(redisContext *context)
{
    // Set Key Value
    //key值也可以是整数,int key = 3
    const char *key = "str";
    const char *val = "Hello World";
    /*SET key value */
    redisReply *reply = (redisReply *)redisCommand(context, "SET %s %s", key, val);
    printf("reply->type: %d\n", reply->type);
    if (reply->type == REDIS_REPLY_STATUS)
    {
        /*SET str Hello World*/
        printf("SET %s %s\n", key, val);
    }
    printf("reply->str: %s\n", reply->str);

    freeReplyObject(reply);
}

//value值是字符串,不能自增会报错
void testErrors(redisContext *context)
{
    flushdb(context);
    // Set Key Value
    printf("SET\n");
    const char *key = "teacher";
    const char *val = "darren";
    /*SET key value */
    redisReply *reply = (redisReply *)redisCommand(context, "SET %s %s", key, val);
    printf("reply->type: %d\n", reply->type);
    if (reply->type == REDIS_REPLY_STATUS)
    {
        /*SET str Hello World*/
        printf("SET %s %s\n", key, val);
    }
    printf("reply->str: %s\n", reply->str);

    freeReplyObject(reply);

    //若键值是字符串,INCR将 key 的value数字值增一
    // INCR Key 
    
    printf("\nINCR\n");
    /*INCR key value */
    reply = (redisReply *)redisCommand(context, "INCR %s", key);
    printf("reply->type: %d\n", reply->type);
    if (reply->type == REDIS_REPLY_ERROR)
    {
        /*INCR key*/
        printf("INCR %s failed:%s\n", key, reply->str);
    } else if (reply->type == REDIS_REPLY_INTEGER) {
        printf("INCR %s = %lld\n", key, reply->integer);     
    }

    freeReplyObject(reply);
}

//插入键值对为:键为字符串,值为字符串
void testIntegers(redisContext *context)
{
    flushdb(context);
    // Set Key Value
    printf("SET\n");
    const char *key = "count";
    const char *val = "10";
    /*SET key value */
    redisReply *reply = (redisReply *)redisCommand(context, "SET %s %s", key, val);
    printf("reply->type: %d\n", reply->type);
    if (reply->type == REDIS_REPLY_STATUS)
    {
        /*SET str Hello World*/
        printf("SET %s %s\n", key, val);
    }
    printf("reply->str: %s\n", reply->str);

    freeReplyObject(reply);

     // INCR Key 
    printf("\nINCR\n");
    /*INCR key value */
    reply = (redisReply *)redisCommand(context, "INCR %s", key);
    printf("reply->type: %d\n", reply->type);
    if (reply->type == REDIS_REPLY_ERROR)
    {
        /*INCR key*/
        printf("INCR %s failed:%s\n", key, reply->str);
    } else if (reply->type == REDIS_REPLY_INTEGER) {
        printf("INCR %s = %lld\n", key, reply->integer);     
    }

    freeReplyObject(reply);
}

void testBulkStrings(redisContext *context)
{
    //MSET key value [key value ...]
    //同时设置一个或多个 key-value 对。
    flushdb(context);
    printf("MSET\n");
    /*MSET key value [key value ...]*/
    redisReply *reply = (redisReply *)redisCommand(context, "MSET king redis darren avmedia");
    //插入两个键值对
    //king   redis
    //darren avmedia
    if (reply->type == REDIS_REPLY_STATUS) {
        printf("MSET king redis darren avmedia\n");
    }
    freeReplyObject(reply);

    printf("\nMGET\n");   
    /*MGET key [key ...]*/
    reply = (redisReply *)redisCommand(context, "MGET king darren");
    printf("reply->type: %d\n", reply->type);
    if (reply->type == REDIS_REPLY_ARRAY) {
        //两个key值,分别为king darren
        printf("MGET king darren\n");
        redisReply **pReply = reply->element;
        int i = 0;
        size_t len = reply->elements;
        for (; i < len; ++i) {
            printf("%s \n", pReply[i]->str);  // 如果没有数据时为空
        }
        printf("\n");
    }
    freeReplyObject(reply);
}

void testArrays(redisContext *context)
{
    flushdb(context);
    printf("LPUSH\n");
    /*LPUSH key value [value ...]*/
    redisReply *reply = (redisReply *)redisCommand(context, "LPUSH list darren qiuxiang king milo");
    printf("reply->type: %d\n", reply->type);
    if (reply->type == REDIS_REPLY_INTEGER) {
        if(1 == reply->integer) {
            printf("LPUSH ok\n");     
        } else {
            printf("LPUSH failed\n");  
        } 
    } else {
        printf("LPUSH failed\n");
    }
    freeReplyObject(reply);

    printf("\nLRANGE\n");   
    /*LRANGE key start stop*/
    reply = (redisReply *)redisCommand(context, "LRANGE list 0 2");
    printf("reply->type: %d\n", reply->type);
    if (reply->type == REDIS_REPLY_ARRAY) {
        printf("LRANGE list 0 2\n");
        redisReply **pReply = reply->element;
        int i = 0;
        size_t len = reply->elements;
        for (; i < len; ++i) {
            printf("%s \n", pReply[i]->str);  
        }
        printf("\n");
    }
    freeReplyObject(reply);

    /*LRANGE key start stop*/
    reply = (redisReply *)redisCommand(context, "LRANGE list 0 -1");
    printf("reply->type: %d\n", reply->type);
    if (reply->type == REDIS_REPLY_ARRAY) {
        printf("LRANGE list 0 -1\n");
        redisReply **pReply = reply->element;
        int i = 0;
        size_t len = reply->elements;
        //hello world good
        for (; i < len; ++i) {
            printf("%s \n", pReply[i]->str);  
        }
        printf("\n");
    }
    freeReplyObject(reply);
}


int main()
{
    redisReply *reply = NULL;
    // 连接Redis服务
    redisContext *context = redisConnect("127.0.0.1", 6379);
    if (context == NULL || context->err)
    {
        if (context)
        {
            printf("%s\n", context->errstr);
        }
        else
        {
            printf("redisConnect error\n");
        }
        exit(EXIT_FAILURE);
    }
    printf("-----------------connect success--------------------\n");

    // reply = (redisReply *)redisCommand(context, "auth 0voice");
    // printf("type : %d\n", reply->type);
    // if (reply->type == REDIS_REPLY_STATUS)
    // {
    //     /*SET str Hello World*/
    //     printf("auth ok\n");
    // }
    // freeReplyObject(reply);

    //testSimpleString(context);              //插入一段键和值都是字符串的键值对
    //testErrors(context);                      //value值是字符串,不能自增会报错
    //testIntegers(context);                  //插入字符串为count-10,10可以用INCR自增
    //testBulkStrings(context);               //成对插入键值对,按key值查询 
    testArrays(context);

    //清空数据库
    redisFree(context);


    return EXIT_SUCCESS;
}

3.散列标签{ … }

= 在默认情况下,Redis将根据用户输入的整个键计算出该键所属的槽,然后将键存储到相应的槽中。但是在某些情况下,出于性能方面的考虑,或者为了在同一个节点上对多个相关联的键执行批量操作,我们也会想要将一些原本不属于同一个槽的键放到相同的槽里面。

  • 为了满足这一需求,Redis为用户提供了散列标签(hash tag)功能,该功能会找出键中第一个被大括号{}包围并且非空的字符串子串(sub string),然后根据子串计算出该键所属的槽。这样一来,即使两个键原本不属于同一个槽,但只要它们拥有相同的被包围子串,那么程序计算出的散列值就是一样的,因此Redis集群就会把它们存储到同一个槽中。
root@iZbp1do67v9l7zwkcs2b22Z:~/kunmk/test/testForRedis# redis-cli -c -p 30002
127.0.0.1:30002> cluster keyslot user::10086	//不在一个槽位
(integer) 14982
127.0.0.1:30002> cluster keyslot user::10087	//不在一个槽位
(integer) 10919
127.0.0.1:30002> cluster keyslot {user}::10086	//在一个槽位
(integer) 5474
127.0.0.1:30002> cluster keyslot {user}::10087	//在一个槽位
(integer) 5474
  • 了验证这一点,我们可以实际地对这两个键执行设置操作。在未使用散列标签功能时,对user::10086键的设置将被转向至节点30003所属的槽14982中,而对user::10087键的设置将被转向至节点30002所属的槽10919中
127.0.0.1:30002> hmset user::10086 name peter age 28 job programmer
-> Redirected to slot [14982] located at 127.0.0.1:30003
OK
127.0.0.1:30003> hmset user::10087 name jack age 28 job writer
-> Redirected to slot [10919] located at 127.0.0.1:30002
OK
  • 注意

关于散列标签的使用,有以下两点需要注意。首先,虽然从逻辑上来讲,我们把user::10086和{user}::10086看作同一个键,但由于散列标签只是Redis集群对键名的一种特殊解释,因此这两个键在实际中并不相同,它们可以同时存在于数据库,并且不会引发任何键冲突。比如,如果我们对节点30002执行KEYS*命令,就会看到user::10086和{user}::10086同时存在于数据库中:
在这里插入图片描述

4.打开/关闭从节点的读命令执行权限

  • readonly打开从服务器的读权限
  • readwrite关闭从服务器的读权限
root@iZbp1do67v9l7zwkcs2b22Z:~/kunmk/test/testForRedis# redis-cli -c -p 30004
127.0.0.1:30004> get num
-> Redirected to slot [2765] located at 127.0.0.1:30001
(nil)
127.0.0.1:30001> quit
root@iZbp1do67v9l7zwkcs2b22Z:~/kunmk/test/testForRedis# redis-cli -c -p 30004
127.0.0.1:30004> readonly
OK
127.0.0.1:30004> get num
(nil)
127.0.0.1:30004> readwrite
OK
127.0.0.1:30004> get num
-> Redirected to slot [2765] located at 127.0.0.1:30001
(nil)
127.0.0.1:30001> 

5.管理集群命令

  • Redis官方为管理集群提供了两种工具,一种是redis-cli客户端附带的集群管理程序,而另一种则是Redis内置的集群管理命令
  • 本节中我们将对redis-cli客户端附带的集群管理程序做详细的介绍。这个程序可以通过在执行redis-cli的时候给定cluster选项来启动,输入help子命令可以看到该程序支持的各个子命令及其格式:
root@iZbp1do67v9l7zwkcs2b22Z:~/kunmk/test/testForRedis# redis-cli --cluster help
Cluster Manager Commands:
  create         host1:port1 ... hostN:portN
                 --cluster-replicas <arg>
  check          host:port
                 --cluster-search-multiple-owners
  info           host:port
  fix            host:port
                 --cluster-search-multiple-owners
                 --cluster-fix-with-unreachable-masters
  reshard        host:port
                 --cluster-from <arg>
                 --cluster-to <arg>
                 --cluster-slots <arg>
                 --cluster-yes
                 --cluster-timeout <arg>
                 --cluster-pipeline <arg>
                 --cluster-replace
  rebalance      host:port
                 --cluster-weight <node1=w1...nodeN=wN>
                 --cluster-use-empty-masters
                 --cluster-timeout <arg>
                 --cluster-simulate
                 --cluster-pipeline <arg>
                 --cluster-threshold <arg>
                 --cluster-replace
  add-node       new_host:new_port existing_host:existing_port
                 --cluster-slave
                 --cluster-master-id <arg>
  del-node       host:port node_id
  call           host:port command arg arg .. arg
                 --cluster-only-masters
                 --cluster-only-replicas
  set-timeout    host:port milliseconds
  import         host:port
                 --cluster-from <arg>
                 --cluster-copy
                 --cluster-replace
  backup         host:port backup_directory
  help           

For check, fix, reshard, del-node, set-timeout you can specify the host and port of any working node in the cluster.

5.1 CLUSTER MEET:将节点添加至集群

cluster meet ip port
//假设现在启动了30001、30002、30003这三个节点,打算用3个节点组成一个集群
root@iZbp1do67v9l7zwkcs2b22Z:~/kunmk/test/testForRedis# redis-cli -c -p 30001
127.0.0.1:30001> cluster meet 127.0.0.1 30002
OK
127.0.0.1:30001> cluster meet 127.0.0.1 30003
OK

在这里插入图片描述

  • 时间复杂度O(1)

5.2 CLUSTER NODES:查看集群内所有节点的相关信息

27.0.0.1:30001> cluster nodes
71e409685a62c8a45cfaf9bdb39af128e2ae4671 127.0.0.1:30003@40003 master - 0 1628074114000 3 connected 10923-16383
3d43b8cffa4934f2b04b507d6918448e0b179010 127.0.0.1:30004@40004 slave cb8f6fd1676f6ddbfa0eb7a67ab45d52e7bd6709 0 1628074115000 1 connected
cb8f6fd1676f6ddbfa0eb7a67ab45d52e7bd6709 127.0.0.1:30001@40001 myself,master - 0 1628074115000 1 connected 0-5460
e61668a9235f08029ef9a78a2314c581b14f58c8 127.0.0.1:30002@40002 master - 0 1628074116667 2 connected 5461-10922
0e634d160d5cd6ca261af2b3c3fadfb9d71b8ea8 127.0.0.1:30005@40005 slave e61668a9235f08029ef9a78a2314c581b14f58c8 0 1628074114000 2 connected
4a6b040bb361d182a705f224b6db28a0e237d62c 127.0.0.1:30006@40006 slave 71e409685a62c8a45cfaf9bdb39af128e2ae4671 0 1628074115665 3 connected

在这里插入图片描述
在这里插入图片描述

  • 讲解1
cb8f6fd1676f6ddbfa0eb7a67ab45d52e7bd6709 127.0.0.1:30001@40001 myself,master - 0 1628074115000 1 connected 0-5460

1)这个节点ID为cb8f6fd1676f6ddbfa0eb7a67ab45d52e7bd6709
2)它的IP地址为127.0.0.1,客户端端口号为30001,集群端口号为40001
3)角色和状态部分的myself表示它是客户端正在连接的节点,而master则表示它是一个主节点。
4)因为这个节点本身就是一个主节点,它没有正在复制的节点,所以它的主节点ID部分为-。
5)节点最近一次发送PING消息的时间戳为0,这表示该节点与其他节点连接正常,并且没有待发送的PING消息;该节点最后一次收到PONG消息的时间戳为1628074115000。
6)这个节点所处的配置纪元为1
7)connected表示这个节点的集群总线连接状态正常
8)0-5460表示这个节点负责处理槽0至槽5460

  • 讲解2
3d43b8cffa4934f2b04b507d6918448e0b179010 127.0.0.1:30004@40004 slave cb8f6fd1676f6ddbfa0eb7a67ab45d52e7bd6709 0 1628074115000 1 connected

1)这个节点的ID为3d43b8cffa4934f2b04b507d6918448e0b179010,它是一个从节点,它的IP地址为127.0.0.1,客户端端口号为30004,集群端口号为40004。
2)这个节点正在复制的主节点的运行ID为cb8f6fd1676f6ddbfa0eb7a67ab45d52e7bd6709
3)这个节点最后一次发送PING消息的时间戳为0,这表示该节点与其他节点的连接正常,并且没有待发送的PING消息;而它最后一次接收到PONG消息的时间戳为1628074115000。
4)这个节点所处的配置纪元为1
5)connected表示这个节点的集群总线的连接状态正常
6)这个节点是一个从节点,它没有被指派任何槽,所以槽信息部分为空

  • 时间复杂度O(N)

5.3 CLUSTER MYID:查看当前节点的运行ID

127.0.0.1:30001> cluster myid
"cb8f6fd1676f6ddbfa0eb7a67ab45d52e7bd6709"
  • 从命令返回的结果可以知道,客户端正在连接ID为cb8f6fd1676f6ddbfa0eb7a67ab45d52e7bd6709的节点。

5.4 CLUSTER INFO:查看集群信息

root@iZbp1do67v9l7zwkcs2b22Z:~/kunmk/test/testForRedis# redis-cli -c -p 30001
127.0.0.1:30001> cluster info
cluster_state:ok				--集群处于极限状态
cluster_slots_assigned:16384	--16384个槽已经被指派
cluster_slots_ok:16384			--16384个槽处于在线状态
cluster_slots_pfail:0			--没有槽属于疑似下线状态
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:4323
cluster_stats_messages_pong_sent:4345
cluster_stats_messages_meet_sent:2
cluster_stats_messages_sent:8670
cluster_stats_messages_ping_received:4340
cluster_stats_messages_pong_received:4325
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:8670
127.0.0.1:30001> 

在这里插入图片描述
在这里插入图片描述

5.5 CLUSTER FORGET:从集群中移除节点

 $ redis-cli --cluster call 127.0.0.1:30001 cluster forget 5f99406c27403564f34f

在这里插入图片描述

  • 时间复杂度O(1)

5.6 CLUSTER REPLICATE:将节点变为从节点

  • CLUSTER REPLICATE命令接受一个主节点ID作为参数,并将执行该命令的节点变成给定主节点的从节点:
  • 如果当前节点是一个主节点,那么它必须是一个没有被指派任何槽的主节点,并且它的数据库中也不能有任何数据,这样它才可以转换成一个从节点。
  • 如果当前节点已经是一个从节点,那么它将清空数据库中已有的数据,并开始复制用户给定的节点。
  • 复杂度:CLUSTER REPLICATE命令本身的复杂度为O(1),但它引起的异步复制操作的复杂度为O(N),其中N为主节点数据库包含的键值对总数量
  • Redis集群只允许节点对主节点而不是从节点进行复制,如果用户尝试使用CLUSTER REPLICATE命令让一个节点去复制一个从节点,那么命令将返回一个错误:
  • 举例:(30005成为30001的从节点,后面的myid是30001的id)
127.0.0.1:30005> cluster replicate cb8f6fd1676f6ddbfa0eb7a67ab45d52e7bd6709
OK

5.7 CLUSTER REPLICAS:查看给定节点的所有从节点

  • 先获取自己的myid,再用myid获取从节点信息
127.0.0.1:30001> cluster myid
"cb8f6fd1676f6ddbfa0eb7a67ab45d52e7bd6709"
127.0.0.1:30001> cluster replicas cb8f6fd1676f6ddbfa0eb7a67ab45d52e7bd6709
1) "3d43b8cffa4934f2b04b507d6918448e0b179010 127.0.0.1:30004@40004 slave cb8f6fd1676f6ddbfa0eb7a67ab45d52e7bd6709 0 1628133424349 1 connected"
  • 补充:这个命令是5.0及以上版本使用的,如果是以下的版本

旧版Redis中的CLUSTER REPLICAS命令CLUSTER REPLICAS命令是从Redis 5.0.0版本开始启用的,在较旧的Redis版本中(不低于3.0.0版本),同样的工作可以通过执行CLUSTER SLAVES命令来完成。因为CLUSTER SLAVES在未来可能会被废弃,所以如果你使用的是Redis5.0.0或以上版本,那么你应该使用CLUSTER REPLICAS而不是CLUSTER SLAVES。

5.8 CLUSTER FAILOVER:强制执行故障转移

  • 故障转移实际上就是从节点变成主节点,主节点变成从节点
root@iZbp1do67v9l7zwkcs2b22Z:~/kunmk/test/testForRedis/my-cluster# redis-cli -c -p 30004
127.0.0.1:30004> cluster nodes
4a6b040bb361d182a705f224b6db28a0e237d62c 127.0.0.1:30006@40006 slave 71e409685a62c8a45cfaf9bdb39af128e2ae4671 0 1628133709582 3 connected
0e634d160d5cd6ca261af2b3c3fadfb9d71b8ea8 127.0.0.1:30005@40005 slave e61668a9235f08029ef9a78a2314c581b14f58c8 0 1628133708000 2 connected
cb8f6fd1676f6ddbfa0eb7a67ab45d52e7bd6709 127.0.0.1:30001@40001 master - 0 1628133708579 1 connected 0-5460
3d43b8cffa4934f2b04b507d6918448e0b179010 127.0.0.1:30004@40004 myself,slave cb8f6fd1676f6ddbfa0eb7a67ab45d52e7bd6709 0 1628133707000 1 connected
71e409685a62c8a45cfaf9bdb39af128e2ae4671 127.0.0.1:30003@40003 master - 0 1628133706576 3 connected 10923-16383
e61668a9235f08029ef9a78a2314c581b14f58c8 127.0.0.1:30002@40002 master - 0 1628133707578 2 connected 5461-10922
127.0.0.1:30004> cluster failover
OK
127.0.0.1:30004> cluster nodes
4a6b040bb361d182a705f224b6db28a0e237d62c 127.0.0.1:30006@40006 slave 71e409685a62c8a45cfaf9bdb39af128e2ae4671 0 1628133749662 3 connected
0e634d160d5cd6ca261af2b3c3fadfb9d71b8ea8 127.0.0.1:30005@40005 slave e61668a9235f08029ef9a78a2314c581b14f58c8 0 1628133749000 2 connected
cb8f6fd1676f6ddbfa0eb7a67ab45d52e7bd6709 127.0.0.1:30001@40001 slave 3d43b8cffa4934f2b04b507d6918448e0b179010 0 1628133748657 7 connected
3d43b8cffa4934f2b04b507d6918448e0b179010 127.0.0.1:30004@40004 myself,master - 0 1628133748000 7 connected 0-5460
71e409685a62c8a45cfaf9bdb39af128e2ae4671 127.0.0.1:30003@40003 master - 0 1628133750665 3 connected 10923-16383
e61668a9235f08029ef9a78a2314c581b14f58c8 127.0.0.1:30002@40002 master - 0 1628133747655 2 connected 5461-10922
127.0.0.1:30004> 
  • cluster failover [force|takeover]:force选项和takeover选项

1)在给定了FORCE选项时,从节点将在不尝试与主节点进行握手的情况下,直接实施故障转移。这种做法可以让用户在主节点已经下线的情况下立即开始故障转移
2)需要注意的是,即使用户给定了FORCE选项,从节点对主节点的故障转移操作仍然要经过集群中大多数主节点的同意才能够真正执行。但如果用户给定了TAKEOVER选项,那么从节点将在不询问集群中其他节点意见的情况下,直接对主节点实施故障转移。

5.9 CLUSTER RESET:重置节点

  • 命令:cluster reset [ soft | hard ]
  • 这个命令接受SOFT和HARD两个可选项作为参数,用于指定重置操作的具体行为(软重置和硬重置)。如果用户在执行CLUSTER RESET命令的时候没有显式地指定重置方式,那么命令默认将使用SOFT选项。
  • CLUSTER RESET命令在执行时,将对节点执行以下操作:

1)遗忘该节点已知的其他所有节点。
2)撤销指派给该节点的所有槽,并清空节点内部的槽-节点映射表。
3)如果执行该命令的节点是一个从节点,那么将它转换成一个主节点。
4)如果执行的是硬重置,那么为节点创建一个新的运行ID。(分配新的ID
5)如果执行的是硬重置,那么将节点的纪元和配置纪元都设置为0。
6)通过集群节点配置文件的方式,将新的配置持久化到硬盘上。

  • 需要注意的是,CLUSTER RESET命令只能在数据库为空的节点上执行,如果节点的数据库非空,那么命令将返回一个错误:
127.0.0.1:30002> cluster reset hard
OK
127.0.0.1:30002> cluster nodes
3d43b8cffa4934f2b04b507d6918448e0b179010 127.0.0.1:30004@40004 myself,master - 0 1628134419000 7 connected 0-5460
  • 复杂度:O(N),其中N为节点所在集群包含的节点数量。

6.集群管理命令

6.1 创建集群

  • 创建3个节点的集群
redis-cli --cluster create 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003
  • 创建3个节点的集群,每个节点配备一个从节点
redis-cli --cluster create 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006 --cluster-replicas 1

6.2 查看集群信息

root@iZbp1do67v9l7zwkcs2b22Z:~/kunmk/test/testForRedis/my-cluster# redis-cli --cluster info 127.0.0.1:30001
127.0.0.1:30002 (e61668a9...) -> 3 keys | 5462 slots | 1 slaves.
127.0.0.1:30004 (3d43b8cf...) -> 0 keys | 5461 slots | 1 slaves.
127.0.0.1:30003 (71e40968...) -> 1 keys | 5461 slots | 1 slaves.
[OK] 4 keys in 3 masters.
0.00 keys per slot on average.
  • 从命令返回的结果可以看到,节点30001所在的集群包含了3个主节点,以主节点127.0.0.1:30002为例(30001为30004的从节点,30004为一个主节点)

它的运行ID前缀为3d43b8cf,
它被指派了5461个槽,但是目前只存储了0个数据库键。
30004这个节点拥有1个从节点为30001。

6.3 检查集群

root@iZbp1do67v9l7zwkcs2b22Z:~/kunmk/test/testForRedis/my-cluster# redis-cli --cluster check 127.0.0.1:30001
127.0.0.1:30002 (e61668a9...) -> 3 keys | 5462 slots | 1 slaves.
127.0.0.1:30004 (3d43b8cf...) -> 0 keys | 5461 slots | 1 slaves.
127.0.0.1:30003 (71e40968...) -> 1 keys | 5461 slots | 1 slaves.
[OK] 4 keys in 3 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 127.0.0.1:30001)
S: cb8f6fd1676f6ddbfa0eb7a67ab45d52e7bd6709 127.0.0.1:30001
   slots: (0 slots) slave
   replicates 3d43b8cffa4934f2b04b507d6918448e0b179010
M: e61668a9235f08029ef9a78a2314c581b14f58c8 127.0.0.1:30002
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: 0e634d160d5cd6ca261af2b3c3fadfb9d71b8ea8 127.0.0.1:30005
   slots: (0 slots) slave
   replicates e61668a9235f08029ef9a78a2314c581b14f58c8
M: 3d43b8cffa4934f2b04b507d6918448e0b179010 127.0.0.1:30004
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
M: 71e409685a62c8a45cfaf9bdb39af128e2ae4671 127.0.0.1:30003
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: 4a6b040bb361d182a705f224b6db28a0e237d62c 127.0.0.1:30006
   slots: (0 slots) slave
   replicates 71e409685a62c8a45cfaf9bdb39af128e2ae4671
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

6.4 修复槽错误

  • 当集群在重分片、负载均衡或者槽迁移的过程中出现错误时,执行cluster选项的fix子命令,可以让操作涉及的槽重新回到正常状态:
fix <ip>:<port>
  • 举个例子,假设我们现在正在将节点30001的5460槽迁移至节点30002,并且为了模拟迁移中断导致出错的情况,我们在迁移完成之后并没有清理相应节点的“导入中”和“迁移中”状态。现在,如果我们执行fix命令,那么它将发现并修复这一问题:
root@iZbp1do67v9l7zwkcs2b22Z:~/kunmk/test/testForRedis/my-cluster# redis-cli --cluster fix 127.0.0.1:30001
127.0.0.1:30002 (e61668a9...) -> 3 keys | 5462 slots | 1 slaves.
127.0.0.1:30004 (3d43b8cf...) -> 0 keys | 5461 slots | 1 slaves.
127.0.0.1:30003 (71e40968...) -> 1 keys | 5461 slots | 1 slaves.
[OK] 4 keys in 3 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 127.0.0.1:30001)
S: cb8f6fd1676f6ddbfa0eb7a67ab45d52e7bd6709 127.0.0.1:30001
   slots: (0 slots) slave
   replicates 3d43b8cffa4934f2b04b507d6918448e0b179010
M: e61668a9235f08029ef9a78a2314c581b14f58c8 127.0.0.1:30002
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: 0e634d160d5cd6ca261af2b3c3fadfb9d71b8ea8 127.0.0.1:30005
   slots: (0 slots) slave
   replicates e61668a9235f08029ef9a78a2314c581b14f58c8
M: 3d43b8cffa4934f2b04b507d6918448e0b179010 127.0.0.1:30004
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
M: 71e409685a62c8a45cfaf9bdb39af128e2ae4671 127.0.0.1:30003
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: 4a6b040bb361d182a705f224b6db28a0e237d62c 127.0.0.1:30006
   slots: (0 slots) slave
   replicates 71e409685a62c8a45cfaf9bdb39af128e2ae4671
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
root@iZbp1do67v9l7zwkcs2b22Z:~/kunmk/test/testForRedis/my-cluster# 

6.5 重分片

  • 通过cluster选项的reshard子命令,用户可以将指定数量的槽从原节点迁移至目标节点,被迁移的槽将交由后者负责,并且槽中已有的数据也会陆续从原节点转移至目标节点:
    在这里插入图片描述
root@iZbp1do67v9l7zwkcs2b22Z:~/kunmk/test/testForRedis/my-cluster# redis-cli --cluster reshard 127.0.0.1:30004 --cluster-from 3d43b8cffa4934f2b04b507d6918448e0b179010 --cluster-to e61668a9235f08029ef9a78a2314c581b14f58c8 --cluster-slots 10
>>> Performing Cluster Check (using node 127.0.0.1:30004)
M: 3d43b8cffa4934f2b04b507d6918448e0b179010 127.0.0.1:30004
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
S: 4a6b040bb361d182a705f224b6db28a0e237d62c 127.0.0.1:30006
   slots: (0 slots) slave
   replicates 71e409685a62c8a45cfaf9bdb39af128e2ae4671
S: 0e634d160d5cd6ca261af2b3c3fadfb9d71b8ea8 127.0.0.1:30005
   slots: (0 slots) slave
   replicates e61668a9235f08029ef9a78a2314c581b14f58c8
S: cb8f6fd1676f6ddbfa0eb7a67ab45d52e7bd6709 127.0.0.1:30001
   slots: (0 slots) slave
   replicates 3d43b8cffa4934f2b04b507d6918448e0b179010
M: 71e409685a62c8a45cfaf9bdb39af128e2ae4671 127.0.0.1:30003
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
M: e61668a9235f08029ef9a78a2314c581b14f58c8 127.0.0.1:30002
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

Ready to move 10 slots.
  Source nodes:
    M: 3d43b8cffa4934f2b04b507d6918448e0b179010 127.0.0.1:30004
       slots:[0-5460] (5461 slots) master
       1 additional replica(s)
  Destination node:
    M: e61668a9235f08029ef9a78a2314c581b14f58c8 127.0.0.1:30002
       slots:[5461-10922] (5462 slots) master
       1 additional replica(s)
  Resharding plan:
    Moving slot 0 from 3d43b8cffa4934f2b04b507d6918448e0b179010
    Moving slot 1 from 3d43b8cffa4934f2b04b507d6918448e0b179010
    Moving slot 2 from 3d43b8cffa4934f2b04b507d6918448e0b179010
    Moving slot 3 from 3d43b8cffa4934f2b04b507d6918448e0b179010
    Moving slot 4 from 3d43b8cffa4934f2b04b507d6918448e0b179010
    Moving slot 5 from 3d43b8cffa4934f2b04b507d6918448e0b179010
    Moving slot 6 from 3d43b8cffa4934f2b04b507d6918448e0b179010
    Moving slot 7 from 3d43b8cffa4934f2b04b507d6918448e0b179010
    Moving slot 8 from 3d43b8cffa4934f2b04b507d6918448e0b179010
    Moving slot 9 from 3d43b8cffa4934f2b04b507d6918448e0b179010
Do you want to proceed with the proposed reshard plan (yes/no)? yes
Moving slot 0 from 127.0.0.1:30004 to 127.0.0.1:30002: 
Moving slot 1 from 127.0.0.1:30004 to 127.0.0.1:30002: 
Moving slot 2 from 127.0.0.1:30004 to 127.0.0.1:30002: 
Moving slot 3 from 127.0.0.1:30004 to 127.0.0.1:30002: 
Moving slot 4 from 127.0.0.1:30004 to 127.0.0.1:30002: 
Moving slot 5 from 127.0.0.1:30004 to 127.0.0.1:30002: 
Moving slot 6 from 127.0.0.1:30004 to 127.0.0.1:30002: 
Moving slot 7 from 127.0.0.1:30004 to 127.0.0.1:30002: 
Moving slot 8 from 127.0.0.1:30004 to 127.0.0.1:30002: 
Moving slot 9 from 127.0.0.1:30004 to 127.0.0.1:30002: 
root@iZbp1do67v9l7zwkcs2b22Z:~/kunmk/test/testForRedis/my-cluster# 

6.6 负载均衡

  • cluster选项的rebalance子命令允许用户在有需要时重新分配各个节点负责的槽数量,从而使得各个节点的负载压力趋于平衡:
  • 1)先看各个节点有多少个槽位:
root@iZbp1do67v9l7zwkcs2b22Z:~/kunmk/test/testForRedis/my-cluster# redis-cli --cluster info 127.0.0.1:30001
127.0.0.1:30002 (e61668a9...) -> 3 keys | 5472 slots | 1 slaves.
127.0.0.1:30004 (3d43b8cf...) -> 0 keys | 5451 slots | 1 slaves.
127.0.0.1:30003 (71e40968...) -> 1 keys | 5461 slots | 1 slaves.
[OK] 4 keys in 3 masters.
0.00 keys per slot on average.
  • 2)再把三个节点的槽位数进行平衡(我这里的槽位近乎平衡,不需要再分配了)
root@iZbp1do67v9l7zwkcs2b22Z:~/kunmk/test/testForRedis/my-cluster# redis-cli --cluster rebalance 127.0.0.1:30001
>>> Performing Cluster Check (using node 127.0.0.1:30001)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
*** No rebalancing needed! All nodes are within the 2.00% threshold.
root@iZbp1do67v9l7zwkcs2b22Z:~/kunmk/test/testForRedis/my-cluster# 

6.7 添加节点

  • cluster选项的add-node子命令允许用户将给定的新节点添加到已有的集群当中,用户只需要依次给定新节点的地址以及集群中某个节点的地址即可:
add-node<new_host>:port <existing_host>:<host>
  • 在默认情况下,add-node命令添加的新节点将作为主节点存在。如果用户想要添加的新节点为从节点,那么可以在执行命令的同时,通过给定以下两个可选项来将新节点设置为从节点
--cluster-slave
--cluster-master-id <id>
  • 以下命令展示为将节点30007添加到30001所在的集群中
redis-cli --cluster add-node 127.0.0.1:30007 127.0.0.1:30001

6.8 移除节点

  • 当用户不再需要集群中的某个节点时,可以通过cluster选项的del-node子命令来移除该节点:
redis-cli --cluster del-node 127.0.0.1:30001 <30007的id>

6.9 执行命令

  • 通过cluster选项的call子命令,用户可以在整个集群的所有节点上执行给定的命令:
call host:port command arg arg .. arg
  • 比如,通过执行以下命令,我们可以在集群的所有节点上执行PING命令并获得相应的反馈:
root@iZbp1do67v9l7zwkcs2b22Z:~/kunmk/test/testForRedis/my-cluster# redis-cli --cluster call 127.0.0.1:30001 ping
>>> Calling ping
127.0.0.1:30001: PONG
127.0.0.1:30002: PONG
127.0.0.1:30005: PONG
127.0.0.1:30004: PONG
127.0.0.1:30003: PONG
127.0.0.1:30006: PONG

----------------------------
root@iZbp1do67v9l7zwkcs2b22Z:~/kunmk/test/testForRedis/my-cluster# redis-cli --cluster call 127.0.0.1:30001 dbsize>>> Calling dbsize
127.0.0.1:30001: 0
127.0.0.1:30002: 3
127.0.0.1:30005: 3
127.0.0.1:30004: 0
127.0.0.1:30003: 1
127.0.0.1:30006: 1

6.10 设置超时时间

  • 通过cluster选项的set-timeout子命令,用户可以为集群的所有节点重新设置cluster-node-timeout选项的值:
root@iZbp1do67v9l7zwkcs2b22Z:~/kunmk/test/testForRedis/my-cluster# redis-cli --cluster set-timeout 127.0.0.1:30001 50000>>> Reconfiguring node timeout in every cluster node...
*** New timeout set for 127.0.0.1:30001
*** New timeout set for 127.0.0.1:30002
*** New timeout set for 127.0.0.1:30005
*** New timeout set for 127.0.0.1:30004
*** New timeout set for 127.0.0.1:30003
*** New timeout set for 127.0.0.1:30006
>>> New node timeout set. 6 OK, 0 ERR.
root@iZbp1do67v9l7zwkcs2b22Z:~/kunmk/test/testForRedis/my-cluster# 

6.11 导入数据

  • 用户可以通过cluster选项的import子命令,将给定单机Redis服务器的数据导入集群中:
impotr <node-host>:<port>   		#集群入口节点的IP地址和端口号
--cluster-from <server-host>:<port> #单机服务器的IP地址和端口号
--cluster-copy 						#使用复制导入
--cluster-repalce					#覆盖同名键
  • 在默认情况下,import命令在向集群导入数据的同时,还会删除单机服务器中的源数据。如果用户想要保留单机服务器中的数据,那么可以在执行命令的同时给定–cluster-copy选项。
  • 此外,在导入数据的过程中,如果命令发现将要导入的键在集群数据库中已经存在(同名键冲突),那么命令在默认情况下将中断导入操作。如果用户想要使用导入的键去覆盖集群中已有的同名键,那么可以在执行命令的同时给定–cluster-replace选项。
  • 例子展示:
root@iZbp1do67v9l7zwkcs2b22Z:~/kunmk/test/testForRedis/my-cluster# redis-cli --cluster import 127.0.0.1:30004 --cluster-from 127.0.0.1:6379 --cluster-copy --cluster-replace
>>> Importing data from 127.0.0.1:6379 to cluster 127.0.0.1:30004
>>> Performing Cluster Check (using node 127.0.0.1:30004)
M: 3d43b8cffa4934f2b04b507d6918448e0b179010 127.0.0.1:30004
   slots:[10-5460] (5451 slots) master
   1 additional replica(s)
S: 4a6b040bb361d182a705f224b6db28a0e237d62c 127.0.0.1:30006
   slots: (0 slots) slave
   replicates 71e409685a62c8a45cfaf9bdb39af128e2ae4671
S: 0e634d160d5cd6ca261af2b3c3fadfb9d71b8ea8 127.0.0.1:30005
   slots: (0 slots) slave
   replicates e61668a9235f08029ef9a78a2314c581b14f58c8
S: cb8f6fd1676f6ddbfa0eb7a67ab45d52e7bd6709 127.0.0.1:30001
   slots: (0 slots) slave
   replicates 3d43b8cffa4934f2b04b507d6918448e0b179010
M: 71e409685a62c8a45cfaf9bdb39af128e2ae4671 127.0.0.1:30003
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
M: e61668a9235f08029ef9a78a2314c581b14f58c8 127.0.0.1:30002
   slots:[0-9],[5461-10922] (5472 slots) master
   1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
*** Importing 15 keys from DB 0
Migrating key:{tag}:__rand_int__ to 127.0.0.1:30002: OK
Migrating key:{tag}:000000000005 to 127.0.0.1:30002: OK
Migrating key:{tag}:000000000000 to 127.0.0.1:30002: OK
Migrating key:{tag}:000000000006 to 127.0.0.1:30002: OK
Migrating mylist:{tag} to 127.0.0.1:30002: OK
Migrating key:{tag}:000000000007 to 127.0.0.1:30002: OK
Migrating key:{tag}:000000000008 to 127.0.0.1:30002: OK
Migrating key:{tag}:000000000009 to 127.0.0.1:30002: OK
Migrating mg to 127.0.0.1:30002: OK
Migrating myhash:{tag} to 127.0.0.1:30002: OK
Migrating counter:{tag}:__rand_int__ to 127.0.0.1:30002: OK
Migrating key:{tag}:000000000001 to 127.0.0.1:30002: OK
Migrating key:{tag}:000000000003 to 127.0.0.1:30002: OK
Migrating key:{tag}:000000000004 to 127.0.0.1:30002: OK
Migrating key:{tag}:000000000002 to 127.0.0.1:30002: OK
root@iZbp1do67v9l7zwkcs2b22Z:~/kunmk/test/testForRedis/my-cluster# 
  • 在这个例子中,程序将从单机服务器127.0.0.1:6379向节点127.0.0.1:30001所在的集群导入数据。在这个过程中,集群中的同名键会被覆盖,而单机服务器原有的数据库则会被保留。

7.槽管理命令

7.1 CLUSTER SLOTS:查看槽与节点之间的关联信息

root@iZbp1do67v9l7zwkcs2b22Z:~/kunmk/test/testForRedis/my-cluster# redis-cli -c -p 30001
127.0.0.1:30001> cluster slots
1) 1) (integer) 0
   2) (integer) 9
   3) 1) "127.0.0.1"
      2) (integer) 30002
      3) "e61668a9235f08029ef9a78a2314c581b14f58c8"
   4) 1) "127.0.0.1"
      2) (integer) 30005
      3) "0e634d160d5cd6ca261af2b3c3fadfb9d71b8ea8"
2) 1) (integer) 5461
   2) (integer) 10922
   3) 1) "127.0.0.1"
      2) (integer) 30002
      3) "e61668a9235f08029ef9a78a2314c581b14f58c8"
   4) 1) "127.0.0.1"
      2) (integer) 30005
      3) "0e634d160d5cd6ca261af2b3c3fadfb9d71b8ea8"
3) 1) (integer) 10
   2) (integer) 5460
   3) 1) "127.0.0.1"
      2) (integer) 30004
      3) "3d43b8cffa4934f2b04b507d6918448e0b179010"
   4) 1) "127.0.0.1"
      2) (integer) 30001
      3) "cb8f6fd1676f6ddbfa0eb7a67ab45d52e7bd6709"
4) 1) (integer) 10923
   2) (integer) 16383
   3) 1) "127.0.0.1"
      2) (integer) 30003
      3) "71e409685a62c8a45cfaf9bdb39af128e2ae4671"
   4) 1) "127.0.0.1"
      2) (integer) 30006
      3) "4a6b040bb361d182a705f224b6db28a0e237d62c"
127.0.0.1:30001> 

7.2 CLUSTER ADDSLOTS:把槽指派给节点

7.3 CLUSTER DELSLOTS:撤销对节点的槽指派

7.4 CLUSTER FLUSHSLOTS:撤销对节点的所有槽指派

7.5 CLUSTER KEYSLOT:查看键所属的槽

7.6 CLUSTER COUNTKEYSINSLOT:查看槽包含的键数量

7.7 CLUSTER GETKEYSINSLOT:获取槽包含的键

7.8 CLUSTER SETSLOT:改变槽的状态

8.总结

  • Redis集群与单机版Redis服务器一样,也提供了主从复制功能。在Redis集群中,各个Redis服务器被称为节点,其中主节点负责处理客户端发送的读写命令请求,而从节点则负责对主节点进行复制。
  • Redis集群会将整个数据库空间划分为16384个槽来实现数据分片,而集群中的各个主节点则会分别负责处理其中的一部分槽
  • Redis集群采用无代理模式,客户端发送的所有命令都会直接交由节点执行,并且对于经过优化的集群客户端来说,客户端发送的命令在绝大部分情况下都不需要实施转向,或者仅需要一次转向,因此在Redis集群中执行命令的性能与在单机Redis服务器上执行命令的性能非常接近。
  • 除了节点之间互通信息带来的性能损耗之外,单个Redis集群节点处理命令请求的性能与单个Redis服务器处理命令请求的性能几乎别无二致。从理论上来讲,集群每增加一倍数量的主节点,集群对于命令请求的处理性能就会提高一倍。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值