• Redis-数据库入门


    Redis 数据库

    1. 简介

    ​ Redis 是基于键值对(Key-value)存储数据库。

    官网

    1.1 特性

    1)高速读写

    2)支持数据持久化

    3)支持弱事务

    4)支持高可用,分片集群

    1.2 应用场景

    1)数据缓存

    2)排行榜应用

    3)消息队列,发布订阅

    2. 部署

    2.1 软件安装部署

    2.1.1 单机部署(RHEL6.8)

    # 1. 解压软件
    cd /ups/soft
    tar -xf redis-5.0.0.tar.gz -C /ups/app/
    
    # 2. make编译
    cd /ups/app/redis-5.0.0
    make -j $(cat /proc/cpuinfo |grep -c processor)
    
    cd /ups/app/redis-5.0.0/src
    make test -j $(cat /proc/cpuinfo |grep -c processor)
    
    # 3. make install
    # 安装到指定目录
    make install PREFIX=/ups/app/redis
    
    
    

    img

    2.1.2 Redis参数配置

    #1. 编辑配置文件
    mkdir -p /ups/app/redis/conf
    cp /ups/app/redis-5.0.0/redis.conf /ups/app/redis/conf/redis.conf
    #2. 日志目录
    mkdir -p /ups/app/redis/logs
    
    ln -s /ups/app/redis/config/redis.conf /etc/redis.conf
    
    #3. 默认情况,Redis不是在后台运行,我们需要把redis放在后台运行,将daemonize的值改为yes
    vim /etc/redis.conf
    daemonize yes
    logfile "/ups/app/redis/logs/redisd.log"
    bind 127.0.0.1 192.168.10.162
    

    2.2 多实例

    # 1. 新建配置文件,配置端口,目录,进程文件等配置,如下:
    vi /ups/app/redis/conf/6380.conf
    port 6380
    daemonize yes
    pidfile /var/run/redis-6380.pid
    logfile "/ups/app/redis5/logs/6380.log"
    dir "/ups/data/redisdata/6380/"
    
    vi /ups/app/redis/conf/6381.conf
    port 6381
    daemonize yes
    pidfile /var/run/redis-6381.pid
    logfile "/ups/app/redis5/logs/6381.log"
    dir "/ups/data/redisdata/6381/"
    
    # 2. 启动
    /ups/app/redis5/bin/redis-server /ups/app/redis/conf/6380.conf
    /ups/app/redis5/bin/redis-server /ups/app/redis/conf/6381.conf
    

    redis配置参数说明

    属性 说明
    daemonize 如果值是“yes”,则启动服务的时候是后台守护进程形式,如果值是“no”,则相反
    pidfile 指定存储Redis进程号的文件路径
    port 指定当前Redis服务的端口,默认为6379
    tcp-backlog 此参数确定了TCP连接中已完成队列(完成三次握手之后)的长度, 当然此值必须不大于Linux系统定义的/proc/sys/net/core/somaxconn值,默认是511,而Linux的默认参数值是128。当系统并发量大并且客户端速度缓慢的时候,可以将这二个参数一起参考设定。
    timeout 客户端和Redis服务端的连接超时时间,默认是0,表示永不超时。
    tcp-keepalive 如果值非0,单位是秒,表示将周期性的使用SO_KEEPALIVE检测客户端是否还处于健康状态,避免服务器一直阻塞,官方给出的建议值是60S。
    loglevel Redis总共支持四个级别:debug、verbose、notice、warning。Debug:记录很多信息,用于开发和测试;Varbose:有用的信息,不像debug会记录那么多;Notice:普通的verbose,常用于生产环境;Warning:只有非常重要或者严重的信息会记录到日志;默认是notice级别。
    logfile 日志的存储路径
    databases 可用的数据库数,默认值为16,默认数据库为0,数据库范围在0-(database-1)之间,个人觉得DB的概念类似于命名空间
    save 保存数据库快照信息到磁盘,其对应的值有两个,比如save 300 10表示:300秒内至少有300个key被改变时,触发保存信息到磁盘的事件。
    stop-writes-on-bgsave-error 当持久化出现错误之后,是否继续提供写服务
    rdbcompression 持久化到RDB文件时,是否压缩,“yes”为压缩,“no”则反之
    rdbchecksum 读取和写入的时候是否支持CRC64校验,默认是开启的
    dbfilename 镜像文件的名字
    dir 当前工作目录,配置文件和镜像文件等都在此目录下
    masterauth 设置访问master服务器的密码
    slave-serve-stale-data 当slave服务器和master服务器失去连接后,或者当数据正在复制传输的时候,如果此参数值设置“yes”,slave服务器可以继续接受客户端的请求,否则,会返回给请求的客户端如下信息“SYNC with master in progress”
    slave-read-only 是否允许slave服务器节点只提供读服务
    repl-disable-tcp-nodelay 指定向slave同步数据时,是否禁用socket的NO_DELAY选 项。若配置为“yes”,则禁用NO_DELAY,则TCP协议栈会合并小包统一发送,这样可以减少主从节点间的包数量并节省带宽,但会增加数据同步到 slave的时间。若配置为“no”,表明启用NO_DELAY,则TCP协议栈不会延迟小包的发送时机,这样数据同步的延时会减少,但需要更大的带宽。 通常情况下,应该配置为no以降低同步延时,但在主从节点间网络负载已经很高的情况下,可以配置为yes。
    slave-priority 指定slave的优先级。在不只1个slave存在的部署环境下,当master宕机时,Redis Sentinel会将priority值最小的slave提升为master。需要注意的是,若该配置项为0,则对应的slave永远不会自动提升为master。
    appendonly 开启append only 模式之后,redis 会把所接收到的每一次写操作请求都追加到appendonly.aof 文件中,当redis 重新启动时,会从该文件恢复出之前的状态。但是这样会造成appendonly.aof 文件过大,所以redis 还支持了BGREWRITEAOF 指令,对appendonly.aof 进行重新整理。默认是不开启的。
    appendfilename 默认为appendonly.aof。
    appendfsync 设置aof的同步频率,有三种选择always、everysec、no,默认是everysec表示每秒同步一次。
    no-appendfsync-on-rewrite 指定是否在后台aof文件rewrite期间调用fsync,默认为no,表示要调用fsync(无论后台是否有子进程在刷盘)。Redis在后台写RDB文件或重写afo文件期间会存在大量磁盘IO,此时,在某些linux系统中,调用fsync可能会阻塞。
    auto-aof-rewrite-percentage 指定Redis重写aof文件的条件,默认为100,表示与上次rewrite的aof文件大小相比,当前aof文件增长量超过上次afo文件大小的100%时,就会触发background rewrite。若配置为0,则会禁用自动rewrite
    auto-aof-rewrite-min-size 指定触发rewrite的aof文件大小。若aof文件小于该值,即使当前文件的增量比例达到auto-aof-rewrite-percentage的配置值,也不会触发自动rewrite。即这两个配置项同时满足时,才会触发rewrite。
    lua-time-limit 一个Lua脚本最长的执行时间,单位为毫秒,如果为0或负数表示无限执行时间,默认为5000
    notify-keyspace-events 见参考3,按键通知事件
    aof-rewrite-incremental-fsync aof rewrite过程中,是否采取增量文件同步策略,默认为“yes”。 rewrite过程中,每32M数据进行一次文件同步,这样可以减少aof大文件写入对磁盘的操作次数

    2.3 OS 参数配置

    #1. 持久配置启效
    cp /etc/sysctl.conf{,_$(date +%Y%m%d)}
    echo -e "
    # redis config
    vm.overcommit_memory=1
    net.core.somaxconn= 1024" >> /etc/sysctl.conf
    
    #2. 大内存页-临时配置
    echo never > /sys/kernel/mm/transparent_hugepage/enabled
    echo 511 > /proc/sys/net/core/somaxconn
    
    cat >> /etc/rc.local <<EOF
    echo never > /sys/kernel/mm/transparent_hugepage/enabled
    echo 511 > /proc/sys/net/core/somaxconn
    EOF
    

    img

    2.4 配置用户环境变量

    cat > /etc/profile.d/redis.sh << EOF
    export REDIS_HOME=/ups/app/redis
    export PATH=${REDIS_HOME}/bin:${PATH}
    EOF
    
    . /etc/profile.d/redis.sh
    
    

    2.5 启动脚本

    vi /ups/app/redis/config/redisd
    #!/bin/sh
    #
    # redis        init file for starting up the redis daemon
    #
    # chkconfig:  35 90 10
    # description: Starts and stops the redis daemon.
    
    # Source function library.
    . /etc/rc.d/init.d/functions
    # APP HOME PATH
    REDIS_BIN_DIR=/ups/app/redis/bin
    PROG="redis-server"
    # 指定redis的配置文件路径
    CONFIG="/etc/redis.conf"
    # 指定redis-server命令的位置(whereis redis-server)
    EXEC="${REDIS_BIN_DIR}/${PROG}"
    HOSTIP=$(hostname -i)
    # 指定redis的监听端口(和配置文件里保持一致)
    PORT=$(grep '^port' ${CONFIG}|awk '{print $NF}')
    # 指定redis的pid文件路径(和配置文件里保持一致)
    PIDFILE=$(grep '^pidfile' ${CONFIG}|awk '{print $NF}')
    # 指定redis客户端位置
    REDIS_CLI=${REDIS_BIN_DIR}/redis-cli
    # REDIS_USER
    REDIS_USER=${USER}
    LOCKFILE=/var/lock/subsys/${PROG}.lk
    RETVAL=0
    
    start() {
        # [[ -f "${CONFIG}" ]] || exit 1
        # [[ -x "${EXEC}" ]] || exit 1
        if [[ ! -e "${PIDFILE}" ]]; then
            # echo -n $"Starting ${PROG}: "
            daemon --user ${REDIS_USER} ${EXEC} ${CONFIG}
            # $EXEC $CONFIG
            [[ "$?" = "0" ]] && touch ${LOCKFILE}
            return ${RETVAL}
        fi
    }
    stop() {
        if [[ -f "${PIDFILE}" ]]; then
            # PID=$(cat $PIDFILE)
            # echo -n $"Stopping ${PROG}: "
            ${REDIS_CLI} -h ${HOSTIP} -p ${PORT} SHUTDOWN
            # killproc -p $PIDFILE ${PROG}
            sleep 2
            while [[ -f "${PIDFILE}" ]]; do
                sleep 1
            done
            [[ ! -f "${PIDFILE}" ]] && rm -f ${LOCKFILE}
            return ${RETVAL}
        fi
    }
    restart() {
        stop
        start
    }
    
    # status() {
    #     status -p ${PIDFILE} ${PROG}
    #     return ${RETVAL}
    # }
    
    case "$1" in
        start)
            $1
            ;;
        stop)
            $1
            ;;
        restart)
            $1
            ;;
        *)
            echo $"Usage: $0 {start|stop|restart}"
            exit 1
    esac
    
    # -------------------------------------------------------------------
    
    # 开机启动配置
    
    ln -s /ups/app/redis/config/redisd /etc/init.d/redisd
    
    chmod +x /ups/app/redis/config/redisd /etc/init.d/redisd
    
    chkconfig --add redisd
    
    chkconfig --level 35 redisd on
    

    2.6 管理服务

    # 启动redis服务
    cd /ups/app/redis
    ./bin/redis-server ./conf/redis.conf
    
    # 关闭redis服务
    ./bin/redis-cli shutdown
    
    

    rhel7配置服务

    cat > /usr/lib/systemd/system/redis.service <<-EOF
    [Unit]
    Description=Redis
    After=network.target
    
    [Service]
    ExecStart=/ups/app/redis5/bin/redis-server /ups/app/redis5/redis.conf --daemonize no
    ExecStop=/ups/app/redis5/bin/redis-cli -h 127.0.0.1 -p 6379 shutdown
    
    [Install]
    WantedBy=multi-user.target
    EOF
    
    --加载配置
    systemctl daemon-reload
    --启动
    systemctl start redis
    --关闭
    systemctl stop redis
    --查看状态
    systemctl status redis
    --开机启动
    systemctl enable redis
    --关闭开机自启
    systemctl disable redis
    
    

    3. 数据持久化

    Redis提供了两种不同的持久化方法可以将数据存储在磁盘中,一种叫快照持久化(RDB),另一种叫只追加文件持久化(AOF)。

    3.1 RDB

    Redis通过创建快照的方式获取某一时刻Redis中所有数据的副本。

    3.1.1 创建快照的方式

    1)客户端直接通过命令BGSAVE或者SAVE来创建一个快照 (手动触发)

    -  BGSAVE 是通过redis调用fork来创建一个子进程,然后子进程负责将快照写入磁盘,而父进程仍然继续处理命令。
    -  SAVE 是在没有足够的内存空间去执行BGSAVE或者无所谓等待的时候。执行SAVE命令过程中,redis不在响应任何其他命令,直到RDB过程完成为止.
    

    2)在redis.conf中设置save配置选项

    # 当在规定的时间内,Redis发生了写操作的个数满足条件,会触发发生BGSAVE命令。
    # save <seconds> <changes>
    # 当用户设置了多个save的选项配置,只要其中任一条满足,Redis都会触发一次BGSAVE操作,比如:900秒之内至少一次写操作、300秒之内至少发生10次写操作、60秒之内发生至少10000次写操作都会触发发生快照操作
    save 900 1
    save 300 10
    save 60 10000
    

    3)当Redis通过shutdown命令关闭服务器请求时,会执行SAVE命令创建一个快照,如果使用kill -9 PID将不会创建快照。

    4)将快照复制到其他服务器从而完成Redis的主从复制

    3.1.2 相关配置参数

    # RDB文件名
    dbfilename "dump.rdb"
    # 导出的rdb文件是否压缩
    rdbcompress yes
    # 后台备份进程出错时,主进程停不停止写入? 主进程不停止容易造成数据不一致
    stop-writes-on-bgsave-error no
    # 导入rbd恢复时数据时,要不要检验rdb的完整性 验证版本是不是一致
    rdbchecksum yes
    save 60 1000
    
    # RDB文件和AOF文件路径
    dir "/usr/local/var/db/redis"
    

    img

    3.1.3 优缺点

    - 优点:
    1. Redis加载RDB恢复数据远远快于AOF的方式;
    2. RDB是一个紧凑压缩的二进制文件, 代表Redis在某个时间点上的数据快照
    
    - 缺点:
    1. RDB方式数据没办法做到实时持久化/秒级持久化
    2. RDB文件使用特定二进制格式保存,存在老版本Redis服务无法兼容新版RDB格式的问题
    
    

    Note

    ​ 在只使用快照持久化来报错数据时,如果系统崩溃或者强杀,用户将会丢失最近一次生成快照之后更改的所有数据。因此如果应用程序对于两次快照间丢失的数据可接受,利用快照就是一个很好的方式,但是往往一些系统对于丢失几分钟的数据都不可接受,比如高频的电子商务系统。

    ​ 此外,如果Redis存储的数据量长达数十G的时候,没执行一次快照需要花费大量时间,严重影响到服务器的性能。因此,针对上述的问题,可以使用AOF方式来持久化数据。

    3.2 AOF(append only file)

    ​ 在执行写命令时,AOF持久化会将执行的写命令也写到AOF文件的末尾,以此来记录数据的变化。换句话说,将AOF文件中包含的内容重新执行一遍,就可以恢复AOF文件所记录的数据集。Redis会在收到客户端修改指令后,进行参数校验,逻辑处理,如果没问题就立即将该指令文本存储到AOF文件中。先执行执行才将日志存盘

    3.2.1 配置参数

    # redis默认关闭AOF机制,可以将no改成yes实现AOF持久化
    appendonly no
    # AOF文件
    appendfilename "appendonly.aof"
    # AOF持久化同步频率,always表示每个Redis写命令都要同步fsync写入到磁盘中,但是这种方式会严重降低redis的速度;everysec表示每秒执行一次同步fsync,显示的将多个写命令同步到磁盘中;no表示让操作系统来决定应该何时进行同步fsync,Linux系统往往可能30秒才会执行一次
    # appendfsync always
    appendfsync everysec
    # appendfsync no
    
    # 在日志进行BGREWRITEAOF时,如果设置为yes表示新写操作不进行同步fsync,只是暂存在缓冲区里,避免造成磁盘IO操作冲突,等重写完成后在写入。redis中默认为no  
    no-appendfsync-on-rewrite no   
    # 当前AOF文件大小是上次日志重写时的AOF文件大小两倍时,发生BGREWRITEAOF操作。  
    auto-aof-rewrite-percentage 100  
    #当前AOF文件执行BGREWRITEAOF命令的最小值,避免刚开始启动Reids时由于文件尺寸较小导致频繁的BGREWRITEAOF。  
    auto-aof-rewrite-min-size 64mb  
    # Redis再恢复时,忽略最后一条可能存在问题的指令(因为最后一条指令可能存在问题,比如写一半时突然断电了)
    aof-load-truncated yes
    #Redis4.0新增RDB-AOF混合持久化格式,在开启了这个功能之后,AOF重写产生的文件将同时包含RDB格式的内容和AOF格式的内容,其中RDB格式的内容用于记录已有的数据,而AOF格式的内存则用于记录最近发生了变化的数据,这样Redis就可以同时兼有RDB持久化和AOF持久化的优点(既能够快速地生成重写文件,也能够在出现问题时,快速地载入数据)。
    aof-use-rdb-preamble no
    

    img

    3.2.2 重写/压缩AOF文件

    ​ 随着写操作的不断增加,AOF文件会越来越大。例如你递增一个计数器100次,那么最终结果就是数据集里的计数器的值为最终的递增结果,但是AOF文件里却会把这100次操作完整的记录下来。而事实上要恢复这个记录,只需要1个命令就行了,也就是说AOF文件里那100条命令其实可以精简为1条。所以Redis支持这样一个功能:在不中断服务的情况下在后台重建AOF文件。

    Redis提供bgrewriteaof指令用于对AOF日志进行瘦身,其原理是开辟一个子进程对内存进行遍历,转换成一系列的操作指令,序列化到新的AOF日志文件中。序列化完毕后再将操作期间增量的AOF日志追加到新AOF日志文件中,追加完毕后就立即替代旧的AOF文件。

    -- AOF重写过程可以手动触发和自动触发:
    - 手动触发: 直接调用bgrewriteaof命令
    - 自动触发: 根据auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参数确定自动触发时机
    
    自动触发时机=aof_current_size>auto-aof-rewrite-minsize && (aof_current_size-aof_base_size)/aof_base_size>=auto-aof-rewritepercentage
    其中aof_current_size和aof_base_size可以在info Persistence统计信息中查看。
    
    127.0.0.1:6379> info Persistence
    # Persistence
    loading:0
    rdb_changes_since_last_save:0
    rdb_bgsave_in_progress:0
    rdb_last_save_time:1574128278
    rdb_last_bgsave_status:ok
    rdb_last_bgsave_time_sec:0
    rdb_current_bgsave_time_sec:-1
    rdb_last_cow_size:425984
    aof_enabled:1
    aof_rewrite_in_progress:0
    aof_rewrite_scheduled:0
    aof_last_rewrite_time_sec:0
    aof_current_rewrite_time_sec:-1
    aof_last_bgrewrite_status:ok
    aof_last_write_status:ok
    aof_last_cow_size:462848
    aof_current_size:988
    aof_base_size:988
    aof_pending_rewrite:0
    aof_buffer_length:0
    aof_rewrite_buffer_length:0
    aof_pending_bio_fsync:0
    aof_delayed_fsync:0
    127.0.0.1:6379> 
    
    
    [2] Persistence指标
    属性名称 属性值
    rdb_bgsave_in_progress bgsave子进程是否正在执行
    rdb_current_bgsave_time_sec 当前运行bgsave的时间,-1表示未运行
    aof_enabled 是否开启AOF功能
    aof_rewrite_in_progress AOF重写子进程是否正在运行
    aof_rewrite_scheduled 在bgsave结束后是否运行AOF重写
    aof_current_rewrite_time_sec 当前运行AOF重写的时间,-1表示未运行
    aof_current_size AOF文件当前的字节数
    aof_base_size AOF上次重写rewrite的字节数

    3.2.3 AOF缓冲区文件同步策略

    通过参数appendfsync控制AOF缓冲区文件同步

    说明
    always 命令写入AOF_buf后调用系统fsync操作同步到AOF文件,fsync完成后线程返回
    everysec 命令写入aof_buf后调用系统write操作,write完成后线程返回。fsync同步文件操作哟专门的线程每秒调用一次
    no 命令写入aof_buf后调用系统write操作,不对AOF文件做fsync同步,同步硬盘操作又操作系统负责,通常同步周期最长30秒

    4. API 接口

    4.1 redis-cli

    4.1.1 全局命令

    [1] 查看所有键(key/scan)
    127.0.0.1:6379> select 0
    OK
    127.0.0.1:6379> set hello world
    OK
    127.0.0.1:6379> keys *
    1) "hello"
    127.0.0.1:6379>
    
    -- keys pattern : pattern 使用glob风格通配符;glob是shell使用的路径通配符
    
    scan cursor [MATCH pattern] [COUNT count] : cursor是一个游标,第一遍从0开始,每次遍历完返回当前游标值,直到游标值为0,表示遍历结束。
    
    127.0.0.1:6379> scan 0
    1) "9"
    2)  1) "user:1_2:inter"
        2) "user:2"
        3) "user:ranking"
        4) "user:ranking:1"
        5) "user:1_2:diff"
        6) "user:2:follow"
        7) "myset"
        8) "listkey"
        9) "hello"
       10) "user:ranking:1_inter_2"
    127.0.0.1:6379> scan 9
    1) "0"
    2) 1) "user:ranking:2"
       2) "mylist"
       3) "user:ranking:1_union_2"
       4) "user:1:follow"
       5) "user:1_2:union"
       6) "java"
       7) "python"
       8) "user:1"
    127.0.0.1:6379> 
    
    hscan: 哈希类型遍历
    zscan: 有序集合遍历
    sscan: 集合类型遍历
    
    [2] 查看当前库键总数 (dbsize)
    127.0.0.1:6379> dbsize
    (integer) 1
    127.0.0.1:6379>
    
    127.0.0.1:6379> set java jedis
    OK
    127.0.0.1:6379> set python redis-py
    OK
    127.0.0.1:6379>
    127.0.0.1:6379> dbsize
    (integer) 3
    127.0.0.1:6379> keys *
    1) "hello"
    2) "python"
    3) "java"
    127.0.0.1:6379>
    
    [3]查看键是否存在 (exists)
    exists key [key ...]
    
    127.0.0.1:6379> exists hello
    (integer) 1
    127.0.0.1:6379> exists hello java
    (integer) 2
    127.0.0.1:6379>
    
    [4]删除键 (del)
    del key [key ...]
    
    127.0.0.1:6379> del java
    (integer) 1
    127.0.0.1:6379> exists java
    (integer) 0
    127.0.0.1:6379>
    
    [5]键过期 (expire)
    expire key seconds : 设置键过期时间,单位:秒
    expireat key timestamp: 键在秒级时间戳后过期
    pexpire key milliseconds: 键在milliseconds毫秒后过期
    pexpireat key milliseconds-timestamp键在毫秒级时间戳timestamp后过期
    ttl key : 查询键剩余过期时间
    pttl key: 查询键剩余过期时间,精度更高,达到毫秒级
    persist命令可以将键的过期时间清除
    
    127.0.0.1:6379> set hello world
    OK
    127.0.0.1:6379> expire hello 10
    (integer) 1
    127.0.0.1:6379> ttl hello
    (integer) 7
    127.0.0.1:6379> 
    127.0.0.1:6379> ttl hello
    (integer) 3
    # 结果为-2,表示键已经过期被删除
    127.0.0.1:6379> ttl hello
    (integer) -2
    127.0.0.1:6379> get hello
    (nil)
    127.0.0.1:6379> get python
    "redis-py"
    127.0.0.1:6379>
    
    [6] 键数据结构类型 (type)
    type key
    
    127.0.0.1:6379> type python
    string
    127.0.0.1:6379> get python
    "redis-py"
    127.0.0.1:6379> type python
    string
    127.0.0.1:6379> rpush mylist a b c d e
    (integer) 5
    127.0.0.1:6379> type mylist
    list
    127.0.0.1:6379> 
    
    [7] 键重命名(rename)
    rename/renamenx key newkey
    
    127.0.0.1:6379> get python
    "redis-pyhelloworld"
    127.0.0.1:6379> rename python java
    OK
    127.0.0.1:6379> get python
    (nil)
    127.0.0.1:6379> get java
    "redis-pyhelloworld"
    127.0.0.1:6379>
    
    127.0.0.1:6379> renamenx java python
    (integer) 0
    127.0.0.1:6379>
    
    [9]随机返回一个键 (randomkey)
    127.0.0.1:6379> randomkey 
    "user:ranking:1_inter_2"
    127.0.0.1:6379> 
    
    [9]切换数据库(select)
    select index
    
    127.0.0.1:6379> select 2
    OK
    127.0.0.1:6379[2]>
    
    -- flushdb/flushall命令用于清除数据库, 两者的区别的是flushdb只清除当前数据库, flushall会清除所有数据库
    
    [10] 键迁移
    move key db :redis内部数据库间迁移数据
    
    
    

    img

    dump + restore : 不同实例间数据迁移数据
    dump key
    restore key ttl serialized-value [REPLACE]
    
    
    

    img

    migrate: 具有原子性,实例间数据迁移数据
    migrate host port key| destination-db timeout [COPY] [REPLACE] [KEYS key]
    第一, 整个过程是原子执行的, 不需要在多个Redis实例上开启客户端的, 只需要在源Redis上执行migrate命令即可。 
    第二, migrate命令的数据传输直接在源Redis和目标Redis上完成的。 
    第三, 目标Redis完成restore后会发送OK给源Redis, 源Redis接收后会根据migrate对应的选项来决定是否在源Redis上删除对应的键
    

    对比

    命令 作用域 原子性 支持多个键
    move 实例之内
    dump+restore 实例之间
    migrate 实例之间
    [11] 获取配置 (config)
    127.0.0.1:6379> config help
    1) CONFIG <subcommand> arg arg ... arg. Subcommands are:
    2) GET <pattern> -- Return parameters matching the glob-like <pattern> and their values.
    3) SET <parameter> <value> -- Set parameter to value.
    4) RESETSTAT -- Reset statistics reported by INFO.
    5) REWRITE -- Rewrite the configuration file.
    127.0.0.1:6379>
    127.0.0.1:6379> CONFIG GET slowlog*
    1) "slowlog-log-slower-than"
    2) "10000"
    3) "slowlog-max-len"
    4) "128"
    127.0.0.1:6379>
    
    -- 修改配置
    127.0.0.1:6379> config set slowlog-log-slower-than 1000
    OK
    -- 写入文件
    127.0.0.1:6379> config rewrite
    OK
    127.0.0.1:6379>
    -- 查看慢查询日志
    127.0.0.1:6379> slowlog get
    1) 1) (integer) 0
       2) (integer) 1574122255
       3) (integer) 2609
       4) 1) "config"
          2) "rewrite"
       5) "127.0.0.1:62268"
       6) ""
    127.0.0.1:6379> 
    
    /*
    每个慢查询由四个属性组成
    - 慢查询的标识ID
    - 发生时间戳
    - 命令耗时,单位为微秒
    - 执行的命令和参数
    - 客户端网络套接字(ip: port)
    */
    
    -- 获取慢查询日志列表当前的长度
    127.0.0.1:6379> slowlog len
    (integer) 1
    127.0.0.1:6379>
    
    -- 慢查询日志重置(清空列表)
    slowlog reset
    

    4.1.2 字符串string

    [1] set
    set key value [expiration EX seconds|PX milliseconds] [NX|XX]
    - EX seconds : 为键设置秒级过期时间
    - PX milliseconds : 为键设置毫秒级过期时间
    - nx: 键必须不存在才可以设置成功
    - xx: 键必须存在才可以设置成功
    
    setex : setex key seconds value
    setnx: setnx key value
    
    [2] get
    get key
    
    [3] mset
    mset key value [ key value...]
    
    [4] mget
    mget key [key ...]
    
    
    

    mset/mget可以节省N此网络时间

    [5] 计数
    incr key: 用于对值做自增操作
    decr key: 用于对值自减操作
    incrby key increment : 对值自增指定数字
    decrby key decrement : 对值自减指定数字
    incybyfloat key increment : 对值自增浮点数
    
    [6] append key value: 对值追加值
    127.0.0.1:6379> get python
    "redis-py"
    127.0.0.1:6379> APPEND python helloworld
    (integer) 18
    127.0.0.1:6379> get python
    "redis-pyhelloworld"
    127.0.0.1:6379> 
    
    [7] strlen key :字符串长度
    127.0.0.1:6379> get python
    "redis-pyhelloworld"
    127.0.0.1:6379> strlen python
    (integer) 18
    127.0.0.1:6379>
    
    [8] getset key value: 设置并返回原值
    127.0.0.1:6379> get hello
    (nil)
    127.0.0.1:6379> set hello wrold
    OK
    127.0.0.1:6379> get hello
    "wrold"
    127.0.0.1:6379> getset hello world
    "wrold"
    127.0.0.1:6379> get hello
    "world"
    127.0.0.1:6379>
    
    [9] getrange key start end: 获取部分字符串
    127.0.0.1:6379> get python
    "redis-pyhelloworld"
    127.0.0.1:6379> getrange python 1 3
    "edi"
    127.0.0.1:6379>
    

    时间复杂度

    img

    4.1.3 哈希hash

    哈希类型值键值本身又是一个键值对结构

    img

    [1] hset key field value: 设置值
    127.0.0.1:6379> hset user:1 name tom
    (integer) 1
    127.0.0.1:6379>
    
    [2] hget key field: 获取值
    127.0.0.1:6379> hget user:1 name
    "tom"
    127.0.0.1:6379>
    
    [3] hdel key field [field ...] : 删除field
    127.0.0.1:6379> hdel user:1 name
    (integer) 1
    127.0.0.1:6379> hget user:1 name
    (nil)
    127.0.0.1:6379> hget user:1 age
    (nil)
    127.0.0.1:6379>
    
    [4] hlen key : 计算field个数
    127.0.0.1:6379> hset user:1 name tom
    (integer) 1
    127.0.0.1:6379> hset user:1 age 18
    (integer) 1
    127.0.0.1:6379> hlen user:1
    (integer) 2
    127.0.0.1:6379>
    
    [5] 批量设置或获取
    hmget key field [field ...]
    hmset key field value [field value ...]
    127.0.0.1:6379> hmset user:2 name biao age 18 city gz
    OK
    127.0.0.1:6379> hmget user:2 name age city
    1) "biao"
    2) "18"
    3) "gz"
    127.0.0.1:6379>
    
    [6] hexists key field: 判断field是否存在
    127.0.0.1:6379> hexists user:1 age 
    (integer) 1
    127.0.0.1:6379>
    
    [7] hkeys key: 获取所有field
    127.0.0.1:6379> hkeys user:2
    1) "name"
    2) "age"
    3) "city"
    127.0.0.1:6379>
    
    [8] hvals key : 获取所有value
    127.0.0.1:6379> hvals user:2
    1) "biao"
    2) "18"
    3) "gz"
    127.0.0.1:6379> 
    
    [9] hgetall key: 获取所有field-value
    127.0.0.1:6379> hgetall user:2
    1) "name"
    2) "biao"
    3) "age"
    4) "18"
    5) "city"
    6) "gz"
    127.0.0.1:6379>
    
    [10] hscan key cursor [MATCH pattern] [COUNT count] : 遍历
    127.0.0.1:6379> hscan user:2 0
    1) "0"
    2) 1) "name"
       2) "biao"
       3) "age"
       4) "18"
       5) "city"
       6) "gz"
    127.0.0.1:6379> hscan user:2 0 match name match city
    1) "0"
    2) 1) "city"
       2) "gz"
    127.0.0.1:6379>
    
    [11] hincrby/hincrbyfloat : 自增
    hincrby key field increment
    hincrbyfloat key field increment 
    
    [12] hstrlen key field: 计算value字符串长度
    hstrlen key field
    127.0.0.1:6379> hget user:1 name
    "tom"
    127.0.0.1:6379> hstrlen user:1 name
    (integer) 3
    127.0.0.1:6379>
    

    时间复杂度

    img

    4.1.4 列表list

    [1] 增加
    # 1. rpush key value [value ...] : 从右边插入元素
    127.0.0.1:6379> rpush listkey a c b
    (integer) 3
    127.0.0.1:6379> lrange listkey 0 -1
    1) "a"
    2) "c"
    3) "b"
    127.0.0.1:6379> 
    
    # 2. lpush key value [value ...] : 从左边插入
    
    # 3. linsert key BEFORE|AFTER pivot value : 向某个元素前或后插入
    127.0.0.1:6379> linsert listkey before c java
    (integer) 4
    127.0.0.1:6379> lrange listkey 0 -1
    1) "a"
    2) "java"
    3) "c"
    4) "b"
    127.0.0.1:6379>
    
    [2] 查找
    #4. lrange key start stop: 获取指定范围元素列表
    
    #5. lindex key index: 获取列表指定索引下标元素
    127.0.0.1:6379> lindex listkey -1
    "b"
    127.0.0.1:6379>
    
    #6. llen key: 获取列表长度
    127.0.0.1:6379> llen listkey
    (integer) 4
    127.0.0.1:6379> 
    
    [3] 删除
    #7. lpop key : 从列表左侧弹出元素
    127.0.0.1:6379> lrange listkey 0 -1
    1) "a"
    2) "java"
    3) "c"
    4) "b"
    127.0.0.1:6379> lpop listkey
    "a"
    127.0.0.1:6379> lrange listkey 0 -1
    1) "java"
    2) "c"
    3) "b"
    127.0.0.1:6379> 
    
    #8. rpop key: 列表右侧弹出元素
    
    #9. lrem key count value : 删除指定元素
    - count>0, 从左到右,删除最多count个元素
    - count<0, 从右到左,删除最多count个元素
    - count=0, 删除所有
    
    127.0.0.1:6379> lrem listkey 3 c
    (integer) 1
    127.0.0.1:6379> lrange listkey 0 -1
    1) "java"
    2) "b"
    127.0.0.1:6379>
    
    [4] 修改
    # 10. ltrim key start stop: 按照索引范围修剪元素
    127.0.0.1:6379> ltrim key 1 3
    OK
    127.0.0.1:6379> lrange listkey 0 -1
    1) "java"
    2) "b"
    127.0.0.1:6379> 
    
    # 11. lset key index newvalue : 修改指定索引下标元素
    127.0.0.1:6379> lrange listkey 0 -1
    1) "java"
    2) "b"
    127.0.0.1:6379> lset listkey 1 python
    OK
    127.0.0.1:6379> lrange listkey 0 -1
    1) "java"
    2) "python"
    127.0.0.1:6379> 
    
    [5] 阻塞操作
    # 12. 阻塞
    blpop key [key ...] timeout
    brpop key [key ...] timeout
    
    

    时间复杂度

    img

    4.1.5 集合set

    [1] CURD
    #1. sadd key member [member ...] : 添加元素
    127.0.0.1:6379> sadd myset a b d c e
    (integer) 5
    
    #2. srem key member [member ...] : 删除元素
    127.0.0.1:6379> srem myset b e
    (integer) 2
    127.0.0.1:6379>
    
    #3. scard key : 计算元素个数
    127.0.0.1:6379> scard myset
    (integer) 3
    127.0.0.1:6379>
    
    #4. sismember key member: 判断元素是否在集合中
    127.0.0.1:6379> smembers myset
    1) "d"
    2) "c"
    3) "a"
    127.0.0.1:6379> sismember myset a
    (integer) 1
    127.0.0.1:6379> 
    
    #5. srandmember key [count] : 随机从集合返回指定个数元素(不会删除元素)
    127.0.0.1:6379> smembers myset
    1) "g"
    2) "e"
    3) "d"
    4) "c"
    5) "f"
    6) "b"
    7) "a"
    127.0.0.1:6379> srandmember myset 2
    1) "f"
    2) "d"
    127.0.0.1:6379> smembers myset
    1) "g"
    2) "e"
    3) "d"
    4) "c"
    5) "f"
    6) "b"
    7) "a"
    127.0.0.1:6379>
    
    #6. spop key : 从集合随机弹出元素,并将他们从集合中删除
    127.0.0.1:6379> smembers myset
    1) "g"
    2) "e"
    3) "d"
    4) "c"
    5) "f"
    6) "b"
    7) "a"
    127.0.0.1:6379> spop myset 2
    1) "a"
    2) "c"
    127.0.0.1:6379> smembers myset
    1) "g"
    2) "e"
    3) "d"
    4) "f"
    5) "b"
    127.0.0.1:6379> 
    
     #7.  smembers key: 获取所有元素
    127.0.0.1:6379> smembers myset
    1) "d"
    2) "c"
    127.0.0.1:6379>
    
    [2] 集合间运算
    127.0.0.1:6379> sadd user:1:follow it music his sports
    (integer) 4
    127.0.0.1:6379> sadd user:2:follow it news ent sports
    (integer) 4
    
    #1. sinter key [key ...] : 交集
    127.0.0.1:6379> sinter user:1:follow user:2:follow
    1) "sports"
    2) "it"
    127.0.0.1:6379>
    
    #2. sunion key [key ...] : 并集
    127.0.0.1:6379> sunion user:1:follow user:2:follow
    1) "sports"
    2) "his"
    3) "music"
    4) "ent"
    5) "it"
    6) "news"
    127.0.0.1:6379>
    
    #3. sdiff key [key ...] : 差集
    127.0.0.1:6379> sdiff user:1:follow user:2:follow
    1) "music"
    2) "his"
    127.0.0.1:6379> sdiff user:2:follow user:1:follow
    1) "ent"
    2) "news"
    127.0.0.1:6379>
    
    #4. 保存集合运算之后结果集操作
    sinterstore destination key [key ...]
    sunionstore destination key [key ...]
    sdiffstore destination key [key ...]
    
    127.0.0.1:6379> sinterstore user:1_2:inter user:1:follow user:2:follow
    (integer) 2
    127.0.0.1:6379> smembers user:1_2:inter
    1) "sports"
    2) "it"
    127.0.0.1:6379>
    
    127.0.0.1:6379> sunionstore user:1_2:union user:1:follow user:2:follow
    (integer) 6
    127.0.0.1:6379> smembers user:1_2:union
    1) "his"
    2) "music"
    3) "sports"
    4) "ent"
    5) "it"
    6) "news"
    127.0.0.1:6379>
    
    127.0.0.1:6379> sdiffstore user:1_2:diff user:1:follow user:2:follow
    (integer) 2
    127.0.0.1:6379> smembers user:1_2:diff
    1) "music"
    2) "his"
    127.0.0.1:6379>
    
    [3] 时间复杂度

    img

    4.1.6 有序集合SortedSet

    [1] CURD
    -- zadd key [NX|XX] [CH] [INCR] score member [score member ...] : 添加成员
    127.0.0.1:6379> zadd user:ranking 251 tom
    (integer) 1
    127.0.0.1:6379>
    
    - NX: member 必须不存在才可以添加
    - XX: member 必须存在才可以添加,用于更新
    - CH: 返回操作后有序集合和分数发生变化的个数
    - INCR: 对score做增加
    
    -- zcard key : 计算成员个数
    127.0.0.1:6379> zcard user:ranking
    (integer) 1
    127.0.0.1:6379>
    
    -- zscore key member: 计算成员分数
    127.0.0.1:6379> zscore user:ranking tom
    "251"
    127.0.0.1:6379>
    
    --  zadd user:ranking 1 kris 91 mike 200 frank 220 tim 250 martin
    
    -- 计算成员排名 
    zrank key member : 分数从高到低的排名
    zrevrank key member : 分数从低到高的排名
    
    -- zrem key member : 删除成员
    127.0.0.1:6379> zrem user:ranking tim
    (integer) 1
    127.0.0.1:6379> 
    
    -- zincrby key increment member : 增加成员分数
    127.0.0.1:6379> zscore user:ranking tom
    "251"
    127.0.0.1:6379> zincrby user:ranking 8 tom
    "259"
    127.0.0.1:6379>
    
    -- 返回指定排名范围的成员
    zrange key start stop [WITHSCORES]   : 按照分值从低到高返回
    zrevrange key start stop [WITHSCORES]
    
    127.0.0.1:6379> zrange user:ranking 0 2 withscores
    1) "kris"
    2) "1"
    3) "mike"
    4) "91"
    5) "frank"
    6) "200"
    127.0.0.1:6379> zrange user:ranking 0 -1 withscores
     1) "kris"
     2) "1"
     3) "mike"
     4) "91"
     5) "frank"
     6) "200"
     7) "martin"
     8) "250"
     9) "tom"
    10) "259"
    127.0.0.1:6379>
    
    -- 返回指定分数范围的成员(+inf代表无限大,-inf代表无限小)
    zrangebyscore key min max [WITHSCORES] [LIMIT offset count]
    zrevrangebyscore key max min [WITHSCORES] [LIMIT offset count]
    
    127.0.0.1:6379> zrangebyscore user:ranking 100 250
    1) "frank"
    2) "martin"
    127.0.0.1:6379>
    
    127.0.0.1:6379> zrevrangebyscore user:ranking 300 20
    1) "tom"
    2) "martin"
    3) "frank"
    4) "mike"
    127.0.0.1:6379> zrevrangebyscore user:ranking 300 20 withscores
    1) "tom"
    2) "259"
    3) "martin"
    4) "250"
    5) "frank"
    6) "200"
    7) "mike"
    8) "91"
    127.0.0.1:6379>
    127.0.0.1:6379> zrangebyscore user:ranking 100 +inf
    1) "frank"
    2) "martin"
    3) "tom"
    127.0.0.1:6379>
    
    -- zcount key min max : 返回指定分数范围成员个数
    127.0.0.1:6379> zcount user:ranking 100 200
    (integer) 1
    127.0.0.1:6379> 
    
    -- zremrangebyrank key start stop : 删除指定排名内升序元素
    -- zremrangebyscore key min max : 删除指定分数范围的成员
    
    
    [2] 集合间运算
    zadd user:ranking:1 1 kris 91 mike 200 frank 220 tim  250 martin 251 tom
    zadd user:ranking:2 8 james 77 mike 625 martin 888 tom
    
    -- 交集: zinterstore destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
    - numkeys : 需要计算键的个数
    - weitht: 每个键的权重,在集合运算时,每个成员将自己的分值乘以这个权重
    - aggregate: 集合运算后,分值汇总函数,默认求和
    
    127.0.0.1:6379> zinterstore user:ranking:1_inter_2 2 user:ranking:1 user:ranking:2
    (integer) 3
    127.0.0.1:6379> zrange user:ranking:1_inter_2 0 -1 withscores
    1) "mike"
    2) "168"
    3) "martin"
    4) "875"
    5) "tom"
    6) "1139"
    127.0.0.1:6379>
    
    127.0.0.1:6379> zinterstore user:ranking:1_inter_2 2 user:ranking:1 user:ranking:2 weights 1 0.5 aggregate max
    (integer) 3
    127.0.0.1:6379> zrange user:ranking:1_inter_2 0 -1 withscores
    1) "mike"
    2) "91"
    3) "martin"
    4) "312.5"
    5) "tom"
    6) "444"
    127.0.0.1:6379>
    
    -- 并集: zunionstore destination numkeys key [key ...] [weights weight [weight ...]] [aggregate sum|min|max]
    127.0.0.1:6379> zunionstore user:ranking:1_union_2 2 user:ranking:1 user:ranking:2
    (integer) 7
    127.0.0.1:6379> zrange user:ranking:1_union_2 0 -1 withscores
     1) "kris"
     2) "1"
     3) "james"
     4) "8"
     5) "mike"
     6) "168"
     7) "frank"
     8) "200"
     9) "tim"
    10) "220"
    11) "martin"
    12) "875"
    13) "tom"
    14) "1139"
    127.0.0.1:6379>
    
    [3] 时间复杂度

    img

    4.1.7 发布订阅

    Publisher可以向不同的Channel发布消息,Subscriber接收订阅频道的消息。

    命令 描述
    PUBLISH channel msg 将信息 message 发送到指定的频道 channel
    SUBSCRIBE channel [channel ...] 订阅频道,可以同时订阅多个频道
    UNSUBSCRIBE [channel ...] 取消订阅指定的频道, 如果不指定频道,则会取消订阅所有频道
    PSUBSCRIBE pattern [pattern ...] 订阅一个或多个符合给定模式的频道,每个模式以 * 作为匹配符,比如 it* 匹配所有以 it 开头的频道( it.news 、 it.blog 、 it.tweets 等等), news.* 匹配所有以 news. 开头的频道( news.it 、 news.global.today 等等),诸如此类
    PUNSUBSCRIBE [pattern [pattern ...]] 退订指定的规则, 如果没有参数则会退订所有规则
    PUBSUB subcommand [argument [argument ...]] 查看订阅与发布系统状态

    注意:使用发布订阅模式实现的消息队列,当有客户端订阅channel后只能收到后续发布到该频道的消息,之前发送的不会缓存,必须Provider和Consumer同时在线。

    4.1.8 客户端管理

    [1] client

    语法

    127.0.0.1:6379> client help
     1) CLIENT <subcommand> arg arg ... arg. Subcommands are:
     2) id                     -- Return the ID of the current connection.
     3) getname                -- Return the name of the current connection.
     4) kill <ip:port>         -- Kill connection made from <ip:port>.
     5) kill <option> <value> [option value ...] -- Kill connections. Options are:
     6)      addr <ip:port>                      -- Kill connection made from <ip:port>
     7)      type (normal|master|replica|pubsub) -- Kill connections by type.
     8)      skipme (yes|no)   -- Skip killing current connection (default: yes).
     9) list [options ...]     -- Return information about client connections. Options:
    10)      type (normal|master|replica|pubsub) -- Return clients of specified type.
    11) pause <timeout>        -- Suspend all Redis clients for <timout> milliseconds.
    12) reply (on|off|skip)    -- Control the replies sent to the current connection.
    13) setname <name>         -- Assign the name <name> to the current connection.
    14) unblock <clientid> [TIMEOUT|ERROR] -- Unblock the specified blocked client.
    127.0.0.1:6379>
    

    实例

    127.0.0.1:6379> client list
    id=38 addr=127.0.0.1:11614 fd=9 name= age=89171 idle=58113 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=NULL
    id=41 addr=127.0.0.1:64820 fd=10 name= age=4681 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=32742 obl=0 oll=0 omem=0 events=r cmd=client
    id=42 addr=127.0.0.1:65108 fd=8 name= age=4501 idle=2592 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=georadiusbymember
    id=43 addr=127.0.0.1:15293 fd=11 name= age=324 idle=324 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=NULL
    id=46 addr=127.0.0.1:15495 fd=12 name= age=200 idle=143 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=get
    127.0.0.1:6379>
    
    # 说明
    -- 1. 标识: id、 addr、 fd、 name
    - id:   客户端连接的唯一标识, 这个id是随着Redis的连接自增的, 重启Redis后会重置为0。
    - addr: 客户端连接的ip和端口
    - fd:   socket的文件描述符, 与lsof命令结果中的fd是同一个, 如果fd=-1代表当前客户端不是外部客户端, 而是Redis内部的伪装客户端
    - name: 客户端的名字, 后面的client setName和client getName两个命令会对其进行说明
    
    -- 2. 输入缓冲区: qbuf、 qbuf-free
    - qbuf :     输入缓冲区的总容量
    - qbuf-free: 输入缓冲区剩余容量
    -- 输入缓冲区使用不当会产生下面2个问题:
    -- 1. 一旦某个客户端的输入缓冲区超过1G, 客户端将会被关闭
    -- 2. 输入缓冲区不受maxmemory控制, 假设一个Redis实例设置了maxmemory为4G, 已经存储了2G数据, 但是如果此时输入缓冲区使用了3G, 已经超过maxmemory限制, 可能会产生数据丢失、 键值淘汰、 OOM等情况
    
    -- 3. 输出缓冲区: obl、 oll、 omem  # 输出缓冲区的容量可以通过参数client-outputbuffer-limit来进行设置
    - obl:  代表固定缓冲区的长度
    - oll:  动态缓冲区列表的长度
    - omem: 代表使用的字节数
    
    -- 调整输出缓冲区 : client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>  
    127.0.0.1:6379> config get client-output-buffer-limit
    1) "client-output-buffer-limit"
    2) "normal 0 0 0 slave 268435456 67108864 60 pubsub 33554432 8388608 60"
    127.0.0.1:6379> config set client-output-buffer-limit 'normal 20mb 10mb 120'
    OK
    127.0.0.1:6379> config get client-output-buffer-limit
    1) "client-output-buffer-limit"
    2) "normal 20971520 10485760 120 slave 268435456 67108864 60 pubsub 33554432 8388608 60"
    127.0.0.1:6379>
    
    - class: 客户端类型, 分为三种。  normal: 普通客户端; | slave: slave客户端, 用于复制; | pubsub: 发布订阅客户端
    - hard limit: 如果客户端使用的输出缓冲区大于<hard limit>, 客户端会被立即关闭
    - <soft limit>和<soft seconds>: 如果客户端使用的输出缓冲区超过了<softlimit>并且持续了<soft limit>秒, 客户端会被立即关闭
    
    -- 默认配置
    $ grep client-output redis.conf |grep -v "^#"
    client-output-buffer-limit normal 0 0 0
    client-output-buffer-limit replica 256mb 64mb 60
    client-output-buffer-limit pubsub 32mb 8mb 60
    
    -- 4. 客户端的存活状态
    - age:  当前客户端已经连接的时间
    - idle: 最近一次的空闲时间
    
    -- 5. 客户端类型 flag
    - flag: 用于标识当前客户端的类型
    /*
    flag=N 代表当前是普通客户端
    flag=M 代表当前客户端是master客户端
    flag=S 代表当前客户端是slave客户端
    flag=O 代表当前客户端正在执行monitor命令
    flag=x 代表当前客户端正在执行事务
    flag=b 代表当前客户端正在等待阻塞事件
    flag=i 代表当前客户端正在等待VM I/O
    flag=d 代表一个受监视的键已被删除,EXEC命令将失败
    flag=u 代表当前客户端未被阻塞
    flag=c 代表恢复完整输出后,关闭连接
    flag=A 代表尽可能快关闭连接
    **/
    
    -- 6. 
    - db: 当前客户端正在使用的数据库索引下标
    - events: 文件描述符事件(r/w):r和w分别表示客户端套接字可读和可写。
    - cmd: 当前客户端最后一次执行的命令,不包含参数
    
    -- 7. 客户端的限制maxclients和timeout
    127.0.0.1:6379> config get maxclients
    1) "maxclients"
    2) "10000"
    127.0.0.1:6379>
    127.0.0.1:6379> config get timeout
    1) "timeout"
    2) "0"
    127.0.0.1:6379>
    -- 设置超时限制,默认为0,即不检测空闲状态
    127.0.0.1:6379> config set timeout 30
    OK
    127.0.0.1:6379> config rewrite
    OK
    127.0.0.1:6379> 
    
    -- monitor命令用于监控Redis正在执行的命令
    
    
    [2] info
    127.0.0.1:6379> info clients
    # Clients
    connected_clients:1
    client_recent_max_input_buffer:2
    client_recent_max_output_buffer:0
    blocked_clients:0
    127.0.0.1:6379>
    
    127.0.0.1:6379> info stats
    # Stats
    total_connections_received:50
    total_commands_processed:415396
    instantaneous_ops_per_sec:0
    total_net_input_bytes:13036266
    total_net_output_bytes:1509467
    instantaneous_input_kbps:0.00
    instantaneous_output_kbps:0.00
    rejected_connections:0
    sync_full:0
    sync_partial_ok:0
    sync_partial_err:0
    expired_keys:2
    expired_stale_perc:0.00
    expired_time_cap_reached_count:0
    evicted_keys:0
    keyspace_hits:314
    keyspace_misses:137871
    pubsub_channels:0
    pubsub_patterns:0
    latest_fork_usec:258
    migrate_cached_sockets:0
    slave_expires_tracked_keys:0
    active_defrag_hits:0
    active_defrag_misses:0
    active_defrag_key_hits:0
    active_defrag_key_misses:0
    127.0.0.1:6379> 
    
    参数说明:
    ·total_connections_received: Redis自启动以来处理的客户端连接数总数。
    ·rejected_connections: Redis自启动以来拒绝的客户端连接数, 需要重点监控
    
    [3] 客户端参数配置
    timeout: 检测客户端空闲连接的超时时间, 一旦idle时间达到了timeout, 客户端将会被关闭, 如果设置为0就不进行检测
    maxclients: 客户端最大连接数
    tcp-keepalive: 检测TCP连接活性的周期, 默认值为0, 也就是不进行检测, 如果需要设置, 建议为60, 那么Redis会每隔60秒对它创建的TCP连接进行活性检测, 防止大量死连接占用系统资源
    tcp-backlog: TCP三次握手后, 会将接受的连接放入队列中, tcpbacklog就是队列的大小, 它在Redis中的默认值是511。
    这个参数会受到操作系统的影响, 例如在Linux操作系统中, 如果/proc/sys/net/core/somaxconn小于tcp-backlog,建议将/proc/sys/net/core/somaxconn设置更大
    
    [4] 对比info和client
    命令 优点 缺点
    client list 能定位每个客户端来定位问题 执行速度慢,频繁执行存在阻塞Redis的可能
    info clients 执行过程比client快,过程简单 不能精确定位
    不能显示所有输入缓冲区的总量,只能显示最大值

    4.1.9 事务

    Redis支持是弱事务。

    事务命令
    命令 描述
    DISCARD 取消事务,放弃执行事务块内的所有命令。
    EXEC 执行所有事务块内的命令。
    MULTI 标记一个事务块的开始。
    UNWATCH 取消 WATCH 命令对所有 key 的监视。
    WATCH key [key ...] 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。
    示例
    ZADD salary 2000 user1
    ZADD salary 3000 user2
    ZRANGE salary 0 -1 WITHSCORES
    MULTI
    ZINCRBY salary 1000 user1
    ZINCRBY salary -1000 user2
    EXEC
    

    4.2 python API

    4.2.1 字符串API

    #!/usr/bin/env python
    # -*- ecoding: utf-8 -*-
    
    import redis
    # 创建redis数据库连接对象
    conn = redis.Redis(host='192.168.10.162', port=6379)
    
    ''' 1. 字符串自增自减 '''
    
    # 尝试获取一个不存在的键(key2)
    print(conn.get('key2'))
    # 将键(key2)存储的值加1,并以字符串的方式返回整数
    print(conn.incr('key2'))
    # 将键(key2)存储的值加15,并以字符串的方式返回整数
    print(conn.incr('key2', 15))
    # 将键(key2)存储的值减5,并以字符串的方式返回整数
    print(conn.decr('key2', 5))
    
    ''' 2. 字符串字串操作和二进制位操作 '''
    
    # 将值value追加到给定键当前存储的值末尾,并返回当前字符串长度
    # append key-name value
    print(conn.append('new-key2', 'hello '))
    print(conn.get('new-key2'))
    
    print(conn.append('new-key2', 'world!'))
    print(conn.get('new-key2'))
    
    # 获取一个包含start至end偏移量范围的所有字符组成的子串(字符串下标从0开始)
    # getrange key-name start end
    print(conn.getrange('new-key2', 0, 4))
    
    # 将从start偏移量开始的字串设置为给定值
    # setrange key-name start-offset value
    print(conn.setrange('new-key2', 0, 'H'))
    print(conn.setrange('new-key2', 6, 'W'))
    
    print(conn.get('new-key2'))
    
    print(conn.setrange('new-key2', 11, ', how are you?'))
    print(conn.get('new-key2'))
    
    
    # 将字符串看作二进制位串,并将二进制位串中偏移量为offset的二进制位的值设置位value
    # setbit key-name offset value
    print(conn.setbit('other-key2', 2, 1))
    print(conn.setbit('other-key2', 7, 1))
    
    print(conn.get('other-key2'))
    
    # 将字符串看作二进制位串,并返回给定偏移量的二进制位的值
    print(conn.getbit('other-key2', 2))
    
    # 统计二进制位串中值为1的数量
    # bitcount key-name [start end]
    print(conn.bitcount('other-key2'))
    
    print(conn.bitcount('other-key2', 1, 10))
    
    # 并(and)、或(OR)、异或(XOR)、非(NOT)的按位运算操作,并将结果保存在dest-key键
    # bitop operation dest-key key-name [key-name ...]
    print(conn.bitop('XOR', 'result-key', 'other-key2'))
    

    4.2.2 列表API

    #!/usr/bin/env python
    # -*- ecoding: utf-8 -*-
    import redis
    # 创建redis的连接对象
    conn = redis.Redis(host='192.168.10.162', port=6379)
    
    """
        1. 列表常用命令
    """
    # 向列表右侧插入元素,并返回列表元素个数
    # rpush key-name value
    print(conn.rpush('list-key', 'last'))
    # 向列表左侧插入元素,并返回列表元素个数
    # lpush key-name value
    print(conn.lpush('list-key', 'first'))
    
    print(conn.rpush('list-key', 'new last'))
    
    # 将列表左侧第一个元素弹出
    # lpop key-name
    print(conn.lpop('list-key'))
    
    # 将列表右侧第一个元素弹出
    # rpop key-name
    print(conn.rpop('list-key'))
    
    # 返回指定范围列表元素
    print(conn.lrange('list-key', 0, -1))
    # 删减列表元素,并返回命令执行结果True/False
    print(conn.ltrim('list-key', 0, 0))
    print(conn.lrange('list-key', 0, -1))
    
    # 返回指定下标的列表元素
    # lindex key-name offset
    print(conn.lindex('list-key', 0))
    
    ''' 2. 列表间运算 '''
    # 2个列表中插入元素
    print(conn.rpush('list1', 'item1'))
    print(conn.rpush('list1', 'item2'))
    
    print(conn.rpush('list2', 'item3'))
    
    print(conn.lrange('list1', 0, -1))
    print(conn.lrange('list2', 0, -1))
    
    # brpoplpush source-key dest-key timeout
    # 将source-key最右端元素弹出,然后插入到dest-key最左端,并返回该元素
    print('------- brpoplpush list1 list2 1 ------- ')
    print(conn.brpoplpush('list1', 'list2', 1))
    print(conn.lrange('list1', 0, -1))
    print(conn.lrange('list2', 0, -1))
    print('------- brpoplpush list2 list1 1 ------- ')
    print(conn.brpoplpush('list2', 'list1', 1))
    print(conn.lrange('list2', 0, -1))
    print(conn.lrange('list1', 0, -1))
    
    # blpop key-name [key-name ...] timeout
    # 在第一个非空列表中弹出最左元素或在timeout秒内阻塞并等待可弹出元素
    print('------- blpop list1 ------- ')
    print(conn.blpop('list1', 1))
    print(conn.lrange('list1', 0, -1))
    print(conn.lrange('list2', 0, -1))
    print('------- blpop list1 list2 ------- ')
    print(conn.blpop(['list1','list2'], 1))
    print(conn.lrange('list1', 0, -1))
    print(conn.lrange('list2', 0, -1))
    
    # brpop key-name [key-name ...] timeout
    # 在第一个非空列表中弹出最右元素或在timeout秒内阻塞并等待可弹出元素
    print('------- brpop list2 ------- ')
    print(conn.brpop('list1', 1))
    print(conn.lrange('list1', 0, -1))
    
    # rpoplpush source-key dest-key
    # 弹出source-key最右端元素并插入到dest-key的最左端,并返回该元素
    print('------- rpoplpush list2 list1 ------- ')
    print(conn.rpoplpush('list2', 'list1'))
    print(conn.lrange('list2', 0, -1))
    print(conn.lrange('list1', 0, -1))
    

    4.2.3 HASH API

    #!/usr/bin/env python
    # -*- ecoding: utf-8 -*-
    import redis
    # 创建redis的连接对象
    conn = redis.Redis(host='192.168.10.162', port=6379)
    
    # hmset key-name key value [key value ...]
    # 为散列一个或多个键设置值
    
    print(conn.hmset('hm1-key', {'k1':'v1', 'k2': 'v2'}))
    print(conn.hmset('hm2-key', {'k1':'v1', 'k2': 'v2', 'k3': 'v3'}))
    
    # hmget key-name key [key] 获取一个或多个键的值
    print(conn.hmget('hm1-key', 'k1','k2') )
    
    # hdel key-name key [key...] 删除散列中一个或多个键值对,并返回删除键值对的数量
    print(conn.hdel('hm2-key', 'k1', 'k2'))
    
    # hlen key-name 返回散列中键值对的数量
    print(conn.hlen('hm1-key'),conn.hlen('hm2-key'))
    
    # hmset key-name key value [key value ...]
    # 为散列一个或多个键设置值
    
    print(conn.hmset('hm1-key', {'k1':'v1', 'k2': 'v2'}))
    print(conn.hmset('hm2-key', {'k1':'v1', 'k2': 'v2', 'k3': 'v3'}))
    
    # hmget key-name key [key] 获取一个或多个键的值
    print(conn.hmget('hm1-key', 'k1','k2'))
    """
    # hdel key-name key [key...] 删除散列中一个或多个键值对,并返回删除键值对的数量
    print(conn.hdel('hm2-key', 'k1', 'k2'))
    
    # hlen key-name 返回散列中键值对的数量
    print(conn.hlen('hm1-key'),conn.hlen('hm2-key'))
    """
    
    # hexists key-name key 检查给定键是否存在散列中
    print(conn.hexists('hm1-key', 'k3'))
    print(conn.hexists('hm2-key', 'k3'))
    
    # hkeys key-name 获取散列所有键
    print(conn.hkeys('hm1-key'))
    print(conn.hkeys('hm2-key'))
    
    # hvals key-name 获取散列包含的所有值
    print(conn.hvals('hm1-key'))
    print(conn.hvals('hm2-key'))
    
    # hgetall key-name 获取散列所有的键值对
    print(conn.hgetall('hm1-key'))
    print(conn.hgetall('hm2-key'))
    
    # hincrby key-name key increment 将键存储的值加上 increment
    print(conn.hincrby('hm2-key', 10))
    print(conn.hgetall('hm2-key'))
    
    # hincrbyfloat key-name key increment 将键存储的值加上浮点数increment
    print(conn.hincrby('hm2-key', 10.1))
    print(conn.hgetall('hm2-key'))
    
    

    4.2.4 集合API

    #!/usr/bin/env python
    # -*- ecoding: utf-8 -*-
    import redis
    # 创建redis的连接对象
    conn = redis.Redis(host='192.168.10.162', port=6379)
    
    """
        1. 集合常用命令
    """
    
    # sadd key-name item [item ...]
    # 将一个或多个元素添加到集合,并返回被添加元素且不存在原集合里面的元素数量
    
    print(conn.sadd('set-key', 'item1', 'item2'))
    # result := 2
    print(conn.sadd('set-key', 'item1', 'item2', 'item3','item4'))
    # result := 2
    
    # scard key-name 返回集合元素的数量
    # smembers key-name 返回集合的所有元素
    print(conn.scard('set-key'),conn.smembers('set-key'))
    
    # srem key-name item [item ...] 从集合中移除一个或多个元素,并返回被移除元素的数量
    print(conn.srem('set-key', 'item1', 'item1'))
    
    print(conn.smembers('set-key'))
    
    # sismember key-name item 检查元素item是否存在于集合里面
    print(conn.sismember('set-key', 'item1'))
    print(conn.sismember('set-key', 'item2'))
    
    # srandmember key-name [count] 从集合里随机返回一个或多个元素
    # 当count为正数时,返回不重复随机元素,当count为负数时,返回随机元素可能出现重复
    print(' ------ srandmember set-key -------- ')
    print(conn.srandmember('set-key', 2))
    print(conn.srandmember('set-key', 2))
    print(conn.srandmember('set-key', -2))
    print(conn.srandmember('set-key', -2))
    
    
    # spop key-name 从集合中随机移除一个元素,并返回被移除的元素
    print(conn.spop('set-key'))
    
    # smove source-key dest-key item
    # 从source-key中移除item,并将元素item添加到dest-key集合,如果成功移除item,则返回true,否则返回失败
    print(conn.sadd('set1-key', 'item1', 'item2'))
    # result := 2
    print(conn.sadd('set2-key', 'item3','item4'))
    print(conn.smove('set1-key', 'set2-key', 'item'))
    print(conn.smove('set1-key', 'set2-key', 'item2'))
    
    print(conn.smembers('set1-key'),conn.smembers('set2-key'))
    
    """
        2. 集合组合命令
    """
    
    print(conn.sadd('set1-key', 'item1', 'item2'))
    print(conn.sadd('set2-key', 'item2', 'item3', 'item4'))
    print(conn.sadd('set3-key', 'item3', 'item4', 'item5'))
    print(conn.smembers('set1-key'),conn.smembers('set2-key'),conn.smembers('set3-key'))
    
    # sdiff key-name [key-name ...] 返回存在于第一个集合但不存在其他集合中的元素
    print(conn.sdiff('set1-key'))
    print(conn.sdiff('set1-key', 'set2-key'))
    print(conn.sdiff('set1-key', 'set2-key', 'set3-key'))
    
    
    # sdiffstore dest-key key-name [key-name ...] 将存在于第一个集合但不存在其他集合中的元素存储到dest-key中,并返回数量
    print(conn.sdiffstore('set-key', 'set1-key', 'set2-key'))
    print(conn.smembers('set-key'))
    
    # sinter key-name [key-name ...] 返回同时存在于所有集合的元素
    print(conn.sinter('set2-key', 'set3-key'))
    
    # sinterstore dest-name key-name [key-name ...] 将同时存在于所有集合的元素存储到dest-name中
    print(conn.sinterstore('set-key', 'set2-key', 'set3-key'))
    print(conn.smembers('set-key'))
    
    # sunion key-name [key-name ...] 返回至少存在于一个集合中的元素(并集)
    print(conn.sunion('set1-key', 'set2-key'))
    
    # sunionstore dest-name key-name [key-name ...] 将至少存在于一个集合中的元素存储到dest-name,并返回其数量
    print(conn.sunionstore('set-key', 'set2-key', 'set3-key'))
    print(conn.smembers('set-key'))
    

    4.2.5 有序集合API

    #!/usr/bin/env python
    # -*- ecoding: utf-8 -*-
    import redis
    # 创建redis的连接对象
    conn = redis.Redis(host='192.168.10.162', port=6379)
    
    # zadd key-name score member [score member ...] 将给定分值的成员添加到有序集合,并返回其数量
    print(conn.zadd('zset-key', {'a': 3, 'b': 2, 'c': 1}))
    
    # zrange key-name start stop [withscores]
    # 返回排名介于start和stop之间的成员,如果有可选项withscores,则将成员分值也一起返回
    print(conn.zrange('zset-key', 0, -1, withscores=True))
    
    # zcard key-name 返回有序集合成员数量
    print(conn.zcard('zset-key'))
    
    # zcount key-name min max 返回分值介于min 和 max的成员数量
    print(conn.zcount('zset-key', 1, 2))
    
    # zincrby key-name increment member 将member成员的分值加上increment
    print(conn.zincrby('zset-key',20, 'c'))
    print(conn.zrange('zset-key', 0, -1, withscores=True))
    
    # zrank key-name member 返回成员member在有序集合中的排名
    print(conn.zrank('zset-key', 'a'))
    
    # zscore key-name member 返回成员member的分值
    print(conn.zscore('zset-key', 'a'))
    print(conn.zrange('zset-key', 0, -1, withscores=True))
    
    # zrem key-name member [member ...] 在有序集合中移除给定的成员并返回被移除成员数量
    print(conn.zrem('zset-key', 'c', 'b'))
    print(conn.zrange('zset-key', 0, -1, withscores=True))
    
    # zadd key-name score member [score member ...] 将给定分值的成员添加到有序集合,并返回其数量
    print(conn.zadd('zset-key', {'a': 3, 'b': 2, 'c': 1, 'd':10}))
    
    # zrange key-name start stop [withscores]
    # 返回排名介于start和stop之间的成员,如果有可选项withscores,则将成员分值也一起返回
    print(conn.zrange('zset-key', 0, -1, withscores=True))
    
    '''
       2. 有序集合中范围型数据获取及操作命令
    '''
    
    # zrevrange key-name start stop [withscores]
    # 返回给定排名范围内成员,按照分值大到小排列
    print(conn.zrevrange('zset-key', 0, -1))
    print(conn.zrevrange('zset-key', 0, -1, withscores=True))
    
    # zrevrank key-name member 返回有序集合成员member的排名,按照分值大到小排序
    print(conn.zrevrank('zset-key', 'c'))
    
    # zrangebyscore key min max [withscores] [limit offset count]
    # 返回集合中分值介于min与max间的成员,按照分值从小到大返回(待确认返回顺序)
    print(conn.zrangebyscore('zset-key', 0, 2, withscores=True))
    
    # zremrangebyrank key-name start stop
    # 移除有序集合中排名介于start和stop间的成员,并返回移除成员数量
    print(conn.zremrangebyrank('zset-key', 4, 10))
    print(conn.zrevrange('zset-key', 0, -1, withscores=True))
    
    # zremrangebyscore key-name min max
    # 移除分值介于min和max间的成员,并返回移除成员数量
    print(conn.zremrangebyscore('zset-key', 10, 100))
    print(conn.zrevrange('zset-key', 0, -1, withscores=True))
    
    # zinterstore dest-key key-count key [key ...] [weights weight [weight ...]] [aggregate sum|min|max]
    # 对给定集合执行交集运算,按照分值从大到小返回
    conn.zadd('zset-1', {'a': 1, 'b': 2, 'c': 3})
    conn.zadd('zset-2', {'b': 4, 'c': 1, 'd': 0})
    
    # aggregate默认值为求和sum
    print(conn.zinterstore('zset-i', ['zset-1', 'zset-2']))
    print(conn.zrevrange('zset-i', 0, -1, withscores=True))
    print(conn.zinterstore('zset-j', ['zset-1', 'zset-2'], aggregate='max'))
    print(conn.zrevrange('zset-j', 0, -1, withscores=True))
    
    # zunionstore dest-key key-count key [key ...] [weights weight [weight ...]] [aggregate sum|min|max]
    # 对给定集合执行并集运算,按照分值从大到小返回
    print(conn.zunionstore('zset-m', ['zset-1', 'zset-2'], aggregate='sum'))
    print(conn.zrevrange('zset-m', 0, -1, withscores=True))
    print(conn.zunionstore('zset-n', ['zset-1', 'zset-2'], aggregate='max'))
    print(conn.zrevrange('zset-n', 0, -1, withscores=True))
    

    4.2.6 发布订阅API

    #!/usr/bin/env python
    # -*- ecoding: utf-8 -*-
    import redis
    import threading
    import time
    
    # 发布消息通道
    def publisher(n):
        time.sleep(1)
        # 在channel上定期(每隔1秒)发布一条消息
        for i in xrange(n):
            conn.publish('channel', i)
            time.sleep(1)
    
    # 订阅
    def run_pubsub():
        # 启动发布线程,并发送3条消息
        threading.Thread(target=publisher, args=(3,)).start()
        # 创建发布订阅对象
        pubsub = conn.pubsub()
        # 订阅给定的频道
        pubsub.subscribe(['channel'])
        #
        count = 0
        # 遍历函数来监听订阅消息
        for item in pubsub.listen():
            # 打印输出接收到的每条消息
            print item
            count += 1
            if count == 4:
                # 在接收到一条订阅反馈消息和3条发布者发送的消息后,执行退订操作并停止监听
                pubsub.unsubscribe()
            # 客户端接收到退订反馈信息后不再接收消息
            if count == 5:
                break
    
    if __name__ == "__main__":
        # 创建redis的连接对象
        conn = redis.Redis(host='192.168.10.162', port=6379)
        # 调用函数
        run_pubsub()
    

    4.2.7 处理过期时间

    #!/usr/bin/env python
    # -*- ecoding: utf-8 -*-
    import redis
    #  创建redis的连接对象
    conn = redis.Redis(host='192.168.10.162', port=6379)
    
    conn.set('hello', 'world!')
    print(conn.get('hello'))
    
    # expire key-name seconds 让给定的键在指定的秒数后过期
    conn.expire('hello', 10)
    
    # ttl key-name 查看指定键距离过期时间的秒数
    print(conn.ttl('hello'))
    
    # pttl key-name 查看指定键距离过期时间的毫秒数
    print(conn.pttl('hello'))
    
    # persist key-name 移除键的过期时间
    conn.persist('hello')
    print(conn.ttl('hello'))
    
    # expireat key-name timestamp 将给定键过期时间设置为给定unix时间戳
    # unix 使用具体时间换算成时间戳: echo $(date --date="2018-12-12 01:00:00" +%s)
    # 时间戳换算具体时间: echo $(date -d @1544547600)
    conn.expireat('hello' ,'1544547600')
    print(conn.pttl('hello'))
    
    # pexpire key-name milliseconds 让给定的键在指定的毫秒数后过期
    conn.pexpire('hello', 100)
    print(conn.pttl('hello'))
    
    # expireat key-name timestamp-milliseconds 将给定键过期时间设置为给定毫秒级精度unix时间戳
    conn.expireat('hello' ,'1544547600')
    print(conn.pttl('hello'))
    

    5. 高可用

    5.1 复制(replication)[master-slave]

    主节点负责写数据,从节点负责读数据,主节点定期把数据同步到从节点保证数据一致性。

    5.1.1 主从复制流程

    img

    1)保存主节点信息

    -- 从节点执行slaveof后只保存主节点的地址信息便直接返回, 这时建立复制流程还没有开始, 在从节点6380执行info replication可以看到如下信息
    master_host:127.0.0.1
    master_port:6379
    master_link_status:down
    
    -- redis日志打印如下信息:
    REPLICAOF 127.0.0.1:6379 enabled (user request from 'id=4 addr=127.0.0.1:41352 fd=9 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=42 qbuf-free=32726 obl=0 oll=0 omem=0 events=r cmd=slaveof')
    

    2)主从建立socket连接

    -- 从节点(slave) 内部通过每秒运行的定时任务维护复制相关逻辑,当定时任务发现存在新的主节点后, 会尝试与该节点建立网络连接
    
    -- redis日志打印如下信息:
    Connecting to MASTER 127.0.0.1:6379
    MASTER <-> REPLICA sync started
    

    3)发送ping命令(心跳检测)

    连接建立成功后从节点发送ping请求进行首次通信,发PING命令用于检测主从之间网络套接字是否可用和检测主节点当前是否可接受处理命令。
    从节点没有收到主节点的pong回复或者超时, 比如网络超时或者主节点正在阻塞无法响应命令, 从节点会断开复制连接, 下次定时任务会发起重连。
    
    -- redis日志打印如下信息:
    Master replied to PING, replication can continue
    

    4)权限验证

    ​ 如果主节点设置了requirepass参数, 则需要密码验证,从节点必须配置masterauth参数保证与主节点相同的密码才能通过验证; 如果验证失败复制将终止, 从节点重新发起复制流程。

    5)同步数据集(RDB)

    ​ 主从复制连接正常通信后, 对于首次建立复制的场景, 主节点调用BGsave命令,并将它发送给从节点。把持有的数据全部发送给从节点, 这部分操作是耗时最长的步骤。

    -- redis日志打印如下信息:
    Trying a partial resynchronization (request 475f32d8676d4d9e58cc4c29cce2503b78ff966c:997).
    Full resync from master: 913a1700ed78f16e5bdfef2e46726b49f0b3432c:996
    

    6)命令持续复制(command propagate)

    ​ 当主节点把当前的数据同步给从节点后, 便完成了复制的建立流程。 接下来主节点会持续地把写命令发送给从节点, 保证主从数据一致性。

    5.1.2 主从复制部署

    [1] 配置方式
    # 1. 在配置文件中加入slaveof {masterHost} {masterPort}随Redis启动生效。
    cat >> 6380.conf <<EOF
    # slave settings
    slaveof 127.0.0.1 6379
    EOF
    
    # 2. 在redis-server启动命令后加入--slaveof{masterHost}{masterPort}生效。
    ./bin/redis-server 6380.conf --slaveof 127.0.0.1 6379 &
    
    # 3. 直接使用命令: slaveof {masterHost} {masterPort}生效。
    ./bin/redis-server 6380.conf &
    ./bin/redis-cli -p 6380
    127.0.0.1:6380> slaveof 127.0.0.1 6379
    

    提高数据安全性参数

    min-slaves-to-write <number of slaves>
    min-slaves-max-lag <number of seconds>
    
    [2] 检查状态
    -- master
    127.0.0.1:6379> info replication
    # Replication
    role:master
    connected_slaves:1
    slave0:ip=192.168.10.181,port=6380,state=online,offset=688,lag=0
    master_replid:913a1700ed78f16e5bdfef2e46726b49f0b3432c
    master_replid2:0000000000000000000000000000000000000000
    master_repl_offset:688
    second_repl_offset:-1
    repl_backlog_active:1
    repl_backlog_size:1048576
    repl_backlog_first_byte_offset:1
    repl_backlog_histlen:688
    127.0.0.1:6379> 
    
    -- slave
    127.0.0.1:6380> info replication
    # Replication
    role:slave
    master_host:127.0.0.1
    master_port:6379
    master_link_status:up
    master_last_io_seconds_ago:0
    master_sync_in_progress:0
    slave_repl_offset:702
    slave_priority:100
    slave_read_only:1
    connected_slaves:0
    master_replid:913a1700ed78f16e5bdfef2e46726b49f0b3432c
    master_replid2:0000000000000000000000000000000000000000
    master_repl_offset:702
    second_repl_offset:-1
    repl_backlog_active:1
    repl_backlog_size:1048576
    repl_backlog_first_byte_offset:225
    repl_backlog_histlen:478
    127.0.0.1:6380>
    
    [3] 断开复制

    断开复制主要流程:

    • 断开与主节点复制关系。
    • 从节点晋升为主节点。

    从节点断开复制后并不会抛弃原有数据, 只是无法再获取主节点上的数据变化

    -- slave 
    127.0.0.1:6380> slaveof no one
    OK
    127.0.0.1:6380> info replication
    # Replication
    role:master
    connected_slaves:0
    master_replid:475f32d8676d4d9e58cc4c29cce2503b78ff966c
    master_replid2:913a1700ed78f16e5bdfef2e46726b49f0b3432c
    master_repl_offset:996
    second_repl_offset:997
    repl_backlog_active:1
    repl_backlog_size:1048576
    repl_backlog_first_byte_offset:225
    repl_backlog_histlen:772
    127.0.0.1:6380> 
    
    
    [4] 切主

    切主指把当前从节点对主节点的复制切换到另一个主节点。

    切主操作流程如下:

    • 断开与旧主节点复制关系。
    • 与新主节点建立复制关系。
    • 删除从节点当前所有数据。
    • 对新主节点进行复制操作。
    slaveof {newMasterIP} {newMasterPort}
    
    -- slave 
    127.0.0.1:6380> slaveof 192.168.10.151 6379
    
    [5] 数据同步阶段

    redis2.8后使用psync命令完成主从数据同步(sync命令仍保留),同步过程分成: 全量复制和部分复制。

    - 1. 全量复制:一般用于初次复制场景, Redis早期(即2.8之前版本)支持的复制功能只有全量复制(sync), 它会把主节点全部数据一次性发送给从节点, 当数据量较大时, 会对主从节点和网络造成很大的开销
    - 2. 部分复制:用于处理在主从复制中因网络闪断等原因造成的数据丢失场景, 当从节点再次连上主节点后, 如果条件允许, 主节点会补发丢失数据给从节点。 
    因为补发的数据远远小于全量数据, 可以有效避免全量复制的过高开销。
    PSYNC 比起 SYNC 的最大改进在于 PSYNC 实现了部分重同步(partial resync)特性:在主从服务器断线并且重新连接的时候,只要条件允许,PSYNC 可以让主服务器只向从服务器同步断线期间缺失的数据,而不用重新向从服务器同步整个数据库。
    
    [6] 心跳检测

    主从心跳判断机制

    1) 主从节点彼此都有心跳检测机制, 各自模拟成对方的客户端进行通信, 通过client list命令查看复制相关客户端信息, 主节点的连接状态为flags=M, 从节点连接状态为flags=S。

    2) 主节点默认每隔10秒对从节点发送ping命令, 判断从节点的存活性和连接状态。 可通过参数repl-ping-slave-period控制发送频率。

    3) 从节点在主线程中每隔1秒发送replconf ack{offset}命令, 给主节点上报自身当前的复制偏移量。

    replconf命令主要作用如下:

    • 实时监测主从节点网络状态。

    • 上报自身复制偏移量, 检查复制数据是否丢失, 如果从节点数据丢失, 再从主节点的复制缓冲区中拉取丢失数据。

    • ·实现保证从节点的数量和延迟性功能, 通过min-slaves-to-write、 min-slaves-max-lag参数配置定义。

      ​ 主节点根据replconf命令判断从节点超时时间, 体现在info replication统计中的lag信息中,lag表示与从节点最后一次通信延迟的秒数, 正常延迟应该在0和1之间。 如果超过repl-timeout配置的值(默认60秒) , 则判定从节点下线并断开复制客户端连接。 即使主节点判定从节点下线后, 如果从节点重新恢复, 心跳检测会继续进行。

    img

    5.2 哨兵(sentinel)

    引入哨兵架构模式的自动故障恢复,解决上面主从架构中,当发生主节点宕机,只能通过手工切换的方式来恢复故障。Redis哨兵负责监控主节点的健康,当主节点不可用时,会自动选择一个从节点切换为主节点。

    功能

    • 监控:
    • 通知:
    • 自动故障转移:
    • 配置提供者:

    sentinel的3个定时任务

    1) 每隔10秒, 每个Sentinel节点会向主节点和从节点发送info命令获取最新的拓扑结构

    • 通过向主节点执行info命令, 获取从节点的信息, 这也是为什么Sentinel节点不需要显式配置监控从节点。
    • 当有新的从节点加入时都可以立刻感知出来。
    • 节点不可达或者故障转移后, 可以通过info命令实时更新节点拓扑信息。

    2)每隔2秒, 每个Sentinel节点会向Redis数据节点的__sentinel__: hello频道上发送该Sentinel节点对于主节点的判断以及当前Sentinel节点的信息
    同时每个Sentinel节点也会订阅该频道, 来了解其他Sentinel节点以及它们对主节点的判断, 所以这个定时任务可以完成以下两个工作:

    • 发现新的Sentinel节点: 通过订阅主节点的__sentinel__: hello了解其他的Sentinel节点信息, 如果是新加入的Sentinel节点, 将该Sentinel节点信息保存起来, 并与该Sentinel节点创建连接。
    • Sentinel节点之间交换主节点的状态, 作为后面客观下线以及领导者举的依据。

    3) 每隔1秒, 每个Sentinel节点会向主节点、 从节点、 其余Sentinel节点发送一条ping命令做一次心跳检测, 来确认这些节点当前是否可达。
    通过上面的定时任务, Sentinel节点对主节点、 从节点、 其余Sentinel节点都建立起连接, 实现了对每个节点的监控, 这个定时任务是节点失败判定的重要依据。

    主观下线和客观下线

    主观下线:
    每个Sentinel节点会每隔1秒(即上面的第3个定时任务)对主节点、 从节点、 其他Sentinel节点发送ping命令做心跳检测, 当这些节点超过down-after-milliseconds没有进行有效回复,
    Sentinel节点就会对该节点做失败判定, 这个行为叫做主观下线。

    客观下线:
    当Sentinel主观下线的节点是主节点时, 该Sentinel节点会通过sentinel ismaster-down-by-addr命令向其他Sentinel节点询问对主节点的判断, 当超过<quorum>个数,
    Sentinel节点认为主节点确实有问题, 这时该Sentinel节点会做出客观下线的决定, 这样客观下线的含义是比较明显了, 也就是大部分Sentinel节点都对主节点的下线做了同意的判定,
    那么这个判定就是客观的

    sentinel领导者选举

    Redis使用了Raft算法实现领导者选举。Raft 是能够实现分布式系统强一致性的算法,每个系统节点有三种状态 Follower(追随者),Candidate(候选人),Leader(领导者)。实现 Raft 算法两个最重要的事是:选主和复制日志。

    • 每个在线的Sentinel节点都有资格成为领导者, 当它确认主节点主观下线时候, 会向其他Sentinel节点发送sentinel is-master-down-by-addr命令,要求将自己设置为领导者。
    • 收到命令的Sentinel节点, 如果没有同意过其他Sentinel节点的sentinelis-master-down-by-addr命令, 将同意该请求, 否则拒绝。
    • 如果该Sentinel节点发现自己的票数已经大于等于max(quorum,num(sentinels) /2+1) , 那么它将成为领导者。
    • 如果此过程没有选举出领导者, 将进入下一次选举。

    ​ raft是一个共识算法(consensus algorithm),所谓共识,就是多个节点对某个事情达成一致的看法,即使是在部分节点故障、网络延时、网络分割的情况下。

    故障转移

    领导者选举出的Sentinel节点负责故障转移, 具体步骤如下:
    1) 在从节点列表中选出一个节点作为新的主节点, 选择方法如下:
    a) 过滤: “不健康”(主观下线、 断线) 、 5秒内没有回复过Sentinel节点ping响应、 与主节点失联超过down-after-milliseconds*10秒。
    b) 选择slave-priority(从节点优先级) 最高的从节点列表, 如果存在则返回, 不存在则继续。
    c) 选择复制偏移量最大的从节点(复制的最完整) , 如果存在则返回, 不存在则继续。
    d) 选择runid最小的从节点
    2) Sentinel领导者节点会对第一步选出来的从节点执行slaveof no one命令让其成为主节点。
    3) Sentinel领导者节点会向剩余的从节点发送命令, 让它们成为新主节点的从节点, 复制规则和parallel-syncs参数有关。
    4) Sentinel节点集合会将原来的主节点更新为从节点, 并保持着对其关注, 当其恢复后命令它去复制新的主节点。

    环境部署

    Sentinel集群可以监控多套不同的主从复制,即1对多关系。

    拓扑结构

    主要包括两部分:Redis Sentinel 集群和 Redis 数据集群

    img

    物理结构
    角色 IP port 别名
    master 127.0.0.1 6379
    slave-1 127.0.0.1 6380
    slave-2 127.0.0.1 6381
    sentinel-1 127.0.0.1 26379
    sentinel-2 127.0.0.1 26380
    sentinel-3 127.0.0.1 26381
    软件安装配置
    -- 1. master
    redis-server 6379.conf
    
    -- 2. slave
    cd /ups/app/redis5
    ./bin/redis-server 6380.conf
    ./bin/redis-server 6381.conf
    
    -- 3. 验证主从
    [root@progs redis5]# ps -ef|grep redis-server
    root      10045      1  0 Nov18 ?        00:09:15 /ups/app/redis5/bin/redis-server 192.168.10.181:6379
    root      27112      1  0 08:25 ?        00:00:00 ./bin/redis-server 192.168.10.181:6380
    root      27124      1  0 08:26 ?        00:00:00 ./bin/redis-server 192.168.10.181:6381
    root      27141  26762  0 08:26 pts/0    00:00:00 grep --color=auto redis-server
    
    [root@progs redis5]# rcli -p 6380 ping
    PONG
    [root@progs redis5]# rcli -p 6381 ping
    PONG
    
    -- master
    [root@progs redis5]# rcli -h 127.0.0.1 -p 6379 info replication
    # Replication
    role:master
    connected_slaves:2
    slave0:ip=192.168.10.181,port=6380,state=online,offset=122474,lag=1
    slave1:ip=192.168.10.181,port=6381,state=online,offset=122474,lag=0
    master_replid:913a1700ed78f16e5bdfef2e46726b49f0b3432c
    master_replid2:0000000000000000000000000000000000000000
    master_repl_offset:122474
    second_repl_offset:-1
    repl_backlog_active:1
    repl_backlog_size:1048576
    repl_backlog_first_byte_offset:1
    repl_backlog_histlen:122474
    [root@progs redis5]# 
    
    -- slave
    [root@progs redis5]# rcli -h 127.0.0.1 -p 6380 info replication
    # Replication
    role:slave
    master_host:127.0.0.1
    master_port:6379
    master_link_status:up
    master_last_io_seconds_ago:1
    master_sync_in_progress:0
    slave_repl_offset:122740
    slave_priority:100
    slave_read_only:1
    connected_slaves:0
    master_replid:913a1700ed78f16e5bdfef2e46726b49f0b3432c
    master_replid2:0000000000000000000000000000000000000000
    master_repl_offset:122740
    second_repl_offset:-1
    repl_backlog_active:1
    repl_backlog_size:1048576
    repl_backlog_first_byte_offset:122349
    repl_backlog_histlen:392
    [root@progs redis5]# 
    
    -- slave-2
    [root@progs redis5]# rcli -h 127.0.0.1 -p 6381 info replication
    # Replication
    role:slave
    master_host:127.0.0.1
    master_port:6379
    master_link_status:up
    master_last_io_seconds_ago:7
    master_sync_in_progress:0
    slave_repl_offset:122754
    slave_priority:100
    slave_read_only:1
    connected_slaves:0
    master_replid:913a1700ed78f16e5bdfef2e46726b49f0b3432c
    master_replid2:0000000000000000000000000000000000000000
    master_repl_offset:122754
    second_repl_offset:-1
    repl_backlog_active:1
    repl_backlog_size:1048576
    repl_backlog_first_byte_offset:122349
    repl_backlog_histlen:406
    
    -- 4. 部署sentinel
    -- sentinel-1
    [root@progs redis5]# grep -Ev "^#|^$" 26379.conf 
    port 26379
    daemonize yes
    pidfile /var/run/redis-sentinel-26379.pid
    logfile "/ups/app/redis5/logs/sentinel-26379.log"
    dir "/ups/data/redisdata"
    sentinel monitor mymaster 127.0.0.1 6379 2
    sentinel down-after-milliseconds mymaster 30000
    sentinel parallel-syncs mymaster 1
    sentinel failover-timeout mymaster 180000
    sentinel deny-scripts-reconfig yes
    [root@progs redis5]# 
    -- sentinel-2
    [root@progs redis5]# grep -Ev "^#|^$" 26380.conf
    port 26380
    daemonize yes
    pidfile /var/run/redis-sentinel-26380.pid
    logfile "/ups/app/redis5/logs/sentinel-26380.log"
    dir "/ups/data/redisdata"
    sentinel monitor mymaster 127.0.0.1 6379 2
    sentinel down-after-milliseconds mymaster 30000
    sentinel parallel-syncs mymaster 1
    sentinel failover-timeout mymaster 180000
    sentinel deny-scripts-reconfig yes
    
    -- sentinel-2
    [root@progs redis5]# grep -Ev "^#|^$" 26381.conf
    port 26381
    daemonize yes
    pidfile /var/run/redis-sentinel-26381.pid
    logfile "/ups/app/redis5/logs/sentinel-26381.log"
    dir "/ups/data/redisdata"
    sentinel monitor mymaster 127.0.0.1 6379 2
    sentinel down-after-milliseconds mymaster 30000
    sentinel parallel-syncs mymaster 1
    sentinel failover-timeout mymaster 180000
    sentinel deny-scripts-reconfig yes
    [root@progs redis5]# 
    
    -- 启动sentinel
    cd /ups/app/redis5
    ./bin/redis-sentinel 26379.conf
    ./bin/redis-sentinel 26380.conf
    ./bin/redis-sentinel 26381.conf
    or 
    redis-server 26379.conf --sentinel
    
    -- 5. 验证确认
    -- redis-cli -h 127.0.0.1 -p 26379 info Sentinel
    [root@progs redis5]# rcli -h 127.0.0.1 -p 26379 info Sentinel
    # Sentinel
    sentinel_masters:1
    sentinel_tilt:0
    sentinel_running_scripts:0
    sentinel_scripts_queue_length:0
    sentinel_simulate_failure_flags:0
    master0:name=mymaster,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=3
    [root@progs redis5]#
    
    -- 6. sentinel启动后,会自动从主节点发现获取有关从节点和其余sentinel节点信息,且更新配置文件
    [root@progs redis5]# grep -Ev "^#|^$" 26379.conf
    port 26379
    daemonize yes
    pidfile "/var/run/redis-sentinel-26379.pid"
    logfile "/ups/app/redis5/logs/sentinel-26379.log"
    dir "/ups/data/redisdata"
    sentinel myid 07b1e48cc7722621f1bebe64f2f84c9e03d6fbdd
    sentinel deny-scripts-reconfig yes
    sentinel monitor mymaster 127.0.0.1 6379 2
    sentinel config-epoch mymaster 0
    sentinel leader-epoch mymaster 0
    protected-mode no
    sentinel known-replica mymaster 192.168.10.181 6381
    sentinel known-replica mymaster 192.168.10.181 6380
    sentinel known-sentinel mymaster 127.0.0.1 26381 46ca78c18e06a7f1d9a79df2dddef8626bd50c61
    sentinel known-sentinel mymaster 127.0.0.1 26380 acbcd245e4e1aac9ae7a6527218bd5295209a9a2
    sentinel current-epoch 0
    [root@progs redis5]# 
    
    参数详解
    -- 1. sentinel monitor
    sentinel monitor <master-name> <ip> <port> <quorum>
    # Sentinel节点会定期监控主节点
    
    -- 2. 当所有sentinel节点启动后,配置文件发生变化:
    - 1. 去掉了默认配置, 例如parallel-syncs、 failover-timeout、down-after-milliseconds参数
    - 2. 添加相关配置,例如known-replica,known-sentinel
    
    -- 3. <quorum>参数用于故障发现和判定
    # quorum 至少要有max(quorum, num(sentinels) /2+1) 个Sentinel节点参与选举, 才能选出领导者Sentinel, 从而完成故障转移
    
    -- 4. down-after-milliseconds
    sentinel down-after-milliseconds <master-name> <times>
    # 每个Sentinel节点都要通过定期发送ping命令来判断Redis数据节点和其余Sentinel节点是否可达, 如果超过了down-after-milliseconds配置的时间且没
    有有效的回复, 则判定节点不可达, <times>(单位为毫秒) 就是超时时间。
    
    -- 5. parallel-syncs
    sentinel parallel-syncs <master-name> <nums>
    # 当Sentinel节点集合对主节点故障判定达成一致时, Sentinel领导者节点会做故障转移操作, 选出新的主节点, 原来的从节点会向新的主节点发起复制操作, 
    parallel-syncs就是用来限制在一次故障转移之后, 每次向新的主节点发起复制操作的从节点个数。 如果这个参数配置的比较大, 那么多个从节点会向新的主节点同时发起复制操作,
     尽管复制操作通常不会阻塞主节点,但是同时向主节点发起复制, 必然会对主节点所在的机器造成一定的网络和磁盘IO开销.
     
     -- 6. failover-timeout
     sentinel failover-timeout <master-name> <times>
     # failover-timeout通常被解释成故障转移超时时间, 但实际上它作用于故障转移的各个阶段:
        a) 选出合适从节点。
        b) 晋升选出的从节点为主节点。
        c) 命令其余从节点复制新的主节点。
        d) 等待原主节点恢复后命令它去复制新的主节点。
      failover-timeout的作用具体体现在四个方面:
        1) 如果Redis Sentinel对一个主节点故障转移失败, 那么下次再对该主节点做故障转移的起始时间是failover-timeout的2倍。
        2) 在b) 阶段时, 如果Sentinel节点向a) 阶段选出来的从节点执行slaveof no one一直失败(例如该从节点此时出现故障) , 当此过程超过failover-timeout时, 则故障转移失败。
        3) 在b) 阶段如果执行成功, Sentinel节点还会执行info命令来确认a)阶段选出来的节点确实晋升为主节点, 如果此过程执行时间超过failovertimeout时, 则故障转移失败。
        4) 如果c) 阶段执行时间超过了failover-timeout(不包含复制时间),则故障转移失败。 注意即使超过了这个时间, Sentinel节点也会最终配置从节点去同步最新的主节点。
        
    -- 7. auth-pass
    sentinel auth-pass <master-name> <password>
    # 如果Sentinel监控的主节点配置了密码, sentinel auth-pass配置通过添加主节点的密码, 防止Sentinel节点对主节点无法监控    
    
    -- 8. notification-script
    sentinel notification-script <master-name> <script-path>
    # 在故障转移期间, 当一些警告级别的Sentinel事件发生(指重要事件, 例如-sdown: 客观下线、 -odown: 主观下线) 时, 会触发对应路径的脚本, 并向脚本发送相应的事件参数。
    
    -- 9. client-reconfig-script
    # 在故障转移结束后, 会触发对应路径的脚本, 并向脚本发送故障转移结果的相关参数
    
    监控多套环境配置模板
    # 结构
    sentinel monitor [master name] [master ip] [master port] [quorum]
    sentinel down-after-milliseconds [master name] 60000
    sentinel failover-timeout [master name] 180000
    sentinel parallel-syncs [master name] 1
    
    
    # 举例
    # master-business-1
    sentinel monitor master-business-1 10.10.xx.1 6379 2
    sentinel down-after-milliseconds master-business-1 60000
    sentinel failover-timeout master-business-1 180000
    sentinel parallel-syncs master-business-1 1
    # master-business-2
    sentinel monitor master-business-2 10.16.xx.2 6380 2
    sentinel down-after-milliseconds master-business-2 10000
    sentinel failover-timeout master-business-2 180000
    sentinel parallel-syncs master-business-2 1
    

    img

    Sentinel参数调整
    -- 动态调整参数,并立刻更新配置文件,set 命令只对当前节点生效
    [root@progs redis5]# rcli -h 127.0.0.1 -p 26380
    127.0.0.1:26380> sentinel set mymaster quorum 3
    OK
    127.0.0.1:26380> sentinel set mymaster quorum 2
    OK
    127.0.0.1:26380>
    
    参数 样例
    quorum sentinel set mymaster quorum 2
    down-after-milliseconds sentinel set mymaster down-after-milliseconds 30000
    failover-timeout sentinel set mymaster failover-timeout 360000
    parallel-syncs sentinel set mymaster parallel-syncs 2
    notification-script sentinel set mymaster notification-script /opt/xx.sh
    client-reconfig-script sentinel set mymaster client-reconfig-script /opt/yy.sh
    auth-pass sentinel set mymaster auth-pass masterPassword
    API 管理
    -- 1. sentinel masters: 展示所有被监控的主节点状态以及相关的统计信息
    127.0.0.1:26380> sentinel masters
    1)  1) "name"
        2) "mymaster"
        3) "ip"
        4) "127.0.0.1"
        5) "port"
        6) "6379"
    ......
    
    -- 2. sentinel master <master name> : 展示指定<master name>的主节点状态以及相关的统计信息
    127.0.0.1:26380> sentinel master mymaster
     1) "name"
     2) "mymaster"
     3) "ip"
     4) "127.0.0.1"
     5) "port"
     6) "6379"
    
    
    -- 3. sentinel slves <master name> : 展示指定<master name>的从节点状态以及相关的统计信息
    127.0.0.1:26380> sentinel slaves mymaster
    1)  1) "name"
        2) "192.168.10.181:6380"
        3) "ip"
        4) "192.168.10.181"
        5) "port"
        6) "6380"
        ......
    2)  1) "name"
        2) "192.168.10.181:6381"
        3) "ip"
        4) "192.168.10.181"
        5) "port"
        6) "6381"
    
     -- 4. sentinel sentinels <master name> : 展示指定<master name>的Sentinel节点集合(不包含当前Sentinel节点)
     127.0.0.1:26380> sentinel sentinels mymaster
    1)  1) "name"
        2) "46ca78c18e06a7f1d9a79df2dddef8626bd50c61"
        3) "ip"
        4) "127.0.0.1"
        5) "port"
        6) "26381"
        ...
    2)  1) "name"
        2) "07b1e48cc7722621f1bebe64f2f84c9e03d6fbdd"
        3) "ip"
        4) "127.0.0.1"
        5) "port"
        6) "26379"
    
      -- 5. sentinel get-master-addr-by-name <master name>:返回指定<master name>主节点的IP地址和端口
      127.0.0.1:26380> sentinel get-master-addr-by-name mymaster
    1) "127.0.0.1"
    2) "6379"
    127.0.0.1:26380>
    
    -- 6. sentinel reset <pattern> : 
    # 当前Sentinel节点对符合<pattern>(通配符风格) 主节点的配置进行重置, 包含清除主节点的相关状态(例如故障转移) , 重新发现从节点和Sentinel节点
    
    -- 7. sentinel failover <master name>: 对指定<master name>主节点进行强制故障转移(没有和其他Sentinel节点“协商”) , 当故障转移完成后, 其他Sentinel节点按照故障转移的结果更新自身配置
    
    -- 8. sentinel ckquorum <master name> : 检测当前可达的Sentinel节点总数是否达到<quorum>的个数。
    127.0.0.1:26380> sentinel ckquorum mymaster
    OK 3 usable Sentinels. Quorum and failover authorization can be reached
    127.0.0.1:26380> 
    
    -- 9. sentinel flushconfig : 将Sentinel节点的配置强制刷到磁盘上
    
    -- 10. sentinel remove <master name> : 取消当前Sentinel节点对于指定<master name>主节点的监控
    
    -- 11. sentinel monitor <master name> <ip> <port> <quorum>: 
    
    -- 12. sentinel set <master name>: 动态修改Sentinel节点配置选项
    
    -- 13. sentinel is-master-down-by-addr: Sentinel节点之间用来交换对主节点是否下线的判断
    
    故障转移模拟实验
    模拟主节点宕机
    -- 1. kill 主节点进程
    [root@progs redis5]# ps -ef|grep redis-
    root      10045      1  0 Nov18 ?        00:10:28 /ups/app/redis5/bin/redis-server 192.168.10.181:6379
    root      27112      1  0 08:25 ?        00:01:05 ./bin/redis-server 192.168.10.181:6380
    root      27124      1  0 08:26 ?        00:01:06 ./bin/redis-server 192.168.10.181:6381
    root      30149      1  0 09:13 ?        00:03:01 ./bin/redis-sentinel *:26379 [sentinel]
    root      31029      1  0 09:26 ?        00:02:59 ./bin/redis-sentinel *:26380 [sentinel]
    root      31037      1  0 09:26 ?        00:02:58 ./bin/redis-sentinel *:26381 [sentinel]
    root      36698  29590  0 10:56 pts/1    00:00:00 /ups/app/redis5/bin/redis-cli -h 127.0.0.1 -p 26380
    root      49862  25666  0 14:25 pts/4    00:00:00 /ups/app/redis5/bin/redis-cli -p 6379
    root      52957  26762  0 15:14 pts/0    00:00:00 grep --color=auto redis-
    [root@progs redis5]# kill -9 10045
    [root@progs redis5]# ps -ef|grep redis-
    root      27112      1  0 08:25 ?        00:01:05 ./bin/redis-server 192.168.10.181:6380
    root      27124      1  0 08:26 ?        00:01:06 ./bin/redis-server 192.168.10.181:6381
    root      30149      1  0 09:13 ?        00:03:01 ./bin/redis-sentinel *:26379 [sentinel]
    root      31029      1  0 09:26 ?        00:02:59 ./bin/redis-sentinel *:26380 [sentinel]
    root      31037      1  0 09:26 ?        00:02:58 ./bin/redis-sentinel *:26381 [sentinel]
    root      36698  29590  0 10:56 pts/1    00:00:00 /ups/app/redis5/bin/redis-cli -h 127.0.0.1 -p 26380
    root      49862  25666  0 14:25 pts/4    00:00:00 /ups/app/redis5/bin/redis-cli -p 6379
    root      52987  26762  0 15:14 pts/0    00:00:00 grep --color=auto redis-
    [root@progs redis5]# date
    Thu Nov 21 15:14:59 CST 2019
    [root@progs redis5]#
    
    -- 2. 6380 节点
      (15:14:44: 发现主节点失联)
    27112:S 21 Nov 2019 08:25:59.126 * Background AOF rewrite finished successfully
    27112:S 21 Nov 2019 15:14:44.053 # Connection with master lost.
    27112:S 21 Nov 2019 15:14:44.053 * Caching the disconnected master state.
    27112:S 21 Nov 2019 15:14:44.584 * Connecting to MASTER 127.0.0.1:6379
    27112:S 21 Nov 2019 15:14:44.584 * MASTER <-> REPLICA sync started
    27112:S 21 Nov 2019 15:14:44.584 # Error condition on socket for SYNC: Connection refused
    27112:S 21 Nov 2019 15:14:45.589 * Connecting to MASTER 127.0.0.1:6379
    27112:S 21 Nov 2019 15:14:45.590 * MASTER <-> REPLICA sync started
    27112:S 21 Nov 2019 15:14:45.590 # Error condition on socket for SYNC: Connection refused
    
      (15:15:14: 接到Sentinel节点的命令: 清理原来缓存的主节点状态, Sentinel节点将6380节点晋升为主节点, 并重写配置)
    27112:S 21 Nov 2019 15:15:13.756 # Error condition on socket for SYNC: Connection refused
    27112:M 21 Nov 2019 15:15:14.396 # Setting secondary replication ID to 913a1700ed78f16e5bdfef2e46726b49f0b3432c, valid up to offset: 4293056. New replication ID is 0be0eab2c4b1150b42d1d3d66ab87fb3ef27b08d
    27112:M 21 Nov 2019 15:15:14.396 * Discarding previously cached master state.
    27112:M 21 Nov 2019 15:15:14.396 * MASTER MODE enabled (user request from 'id=10 addr=192.168.10.181:11474 fd=13 name=sentinel-46ca78c1-cmd age=20902 idle=0 flags=x db=0 sub=0 psub=0 multi=3 qbuf=154 qbuf-free=32614 obl=36 oll=0 omem=0 events=r cmd=exec')
    27112:M 21 Nov 2019 15:15:14.401 # CONFIG REWRITE executed with success.
    
      ( 15:15:14: 6381节点发来了复制请求)
    27112:M 21 Nov 2019 15:15:14.463 * Replica 192.168.10.181:6381 asks for synchronization
    27112:M 21 Nov 2019 15:15:14.463 * Partial resynchronization request from 192.168.10.181:6381 accepted. Sending 166 bytes of backlog starting from offset 4293056.
    
    -- 3. 6381 节点
      (15:14:44: 发现主节点失联)
    27124:S 21 Nov 2019 08:26:04.904 * Background AOF rewrite finished successfully
    27124:S 21 Nov 2019 15:14:44.053 # Connection with master lost.
    27124:S 21 Nov 2019 15:14:44.053 * Caching the disconnected master state.
    27124:S 21 Nov 2019 15:14:44.284 * Connecting to MASTER 127.0.0.1:6379
    27124:S 21 Nov 2019 15:14:44.284 * MASTER <-> REPLICA sync started
    27124:S 21 Nov 2019 15:14:44.284 # Error condition on socket for SYNC: Connection refused
    
      (15:15:14: 接到Sentinel节点的命令,  让它去复制新的主节点)
    27124:S 21 Nov 2019 15:15:13.452 # Error condition on socket for SYNC: Connection refused
    27124:S 21 Nov 2019 15:15:14.452 * REPLICAOF 192.168.10.181:6380 enabled (user request from 'id=10 addr=192.168.10.181:39479 fd=13 name=sentinel-46ca78c1-cmd age=20902 idle=0 flags=x db=0 sub=0 psub=0 multi=3 qbuf=297 qbuf-free=32471 obl=36 oll=0 omem=0 events=r cmd=exec')
    27124:S 21 Nov 2019 15:15:14.459 # CONFIG REWRITE executed with success.
    
      (15:15:14: 向新的主节点(6380节点) 发起复制操作)
    27124:S 21 Nov 2019 15:15:14.459 * Connecting to MASTER 192.168.10.181:6380
    27124:S 21 Nov 2019 15:15:14.460 * MASTER <-> REPLICA sync started
    27124:S 21 Nov 2019 15:15:14.460 * Non blocking connect for SYNC fired the event.
    27124:S 21 Nov 2019 15:15:14.461 * Master replied to PING, replication can continue...
    27124:S 21 Nov 2019 15:15:14.462 * Trying a partial resynchronization (request 913a1700ed78f16e5bdfef2e46726b49f0b3432c:4293056).
    27124:S 21 Nov 2019 15:15:14.464 * Successful partial resynchronization with master.
    27124:S 21 Nov 2019 15:15:14.464 # Master replication ID changed to 0be0eab2c4b1150b42d1d3d66ab87fb3ef27b08d
    27124:S 21 Nov 2019 15:15:14.464 * MASTER <-> REPLICA sync: Master accepted a Partial Resynchronization.
    
    -- 4. sentinel-1
      (15:15:14.118: 主观下线, Sentinel节点更新自己的配置纪元(new-epoch))
    30149:X 21 Nov 2019 15:14:43.851 * +sentinel-address-switch master mymaster 127.0.0.1 6379 ip 192.168.10.181 port 26381 for 46ca78c18e06a7f1d9a79df2dddef8626bd50c61
    30149:X 21 Nov 2019 15:15:14.118 # +sdown master mymaster 127.0.0.1 6379
    30149:X 21 Nov 2019 15:15:14.187 # +new-epoch 1
      (投票给)
    30149:X 21 Nov 2019 15:15:14.188 # +vote-for-leader 46ca78c18e06a7f1d9a79df2dddef8626bd50c61 1
    30149:X 21 Nov 2019 15:15:14.452 # +config-update-from sentinel 46ca78c18e06a7f1d9a79df2dddef8626bd50c61 192.168.10.181 26381 @ mymaster 127.0.0.1 6379
      (6380 成为主节点,发现2个从节点, 然后6379主观下线)
    30149:X 21 Nov 2019 15:15:14.452 # +switch-master mymaster 127.0.0.1 6379 192.168.10.181 6380
    30149:X 21 Nov 2019 15:15:14.453 * +slave slave 192.168.10.181:6381 192.168.10.181 6381 @ mymaster 192.168.10.181 6380
    30149:X 21 Nov 2019 15:15:14.453 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 192.168.10.181 6380
    30149:X 21 Nov 2019 15:15:44.484 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 192.168.10.181 6380
    
    -- 5. sentinel-2
    31029:X 21 Nov 2019 15:15:14.150 # +sdown master mymaster 127.0.0.1 6379
    31029:X 21 Nov 2019 15:15:14.187 # +new-epoch 1
    31029:X 21 Nov 2019 15:15:14.188 # +vote-for-leader 46ca78c18e06a7f1d9a79df2dddef8626bd50c61 1
    31029:X 21 Nov 2019 15:15:14.213 # +odown master mymaster 127.0.0.1 6379 #quorum 3/2
    31029:X 21 Nov 2019 15:15:14.213 # Next failover delay: I will not start a failover before Thu Nov 21 15:21:14 2019
    31029:X 21 Nov 2019 15:15:14.455 # +config-update-from sentinel 46ca78c18e06a7f1d9a79df2dddef8626bd50c61 192.168.10.181 26381 @ mymaster 127.0.0.1 6379
    31029:X 21 Nov 2019 15:15:14.455 # +switch-master mymaster 127.0.0.1 6379 192.168.10.181 6380
    31029:X 21 Nov 2019 15:15:14.456 * +slave slave 192.168.10.181:6381 192.168.10.181 6381 @ mymaster 192.168.10.181 6380
    31029:X 21 Nov 2019 15:15:14.456 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 192.168.10.181 6380
    31029:X 21 Nov 2019 15:15:44.478 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 192.168.10.181 6380
    
    -- 6. sentinel-3
      (15:15:14: 达到客观下线条件)
    31037:X 21 Nov 2019 15:15:14.119 # +sdown master mymaster 127.0.0.1 6379
    31037:X 21 Nov 2019 15:15:14.185 # +odown master mymaster 127.0.0.1 6379 #quorum 2/2
    31037:X 21 Nov 2019 15:15:14.185 # +new-epoch 1
      (sentinel-3 被选为领导者)
    31037:X 21 Nov 2019 15:15:14.185 # +try-failover master mymaster 127.0.0.1 6379
    31037:X 21 Nov 2019 15:15:14.186 # +vote-for-leader 46ca78c18e06a7f1d9a79df2dddef8626bd50c61 1
    31037:X 21 Nov 2019 15:15:14.188 # 07b1e48cc7722621f1bebe64f2f84c9e03d6fbdd voted for 46ca78c18e06a7f1d9a79df2dddef8626bd50c61 1
    31037:X 21 Nov 2019 15:15:14.188 # acbcd245e4e1aac9ae7a6527218bd5295209a9a2 voted for 46ca78c18e06a7f1d9a79df2dddef8626bd50c61 1
    31037:X 21 Nov 2019 15:15:14.249 # +elected-leader master mymaster 127.0.0.1 6379
      (寻找合适的从节点作为新的主节点 6380)
    31037:X 21 Nov 2019 15:15:14.249 # +failover-state-select-slave master mymaster 127.0.0.1 6379
    31037:X 21 Nov 2019 15:15:14.332 # +selected-slave slave 192.168.10.181:6380 192.168.10.181 6380 @ mymaster 127.0.0.1 6379
      (命令6380节点执行slaveof no one, 使其成为主节点)
    31037:X 21 Nov 2019 15:15:14.333 * +failover-state-send-slaveof-noone slave 192.168.10.181:6380 192.168.10.181 6380 @ mymaster 127.0.0.1 6379
      (等待6380节点晋升为主节点)
    31037:X 21 Nov 2019 15:15:14.395 * +failover-state-wait-promotion slave 192.168.10.181:6380 192.168.10.181 6380 @ mymaster 127.0.0.1 6379
      (确认6380节点已经晋升为主节点)
    31037:X 21 Nov 2019 15:15:14.403 # +promoted-slave slave 192.168.10.181:6380 192.168.10.181 6380 @ mymaster 127.0.0.1 6379
      (故障转移进入重新配置从节点阶段)
    31037:X 21 Nov 2019 15:15:14.403 # +failover-state-reconf-slaves master mymaster 127.0.0.1 6379
      (命令6381节点复制新的主节点)
    31037:X 21 Nov 2019 15:15:14.452 * +slave-reconf-sent slave 192.168.10.181:6381 192.168.10.181 6381 @ mymaster 127.0.0.1 6379
      (正在重新配置成为6380节点的从节点, 但是同步过程尚未完成)
    31037:X 21 Nov 2019 15:15:14.772 * +slave-reconf-inprog slave 192.168.10.181:6381 192.168.10.181 6381 @ mymaster 127.0.0.1 6379
      (6381节点完成对6380节点的同步)
    31037:X 21 Nov 2019 15:15:14.772 * +slave-reconf-done slave 192.168.10.181:6381 192.168.10.181 6381 @ mymaster 127.0.0.1 6379
      (故障转移顺利完成)
    31037:X 21 Nov 2019 15:15:14.849 # +failover-end master mymaster 127.0.0.1 6379
      (故障转移成功后, 发布主节点的切换消息)
    31037:X 21 Nov 2019 15:15:14.849 # +switch-master mymaster 127.0.0.1 6379 192.168.10.181 6380
    31037:X 21 Nov 2019 15:15:14.850 * +slave slave 192.168.10.181:6381 192.168.10.181 6381 @ mymaster 192.168.10.181 6380
    31037:X 21 Nov 2019 15:15:14.850 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 192.168.10.181 6380
    31037:X 21 Nov 2019 15:15:44.867 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 192.168.10.181 6380
    
    
    主动切换

    img

    -- 6380
    [root@progs ~]# rcli -p 6380
    127.0.0.1:6380> config set slave-priority 0
    OK
    127.0.0.1:6380> config rewrite
    OK
    127.0.0.1:6380> exit
    
    [root@progs ~]# rcli -p 6381
    127.0.0.1:6381> config set slave-priority 0
    OK
    127.0.0.1:6381> config rewrite
    OK
    127.0.0.1:6381> exit
    
    -- failover
    [root@progs logs]# rcli -p 26379 
    127.0.0.1:26379> sentinel failover mymaster
    OK
    127.0.0.1:26379> 
    
    -- 检查
    for i in 6379 6380 6381; do 
        echo -e $i
        rcli -p $i info replication|grep role
        rcli -p $i config get slave-priority
    done
    
    Sentinel发布订阅

    img

    5.3 集群(cluster)

    Redis Cluster是Redis的分布式解决方案, 在3.0版本正式推出, 有效地解决了Redis分布式方面的需求。Redis3.0版本之前,通过Redis Sentinel(哨兵)来实现高可用(HA)。

    Redis Cluster 集群节点最小配置 6 个节点以上(3主3从),其中主节点提供读写操作,从节点作为备用节点,不提供请求,只作为故障转移使用。

    Redis Cluster 采用虚拟槽分区,所有的键根据哈希函数映射到 0~16383 个整数槽内,每个节点负责维护一部分槽以及槽所映射的键值数据。

    环境部署

    手动敲命令部署Cluster集群
    -- 1. 配置6个节点
    cd /ups/app/redis5/config
    cp redis-cluster-6379.conf redis-cluster-6380.conf 
    cp redis-cluster-6379.conf redis-cluster-6381.conf 
    cp redis-cluster-6379.conf redis-cluster-6382.conf 
    cp redis-cluster-6379.conf redis-cluster-6383.conf 
    cp redis-cluster-6379.conf redis-cluster-6384.conf 
    cp redis-cluster-6379.conf redis-cluster-6385.conf 
    cp redis-cluster-6379.conf redis-cluster-6386.conf
    
    sed -i 's/6379/6384/g' redis-cluster-6384.conf
    
    -- 2. 启动
    /ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6379.conf
    /ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6380.conf
    /ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6381.conf
    /ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6382.conf
    /ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6383.conf
    /ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6384.conf
    
    -- 3. 建立节点握手
    [root@progs redisdata]# rcli -p 6379
    127.0.0.1:6379> cluster nodes
    02a7b79fc1a4848b05b8369616a2f6ba7327dc02 :6379@16379 myself,master - 0 0 0 connected
    127.0.0.1:6379> 
    127.0.0.1:6379> 
    127.0.0.1:6379> 
    127.0.0.1:6379> cluster meet 127.0.0.1 6380
    OK
    127.0.0.1:6379> cluster nodes
    3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574407049306 0 connected
    02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,master - 0 0 1 connected
    127.0.0.1:6379> cluster meet 127.0.0.1 6381
    OK
    127.0.0.1:6379> cluster meet 127.0.0.1 6382
    OK
    127.0.0.1:6379> cluster meet 127.0.0.1 6383
    OK
    127.0.0.1:6379> cluster meet 127.0.0.1 6384
    OK
    127.0.0.1:6379> cluster nodes
    3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574407299822 2 connected
    eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 master - 0 1574407301835 0 connected
    4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 master - 0 1574407299000 5 connected
    763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574407299000 3 connected
    02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,master - 0 1574407300000 1 connected
    052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 master - 0 1574407301000 4 connected
    127.0.0.1:6379> cluster info
    cluster_state:fail
    cluster_slots_assigned:0
    cluster_slots_ok:0
    cluster_slots_pfail:0
    cluster_slots_fail:0
    cluster_known_nodes:6
    cluster_size:0
    cluster_current_epoch:5
    cluster_my_epoch:1
    cluster_stats_messages_ping_sent:303
    cluster_stats_messages_pong_sent:297
    cluster_stats_messages_meet_sent:5
    cluster_stats_messages_sent:605
    cluster_stats_messages_ping_received:297
    cluster_stats_messages_pong_received:308
    cluster_stats_messages_received:605
    127.0.0.1:6379> 
    
    -- 4. 分配槽
    rcli -h 127.0.0.1 -p 6379 cluster addslots {0..5461}
    rcli -h 127.0.0.1 -p 6380 cluster addslots {5462..10922}
    rcli -h 127.0.0.1 -p 6381 cluster addslots {10923..16383}
    
    [root@progs ~]# rcli -h 127.0.0.1 -p 6379 cluster addslots {0..5461}
    OK
    [root@progs ~]#
    
    127.0.0.1:6379> cluster info
    cluster_state:ok
    cluster_slots_assigned:16384
    cluster_slots_ok:16384
    cluster_slots_pfail:0
    cluster_slots_fail:0
    cluster_known_nodes:6
    cluster_size:3
    cluster_current_epoch:5
    cluster_my_epoch:1
    cluster_stats_messages_ping_sent:728
    cluster_stats_messages_pong_sent:695
    cluster_stats_messages_meet_sent:5
    cluster_stats_messages_sent:1428
    cluster_stats_messages_ping_received:695
    cluster_stats_messages_pong_received:733
    cluster_stats_messages_received:1428
    127.0.0.1:6379> 
    127.0.0.1:6379> cluster nodes
    3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574407820950 2 connected 5462-10922
    eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 master - 0 1574407821000 0 connected
    4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 master - 0 1574407823969 5 connected
    763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574407821956 3 connected 10923-16383
    02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,master - 0 1574407818000 1 connected 0-5461
    052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 master - 0 1574407822962 4 connected
    127.0.0.1:6379>
    
    -- 5. 配置从节点
    127.0.0.1:6382>cluster replicate 02a7b79fc1a4848b05b8369616a2f6ba7327dc02
    127.0.0.1:6383>cluster replicate 3a551c30fce6f8455728a4cc88ac05ad5a15fc41
    127.0.0.1:6384>cluster replicate 763c88dfe985e3946ce829eba5b3e32535f322f6
    
    [root@progs ~]# rcli -p 6382 cluster replicate 02a7b79fc1a4848b05b8369616a2f6ba7327dc02
    OK
    [root@progs ~]# rcli -p 6383 cluster replicate 3a551c30fce6f8455728a4cc88ac05ad5a15fc41
    OK
    [root@progs ~]# rcli -p 6384 cluster replicate 763c88dfe985e3946ce829eba5b3e32535f322f6
    OK
    [root@progs ~]#
    
    -- 检查从节点及其槽
    127.0.0.1:6379> cluster nodes
    3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574408081000 2 connected 5462-10922
    eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 slave 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 0 1574408082000 1 connected
    4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 slave 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 0 1574408081000 5 connected
    763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574408080459 3 connected 10923-16383
    02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,master - 0 1574408079000 1 connected 0-5461
    052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 slave 763c88dfe985e3946ce829eba5b3e32535f322f6 0 1574408082474 4 connected
    127.0.0.1:6379>
    
    API工具部署
    redis-trib.rb工具快捷部署(适用于redis 5版本之前)
    -- 1. 安装Ruby
    curl -C - -O https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.5.tar.gz
    
    tar xf ruby-2.6.5tar.gz
    ./configure --prefix=/ups/app/ruby
    make && make install
    
    OR
    yum -y install ruby
    
    -- 2. 安装插件
    /*
    curl -C - -O https://rubygems.org/rubygems/rubygems-3.0.6.tgz
    tar xf rubygems-3.0.6.tgz
    cd rubygems-3.0.6
     ruby setup.rb
    */
    export PATH=${PATH}:/ups/app/ruby/bin
    curl -C - -O https://rubygems.org/downloads/redis-4.1.3.gem
    gem install -l redis-4.1.3.gem
    gem list redis gem
    
    -- 安装redis-trib.rb
    cp /ups/soft/redis-5.0.5/src/redis-trib.rb /ups/app/redis5/bin/
    [root@progs bin]# ./redis-trib.rb --help
    WARNING: redis-trib.rb is not longer available!
    You should use redis-cli instead.
    
    
    -- 3. 准备配置
    cd /ups/app/redis5/config
    cp redis-cluster-6379.conf redis-cluster-6380.conf 
    cp redis-cluster-6379.conf redis-cluster-6381.conf 
    cp redis-cluster-6379.conf redis-cluster-6382.conf 
    cp redis-cluster-6379.conf redis-cluster-6383.conf 
    cp redis-cluster-6379.conf redis-cluster-6384.conf 
    cp redis-cluster-6379.conf redis-cluster-6385.conf 
    cp redis-cluster-6379.conf redis-cluster-6386.conf
    
    sed -i 's/6379/6384/g' redis-cluster-6384.conf
    
    /ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6379.conf
    /ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6380.conf
    /ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6381.conf
    /ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6382.conf
    /ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6383.conf
    /ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6384.conf
    
    -- 4. 创建集群
    redis-trib.rb create --replicas 1 127.0.0.1:6481 127.0.0.1:6482 127.0.0.1:6483 127.0.0.1:6484 127.0.0.1:6485 127.0.0.1:6486
    # --replicas参数指定集群中每个主节点配备几个从节点
    
    -- 5. 检查完整性
    redis-trib.rb check 127.0.0.1:6379
    
    
    redis-cli工具部署(适用于redis5及以上)
    -- 1. 准备配置文件和启动服务
    cd /ups/app/redis5/config
    cp redis-cluster-6379.conf redis-cluster-6380.conf 
    cp redis-cluster-6379.conf redis-cluster-6381.conf 
    cp redis-cluster-6379.conf redis-cluster-6382.conf 
    cp redis-cluster-6379.conf redis-cluster-6383.conf 
    cp redis-cluster-6379.conf redis-cluster-6384.conf 
    cp redis-cluster-6379.conf redis-cluster-6385.conf 
    cp redis-cluster-6379.conf redis-cluster-6386.conf
    
    sed -i 's/6379/6384/g' redis-cluster-6384.conf
    
    /ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6379.conf
    /ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6380.conf
    /ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6381.conf
    /ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6382.conf
    /ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6383.conf
    /ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6384.conf
    
    -- 2. 创建集群
    redis-cli --cluster create --cluster-replicas 1 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384
    
    -- 3. 验证
    rcli -c -p 6379 cluster nodes
    rcli -c -p 6379 cluster info
    

    集群扩容

    手动扩容集群
    -- 启动孤儿节点
    /ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6385.conf
    /ups/app/redis5/bin/redis-server /ups/app/redis5/config/redis-cluster-6386.conf
    
    -- 2. 加入集群
    [root@progs ~]# rcli -p 6379
    127.0.0.1:6379> cluster meet 127.0.0.1 6385
    OK
    127.0.0.1:6379> cluster meet 127.0.0.1 6386
    OK
    127.0.0.1:6379> cluster nodes
    cd64d0f790fb0c568fe48f458fc9bf18c51662a5 127.0.0.1:6385@16385 master - 0 1574641619000 0 connected
    3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574641618000 2 connected 5462-10922
    3bbb9a93de8cff52a813ef754cd8229da05d2e12 127.0.0.1:6386@16386 master - 0 1574641620564 6 connected
    eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 slave 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 0 1574641617143 1 connected
    4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 slave 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 0 1574641617000 5 connected
    763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574641619156 3 connected 10923-16383
    02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,master - 0 1574641617000 1 connected 0-5461
    052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 slave 763c88dfe985e3946ce829eba5b3e32535f322f6 0 1574641620162 4 connected
    127.0.0.1:6379>
    
    /*
    # 准备数据
    127.0.0.1:6379> SET key:{test}:555 value:test:555
    127.0.0.1:6380> SET key:{test}:666 value:test:666
    OK
    127.0.0.1:6380> SET key:{test}:777 value:test:777
    OK
    127.0.0.1:6380> CLUSTER KEYSLOT key:{test}:555
    */
    
    -- 3. 迁移槽和数据(数据迁移过程是逐个槽进行的, 每个槽数据迁移的流程如下)
    - 3.1 目标节点导入4096槽数据 (cluster setslot {slot} importing {sourceNodeId})
    rcli -p 6385
    cluster setslot 4096 importing 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 
    cluster nodes
    
    [root@progs ~]# rcli -p 6385
    127.0.0.1:6385> cluster setslot 4096 importing 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 
    OK
    127.0.0.1:6385> cluster nodes
    763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574642222000 3 connected 10923-16383
    3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574642223000 2 connected 5462-10922
    4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 slave 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 0 1574642222000 2 connected
    eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 slave 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 0 1574642224884 1 connected
    cd64d0f790fb0c568fe48f458fc9bf18c51662a5 127.0.0.1:6385@16385 myself,master - 0 1574642223000 0 connected [4096-<-02a7b79fc1a4848b05b8369616a2f6ba7327dc02]
    3bbb9a93de8cff52a813ef754cd8229da05d2e12 127.0.0.1:6386@16386 master - 0 1574642225890 6 connected
    052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 slave 763c88dfe985e3946ce829eba5b3e32535f322f6 0 1574642223000 3 connected
    02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 master - 0 1574642224000 1 connected 0-5461
    127.0.0.1:6385>
    
    - 3.2 对源节点发送命令,准备迁移出槽的数据 (cluster setslot {slot} migrating {targetNodeId})
    rcli -p 6379
    cluster setslot 4096 migrating cd64d0f790fb0c568fe48f458fc9bf18c51662a5
    
    127.0.0.1:6379> cluster setslot 4096 migrating cd64d0f790fb0c568fe48f458fc9bf18c51662a5
    OK
    127.0.0.1:6379> cluster nodes
    cd64d0f790fb0c568fe48f458fc9bf18c51662a5 127.0.0.1:6385@16385 master - 0 1574642298533 0 connected
    3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574642295000 2 connected 5462-10922
    3bbb9a93de8cff52a813ef754cd8229da05d2e12 127.0.0.1:6386@16386 master - 0 1574642295515 6 connected
    eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 slave 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 0 1574642297529 1 connected
    4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 slave 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 0 1574642294000 5 connected
    763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574642296520 3 connected 10923-16383
    02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,master - 0 1574642296000 1 connected 0-5461 [4096->-cd64d0f790fb0c568fe48f458fc9bf18c51662a5]
    052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 slave 763c88dfe985e3946ce829eba5b3e32535f322f6 0 1574642297000 4 connected
    127.0.0.1:6379> 
    
    - 3.3 源节点循环执行 (cluster getkeysinslot {slot} {count})命令, 获取count个属于槽{slot}的键
    cluster getkeysinslot 4096 100
    
    - 3.4 在源节点上执行(migrate {targetIp} {targetPort} "" 0 {timeout} keys {keys...})命令, 把获取的键通过流水线(pipeline) 机制批量迁移到目标节点
    migrate 127.0.0.1 6385 "" 0 5000 keys key:test:5028
    
    - 3.5 重复3和4步骤,直到槽下所有的键值数据迁移到目标节点
    
    - 3.6 向集群内所有主节点发送cluster setslot {slot} node {targetNodeId}命令, 通知槽分配给目标节点
    cluster setslot 4096 node cd64d0f790fb0c568fe48f458fc9bf18c51662a5
    
    [root@progs config]# rcli -p 6379 cluster setslot 4096 node cd64d0f790fb0c568fe48f458fc9bf18c51662a5
    OK
    [root@progs config]# rcli -p 6380 cluster setslot 4096 node cd64d0f790fb0c568fe48f458fc9bf18c51662a5
    OK
    [root@progs config]# rcli -p 6381 cluster setslot 4096 node cd64d0f790fb0c568fe48f458fc9bf18c51662a5
    OK
    [root@progs config]# rcli -p 6385 cluster setslot 4096 node cd64d0f790fb0c568fe48f458fc9bf18c51662a5
    OK
    [root@progs config]# rcli -p 6379 cluster nodes
    cd64d0f790fb0c568fe48f458fc9bf18c51662a5 127.0.0.1:6385@16385 master - 0 1574642672950 7 connected 4096
    3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574642669928 2 connected 5462-10922
    3bbb9a93de8cff52a813ef754cd8229da05d2e12 127.0.0.1:6386@16386 master - 0 1574642667916 6 connected
    eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 slave 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 0 1574642670000 1 connected
    4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 slave 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 0 1574642670936 5 connected
    763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574642670000 3 connected 10923-16383
    02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,master - 0 1574642672000 1 connected 0-4095 4097-5461
    052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 slave 763c88dfe985e3946ce829eba5b3e32535f322f6 0 1574642671944 4 connected
    [root@progs config]# 
    
    -- 4. 添加从节点(cluster replicate {targetMasterNodeId})
    rcli -p 6386 cluster replicate cd64d0f790fb0c568fe48f458fc9bf18c51662a5
    
    [root@progs config]# rcli -p 6386 cluster replicate cd64d0f790fb0c568fe48f458fc9bf18c51662a5
    OK
    [root@progs config]# rcli -p 6379 cluster nodes
    cd64d0f790fb0c568fe48f458fc9bf18c51662a5 127.0.0.1:6385@16385 master - 0 1574642826000 7 connected 4096
    3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574642823000 2 connected 5462-10922
    3bbb9a93de8cff52a813ef754cd8229da05d2e12 127.0.0.1:6386@16386 slave cd64d0f790fb0c568fe48f458fc9bf18c51662a5 0 1574642827996 7 connected
    eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 slave 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 0 1574642826989 1 connected
    4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 slave 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 0 1574642827000 5 connected
    763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574642828000 3 connected 10923-16383
    02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,master - 0 1574642824000 1 connected 0-4095 4097-5461
    052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 slave 763c88dfe985e3946ce829eba5b3e32535f322f6 0 1574642829001 4 connected
    [root@progs config]#
    
    集群水平伸缩(redis5 )
    # 用法
    [root@progs config]# rcli --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
      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
      set-timeout    host:port milliseconds
      import         host:port
                     --cluster-from <arg>
                     --cluster-copy
                     --cluster-replace
      help           
    
    For check, fix, reshard, del-node, set-timeout you can specify the host and port of any working node in the cluster.
    
    # 操作
    -- 1. 加节点
    redis-cli --cluster add-node 127.0.0.1:6385 127.0.0.1:6379
    redis-cli --cluster add-node 127.0.0.1:6386 127.0.0.1:6379
    
    -- 2. 分配hash槽 (redis-cli --cluster reshard 127.0.0.1:6379)
    [root@progs config]# rcli --cluster reshard 127.0.0.1:6379
    >>> Performing Cluster Check (using node 127.0.0.1:6379)
    M: 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379
       slots:[0-4095],[4097-5461] (5461 slots) master
       1 additional replica(s)
    M: cd64d0f790fb0c568fe48f458fc9bf18c51662a5 127.0.0.1:6385
       slots:[4096] (1 slots) master
       1 additional replica(s)
    M: 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380
       slots:[5462-10922] (5461 slots) master
       1 additional replica(s)
    S: 3bbb9a93de8cff52a813ef754cd8229da05d2e12 127.0.0.1:6386
       slots: (0 slots) slave
       replicates cd64d0f790fb0c568fe48f458fc9bf18c51662a5
    S: eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382
       slots: (0 slots) slave
       replicates 02a7b79fc1a4848b05b8369616a2f6ba7327dc02
    S: 4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383
       slots: (0 slots) slave
       replicates 3a551c30fce6f8455728a4cc88ac05ad5a15fc41
    M: 763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381
       slots:[10923-16383] (5461 slots) master
       1 additional replica(s)
    S: 052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384
       slots: (0 slots) slave
       replicates 763c88dfe985e3946ce829eba5b3e32535f322f6
    [OK] All nodes agree about slots configuration.
    >>> Check for open slots...
    >>> Check slots coverage...
    [OK] All 16384 slots covered.
    How many slots do you want to move (from 1 to 16384)? 4096                             <<<<<<<<<<<< 分配多少个槽给新节点6385
    What is the receiving node ID? cd64d0f790fb0c568fe48f458fc9bf18c51662a5                <<<<<<<<<<<< 接收节点的ID(6385)
    Please enter all the source node IDs.
      Type 'all' to use all the nodes as source nodes for the hash slots.
      Type 'done' once you entered all the source nodes IDs.
    Source node #1: all                                                                    <<<<<<<<<<<< all表示是所有服务节点
    
    Ready to move 4096 slots.
      Source nodes:
        M: 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379
           slots:[0-4095],[4097-5461] (5461 slots) master
           1 additional replica(s)
        M: 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380
           slots:[5462-10922] (5461 slots) master
           1 additional replica(s)
        M: 763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381
           slots:[10923-16383] (5461 slots) master
           1 additional replica(s)
      Destination node:
        M: cd64d0f790fb0c568fe48f458fc9bf18c51662a5 127.0.0.1:6385
           slots:[4096] (1 slots) master
           1 additional replica(s)
      Resharding plan:
        Moving slot 0 from 02a7b79fc1a4848b05b8369616a2f6ba7327dc02
        Moving slot 1 from 02a7b79fc1a4848b05b8369616a2f6ba7327dc02
        Moving slot 2 from 02a7b79fc1a4848b05b8369616a2f6ba7327dc02
        ...
        ...
        Moving slot 12287 from 763c88dfe985e3946ce829eba5b3e32535f322f6
    Do you want to proceed with the proposed reshard plan (yes/no)? yes
    
    -- 3. 设置从节点(cluster replicate {targetMasterNodeId})
    rcli -p 6386 cluster replicate cd64d0f790fb0c568fe48f458fc9bf18c51662a5
    
    -- 4. 查看节点情况
    [root@progs config]# rcli -c -p 6379 cluster nodes
    cd64d0f790fb0c568fe48f458fc9bf18c51662a5 127.0.0.1:6385@16385 master - 0 1574646742000 7 connected 0-1365 4096 5462-6826 10923-12287
    3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574646742000 2 connected 6827-10922
    3bbb9a93de8cff52a813ef754cd8229da05d2e12 127.0.0.1:6386@16386 slave cd64d0f790fb0c568fe48f458fc9bf18c51662a5 0 1574646739000 7 connected
    eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 slave 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 0 1574646740986 1 connected
    4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 slave 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 0 1574646737000 5 connected
    763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574646743002 3 connected 12288-16383
    02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,master - 0 1574646739000 1 connected 1366-4095 4097-5461
    052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 slave 763c88dfe985e3946ce829eba5b3e32535f322f6 0 1574646742000 4 connected
    [root@progs config]#
    
    集群收缩(redis5)
    -- 1. 删除从节点 (6386) rcli --cluster del-node 127.0.0.1:6386 3bbb9a93de8cff52a813ef754cd8229da05d2e12
    
    [root@progs config]# rcli --cluster del-node 127.0.0.1:6386 3bbb9a93de8cff52a813ef754cd8229da05d2e12
    >>> Removing node 3bbb9a93de8cff52a813ef754cd8229da05d2e12 from cluster 127.0.0.1:6386
    >>> Sending CLUSTER FORGET messages to the cluster...
    >>> SHUTDOWN the node.
    [root@progs config]# rcli -c -p 6379 cluster nodes
    cd64d0f790fb0c568fe48f458fc9bf18c51662a5 127.0.0.1:6385@16385 master - 0 1574647032000 7 connected 0-1365 4096 5462-6826 10923-12287
    3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574647033000 2 connected 6827-10922
    eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 slave 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 0 1574647034841 1 connected
    4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 slave 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 0 1574647033000 5 connected
    763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574647033837 3 connected 12288-16383
    02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,master - 0 1574647032000 1 connected 1366-4095 4097-5461
    052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 slave 763c88dfe985e3946ce829eba5b3e32535f322f6 0 1574647032000 4 connected
    [root@progs config]#
    
    -- 2. 对主节点重新分片 ( redis-cli --cluster reshard 127.0.0.1:6385)
    [root@progs config]# rcli --cluster reshard 127.0.0.1:6385
    >>> Performing Cluster Check (using node 127.0.0.1:6385)
    M: cd64d0f790fb0c568fe48f458fc9bf18c51662a5 127.0.0.1:6385
       slots:[0-1365],[4096],[5462-6826],[10923-12287] (4097 slots) master
    M: 763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381
       slots:[12288-16383] (4096 slots) master
       1 additional replica(s)
    M: 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380
       slots:[6827-10922] (4096 slots) master
       1 additional replica(s)
    S: 4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383
       slots: (0 slots) slave
       replicates 3a551c30fce6f8455728a4cc88ac05ad5a15fc41
    S: eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382
       slots: (0 slots) slave
       replicates 02a7b79fc1a4848b05b8369616a2f6ba7327dc02
    S: 052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384
       slots: (0 slots) slave
       replicates 763c88dfe985e3946ce829eba5b3e32535f322f6
    M: 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379
       slots:[1366-4095],[4097-5461] (4095 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.
    How many slots do you want to move (from 1 to 16384)? 4097
    What is the receiving node ID? 02a7b79fc1a4848b05b8369616a2f6ba7327dc02
    Please enter all the source node IDs.
      Type 'all' to use all the nodes as source nodes for the hash slots.
      Type 'done' once you entered all the source nodes IDs.
    Source node #1: cd64d0f790fb0c568fe48f458fc9bf18c51662a5
    Source node #2: done
    ...
        Moving slot 12286 from cd64d0f790fb0c568fe48f458fc9bf18c51662a5
        Moving slot 12287 from cd64d0f790fb0c568fe48f458fc9bf18c51662a5
    Do you want to proceed with the proposed reshard plan (yes/no)? yes
    ...
    [root@progs config]#
    
    [root@progs config]# rcli -c -p 6379 cluster nodes
    cd64d0f790fb0c568fe48f458fc9bf18c51662a5 127.0.0.1:6385@16385 master - 0 1574648077000 7 connected
    3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574648078000 2 connected 6827-10922
    eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 slave 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 0 1574648079611 8 connected
    4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 slave 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 0 1574648077000 5 connected
    763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574648080618 3 connected 12288-16383
    02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,master - 0 1574648076000 8 connected 0-6826 10923-12287
    052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 slave 763c88dfe985e3946ce829eba5b3e32535f322f6 0 1574648079000 4 connected
    [root@progs config]# 
    
    -- 3. 删除节点(6385)
    rcli --cluster del-node 127.0.0.1:6385 cd64d0f790fb0c568fe48f458fc9bf18c51662a5
    
    [root@progs config]# rcli --cluster del-node 127.0.0.1:6385 cd64d0f790fb0c568fe48f458fc9bf18c51662a5
    >>> Removing node cd64d0f790fb0c568fe48f458fc9bf18c51662a5 from cluster 127.0.0.1:6385
    >>> Sending CLUSTER FORGET messages to the cluster...
    >>> SHUTDOWN the node.
    [root@progs config]# rcli -c -p 6379 cluster nodes
    3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574648207000 2 connected 6827-10922
    eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 slave 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 0 1574648206452 8 connected
    4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 slave 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 0 1574648209000 5 connected
    763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574648209474 3 connected 12288-16383
    02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,master - 0 1574648208000 8 connected 0-6826 10923-12287
    052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 slave 763c88dfe985e3946ce829eba5b3e32535f322f6 0 1574648207459 4 connected
    [root@progs config]#
    
    -- 4. 数据平衡
    [root@progs config]# rcli --cluster rebalance  127.0.0.1:6379
    >>> Performing Cluster Check (using node 127.0.0.1:6379)
    [OK] All nodes agree about slots configuration.
    >>> Check for open slots...
    >>> Check slots coverage...
    [OK] All 16384 slots covered.
    >>> Rebalancing across 3 nodes. Total weight = 3.00
    Moving 1366 slots from 127.0.0.1:6379 to 127.0.0.1:6380
    ################################################################################################################################################
    Moving 1365 slots from 127.0.0.1:6379 to 127.0.0.1:6381
    ################################################################################################################################################--
    [root@progs config]# 
    [root@progs config]# rcli -c -p 6379 cluster nodes
    3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574650327091 9 connected 0-1365 6827-10922
    eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 slave 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 0 1574650324000 8 connected
    4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 slave 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 0 1574650328097 9 connected
    763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574650326083 10 connected 1366-2730 12288-16383
    02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,master - 0 1574650325000 8 connected 2731-6826 10923-12287
    052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 slave 763c88dfe985e3946ce829eba5b3e32535f322f6 0 1574650325075 10 connected
    [root@progs config]#
    

    故障转移

    -- 1. failover
    [root@progs config]# rcli -c -p 6379 cluster nodes
    3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574650946000 9 connected 0-1365 6827-10922
    eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 slave 02a7b79fc1a4848b05b8369616a2f6ba7327dc02 0 1574650947235 8 connected
    4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 slave 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 0 1574650945222 9 connected
    763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574650945000 10 connected 1366-2730 12288-16383
    02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,master - 0 1574650946000 8 connected 2731-6826 10923-12287
    052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 slave 763c88dfe985e3946ce829eba5b3e32535f322f6 0 1574650946228 10 connected
    [root@progs config]# rcli -p 6382 cluster failover
    OK
    [root@progs config]# rcli -c -p 6379 cluster nodes
    3a551c30fce6f8455728a4cc88ac05ad5a15fc41 127.0.0.1:6380@16380 master - 0 1574650966371 9 connected 0-1365 6827-10922
    eacdc8ec0f52b13a188a39cd40aa3272f32d3852 127.0.0.1:6382@16382 master - 0 1574650964360 11 connected 2731-6826 10923-12287
    4b6d3dbc64661d57564fadd25fdb38bcc12401e5 127.0.0.1:6383@16383 slave 3a551c30fce6f8455728a4cc88ac05ad5a15fc41 0 1574650964000 9 connected
    763c88dfe985e3946ce829eba5b3e32535f322f6 127.0.0.1:6381@16381 master - 0 1574650964000 10 connected 1366-2730 12288-16383
    02a7b79fc1a4848b05b8369616a2f6ba7327dc02 127.0.0.1:6379@16379 myself,slave eacdc8ec0f52b13a188a39cd40aa3272f32d3852 0 1574650963000 8 connected
    052722f4e34429e5958617d8f2c72172983cfb4a 127.0.0.1:6384@16384 slave 763c88dfe985e3946ce829eba5b3e32535f322f6 0 1574650965365 10 connected
    [root@progs config]#
    
  • 相关阅读:
    iterm2 配色修改
    让Dock自动 显示/隐藏 不再有延迟
    Xcode更改配色方案
    CocoaPods安装与使用
    CocoaPods安装和使用及问题:Setting up CocoaPods master repo
    CocoaPods安装和使用教程
    RubyGems 镜像
    iOS Mac系统下Ruby环境安装
    MAC机中安装RUBY环境
    Kibana+Logstash+Elasticsearch 日志查询系统
  • 原文地址:https://www.cnblogs.com/binliubiao/p/13673674.html
Copyright © 2020-2023  润新知