Redis Cluster去中心化集群
    Redis Cluster是Redis官方提供的分布式解决方案。当遇到内存、并发、流量等瓶颈时,就可以采用Cluster架构达到负载均衡目的。官方文档:https://redis.io/topics/cluster-tutorial

    1. 为什么要用redis-cluster集群?

    1.首先Redis单实例主要有单点,容量有限,流量压力上限的问题。
    Redis单点故障,可以通过主从复制replication,和自动故障转移sentinel哨兵机制。但Redis单Master实例提供写服务,仍然有容量和压力问题,因此需要数据分区,构建多个Master实例同时提供读写服务(不仅限于从replica节点提供读服务)。
    2.并发问题
    redis官方声称可以达到 10万/s,每秒执行10万条命令
    假如业务需要每秒100万的命令执行呢?
    解决方案如下:
    正确的应该是考虑分布式,加机器,把数据分到不同的位置,
    摊集中式的压力,一堆机器做一件事.还需要一定的机制保证数据分区,并且数据在各个主Master节点间不能混乱,当然最好还能支持在线数据热迁移的特性。
    为何要搭建Redis集群。Redis是在内存中保存数据的,而我们的电脑一般内存都不大,这也就意味着Redis不适合存储大数据,Redis更适合处理高并发,一台设备的存储能力是很有限的,但是多台设备协同合作,就可以让内存增大很多倍,这就需要用到集群。
    Redis集群搭建的方式有多种,例如使用客户端分片、Twemproxy、Codis等,但从redis 3.0之后版本支持redis-cluster集群,它是Redis官方提出的解决方案:
    Redis-Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接。其Redis-cluster架构图如下:
    redis-cluster集群部署 - 图1
    Redis-cluster特点:
    1.所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。
    2.客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
    3.节点的fail是通过集群中超过半数的节点检测失效时才生效。
    Redis-cluster数据分布:
    Redis集群中有16384个哈希槽,每个redis实例负责一部分slot,集群中的所有信息通过节点数据交换而更新。一个hash slot中会有很多key和value。
    数据分布存储原理:
    Redis 集群使用数据分片(sharding)来实现:Redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value 时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数(集群使用公式 CRC16(key) % 16384),这样每个key 都会对应一个编号在 0-16383 之间的哈希槽,那么redis就会把这个key 分配到对应范围的节点上了。同样,当连接三个节点任何一个节点想获取这个key时,也会这样的算法,然后内部跳转到存放这个key节点上获取数据。
    例如三个节点:哈希槽分布的值如下:
    shell
    cluster1: 0-5460
    cluster2: 5461-10922
    cluster3: 10923-16383
    这种将哈希槽分布到不同节点的做法使得用户可以很容易地向集群中添加或者删除节点。 比如说:
    - 如果用户将新节点 D 添加到集群中, 那么集群只需要将节点 A 、B 、 C 中的某些槽移动到节点 D 就可以了。
    - 如果用户要从集群中移除节点 A , 那么集群只需要将节点 A 中的所有哈希槽移动到节点 B 和节点 C , 然后再移除空白(不包含任何哈希槽)的节点 A 就可以了。
    因为将一个哈希槽从一个节点移动到另一个节点不会造成节点阻塞, 所以无论是添加新节点还是移除已存在节点, 又或者改变某个节点包含的哈希槽数量, 都不会造成集群下线。
    Redis-cluster主从模式:
    redis cluster 为了保证数据的高可用性,加入了主从模式,一个主节点对应一个或多个从节点,主节点提供数据存取,从节点则是从主节点拉取数据备份,当这个主节点挂掉后,就会有这个从节点选取一个来充当主节点,从而保证集群不会挂掉.
    1.主从切换机制
    选举过程是集群中所有master参与,如果半数以上master节点与故障节点通信超过(cluster-node-timeout),认为该节点故障,自动触发故障转移操作. #故障节点对应的从节点自动升级为主节点
    2.什么时候整个集群就不能用了?
    如果集群任意一个主节点挂掉,且当前主节点没有从节点,则集群将无法继续,因为我们不再有办法为这个节点承担范围内的哈希槽提供服务。但是,如果这个主节点和所对应的从节点同时失败,则Redis Cluster无法继续运行。
    集群部署流程:
    环境准备:
    1.准备三机器,关闭防火墙和selinux
    2.制作解析并相互做解析。
    注:规划架构两种方案,一种是单机多实例,这里我们采 用多机器部署:
    三台机器,每台机器上面两个redis实例,一个master一个slave,第一列做主库,第二列做备库
    #记得选出控制节点
    redis-1 10.0.0.130 7000、7001
    redis-2 10.0.0.128 7002、7003
    redis-3 10.0.0.131 7004、7005
    三台机器相同操作:
    注意配置文件为更改文件,不要把配置文件删除后复制粘贴。
    >mkdir /data
    >yum -y install gcc automake autoconf libtool make
    >wget https://download.redis.io/releases/redis-6.2.0.tar.gz
    #可以下载一个redis包,scp到别的服务器上去
    >tar xzvf redis-6.2.0.tar.gz -C /data/
    >cd /data/
    >mv redis-6.2.0/ redis
    >cd redis/
    >make
    >mkdir /data/redis/data
    >pwd
    /data/redis
    Redis-1服务器:
    >mkdir cluster
    >cd cluster
    >mkdir 7000 7001
    Redis-2服务器:
    >mkdir cluster
    >cd cluster
    >mkdir 7002 7003
    Redis-3服务器:
    >mkdir cluster
    >cd cluster
    >mkdir 7004 7005
    Redis-1服务器:
    >cp /data/redis/redis.conf 7000/
    >vim 7000/redis.conf
    以下是要修改的内容:
    bind 10.0.0.130
    port 7000
    daemonize yes
    pidfile /var/run/redis_7000.pid
    logfile /var/log/redis_7000.log
    dir /data/redis/data
    appendonly yes
    appendfilename “appendonly.aof”
    appendfsync everysec
    以下为打开注释并修改
    cluster-enabled yes
    cluster-config-file nodes-7000.conf
    cluster-node-timeout 5000
    cluster-replica-validity-factor 10
    cluster-migration-barrier 1
    cluster-require-full-coverage yes
    保存退出
    >cp 7000/redis.conf 7001/
    >vim 7001/redis.conf
    bind 10.0.0.130
    port 7001
    daemonize yes
    pidfile /var/run/redis_7001.pid
    logfile /var/log/redis_7001.log
    dir /data/redis/data
    appendonly yes
    appendfilename “appendonly.aof”
    appendfsync everysec
    以下为打开注释并修改
    cluster-enabled yes
    cluster-config-file nodes-7001.conf
    cluster-node-timeout 5000
    cluster-replica-validity-factor 10
    cluster-migration-barrier 1
    cluster-require-full-coverage yes
    保存退出
    >scp 7000/redis.conf 10.0.0.128:/data/redis/cluster/7002/
    >scp 7000/redis.conf 10.0.0.128:/data/redis/cluster/7003/
    >scp 7000/redis.conf 10.0.0.131:/data/redis/cluster/7004/
    >scp 7000/redis.conf 10.0.0.131:/data/redis/cluster/7005/
    在redis-2服务器:
    >cd /data/redis/cluster/
    >vim 7002/redis.conf
    bind 10.0.0.128
    port 7002
    daemonize yes
    pidfile /var/run/redis_7002.pid
    logfile /var/log/redis_7002.log
    dir /data/redis/data
    appendonly yes
    appendfilename “appendonly.aof”
    appendfsync everysec
    以下为打开注释并修改
    cluster-enabled yes
    cluster-config-file nodes-7002.conf
    cluster-node-timeout 5000
    cluster-replica-validity-factor 10
    cluster-migration-barrier 1
    cluster-require-full-coverage yes
    保存退出
    >vim 7003/redis.conf
    bind 10.0.0.130
    port 7003
    daemonize yes
    pidfile /var/run/redis_7003.pid
    logfile /var/log/redis_7003.log
    dir /data/redis/data
    appendonly yes
    appendfilename “appendonly.aof”
    appendfsync everysec
    以下为打开注释并修改
    cluster-enabled yes
    cluster-config-file nodes-7003.conf
    cluster-node-timeout 5000
    cluster-replica-validity-factor 10
    cluster-migration-barrier 1
    cluster-require-full-coverage yes
    保存退出
    在redis-3服务器上:
    >cd /data/redis/cluster/
    >vim 7004/redis.conf
    bind 10.0.0.131
    port 7004
    daemonize yes
    pidfile /var/run/redis_7004.pid
    logfile /var/log/redis_7004.log
    dir /data/redis/data
    appendonly yes
    appendfilename “appendonly.aof”
    appendfsync everysec
    以下为打开注释并修改
    cluster-enabled yes
    cluster-config-file nodes-7004.conf
    cluster-node-timeout 5000
    cluster-replica-validity-factor 10
    cluster-migration-barrier 1
    cluster-require-full-coverage yes
    保存退出
    >vim 7005/redis.conf
    bind 10.0.0.131
    port 7005
    daemonize yes
    pidfile /var/run/redis_7005.pid
    logfile /var/log/redis_7005.log
    dir /data/redis/data
    appendonly yes
    appendfilename “appendonly.aof”
    appendfsync everysec
    以下为打开注释并修改
    cluster-enabled yes
    cluster-config-file nodes-7005.conf
    cluster-node-timeout 5000
    cluster-replica-validity-factor 10
    cluster-migration-barrier 1
    cluster-require-full-coverage yes
    保存退出
    进行服务器的启动:
    在redis-1服务器上:
    >cd /data/redis/src/
    >./redis-server ../cluster/7000/redis,conf
    >./redis-server ../cluster/7001/redis.conf
    在redis-2服务器上:
    >cd /data/redis/src/
    >./redis-server ../cluster/7002/redis,conf
    >./redis-server ../cluster/7003/redis.conf
    在redis-3服务器上:
    >cd /data/redis/src/
    >./redis-server ../cluster/7004/redis,conf
    >./redis-server ../cluster/7005/redis.conf
    随后查看端口:
    >ss -antpl
    redis-cluster集群部署 - 图2
    redis-cluster集群部署 - 图3
    redis-cluster集群部署 - 图4
    随后创建集群,任意一台服务器即可:
    >cd /data/redis/src/
    >./redis-cli —cluster create —cluster-replicas 1 10.0.0.130:7000 10.0.0.130:7001 10.0.0.128:7002 10.0.0.128:7003 10.0.0.131:7004 10.0.0.131:7005
    redis-cluster集群部署 - 图5
    redis-cluster集群部署 - 图6
    测试:
    任意一个服务器:(-h 后跟使用服务器的ip地址)
    >./redis-cli -h 10.0.0.130 -c -p 7000
    >ping
    Pong
    >set name wanger
    >get name
    Wanger
    再去另一个服务器进行数据的获取:
    >redis -cli -h 10.0.0.128 -c -p 7002
    >ping
    Pong
    >get name
    Wanger
    如果有回显则代表数据可以实现一致性。
    基于以上的集群进行节点的添加:
    实验环境准备:
    新添加一台服务器
    保证网络畅通,base、epel仓库可用
    Selinux、firewalld防火墙关闭
    安装相同版本的redis
    新机器为10.0.0.129 redis-4
    部署流程:
    >mkdir /data
    >yum -y install gcc automake autoconf libtool make
    >wget https://download.redis.io/releases/redis-6.2.0.tar.gz
    #下载redis包的过程较慢的话,也可以通过别的服务器
    进行远程拷贝。
    >tar xf redis-6.2.0.tar.ge -C /data/
    >cd /data/
    >mv redis-6.2.0/ redis
    >cd redis/
    >mkdir data
    >mkdir cluster
    >mkdir cluster/{7006,7007}
    >cp redis.conf cluster/7006/
    >cp redis.conf cluster/7007/
    >vim cluster/7006/redis.conf
    修改内容为下,请勿直接删除复制,根据自己的情况进行对应的修改
    bind 10.0.0.129
    port 7006
    daemonize yes
    pidfile /var/run/redis_7006.pid
    logfile /var/log/redis_7006.log
    dir /data/redis/data
    appendonly yes
    appendfilename “appendonly.aof”
    appendfsync everysec
    以下为打开注释并修改
    cluster-enabled yes
    cluster-config-file nodes-7006.conf
    cluster-node-timeout 5000
    cluster-replica-validity-factor 10
    cluster-migration-barrier 1
    cluster-require-full-coverage yes
    保存退出
    >vim cluster/7007/redis.conf
    bind 10.0.0.129
    port 7007
    daemonize yes
    pidfile /var/run/redis_7007.pid
    logfile /var/log/redis_7007.log
    dir /data/redis/data
    appendonly yes
    appendfilename “appendonly.aof”
    appendfsync everysec
    以下为打开注释并修改
    cluster-enabled yes
    cluster-config-file nodes-7007.conf
    cluster-node-timeout 5000
    cluster-replica-validity-factor 10
    cluster-migration-barrier 1
    cluster-require-full-coverage yes
    保存退出
    启动对应节点:
    >cd /data/redis/src
    >pwd
    /data/redis/src/
    > ./redis-server ../cluster/7006/redis.conf
    > ./redis-server ../cluster/7007/redis.conf
    将7006节点添加进集群:
    ./redis-cli —cluster add-node 10.0.0.129:7006 10.0.0.130:7000
    #注意这里后面跟的不是刚刚创建的节点,而是集群内一个主节点
    >./redis-cli -h 10.0.0.130 -c -p 7000
    >cluster nodes
    #查看集群信息
    详细解释:
    runid: 该行描述的节点的id。
    ip:prot: 该行描述的节点的ip和port
    flags: 逗号分隔的标记位,可能的值有:
    1.master: 该行描述的节点是master
    2.slave: 该行描述的节点是slave
    3.fail?:该行描述的节点可能不可用
    4.fail:该行描述的节点不可用(故障)
    master_runid: 该行描述的节点的master的id,如果本身是master则显示-
    ping-sent: 最近一次发送ping的Unix时间戳,0表示未发送过
    pong-recv:最近一次收到pong的Unix时间戳,0表示未收到过
    config-epoch: 主从切换的次数
    link-state: 连接状态,connnected 和 disconnected
    hash slot: 该行描述的master中存储的key的hash的范围
    给新添加的节点添加hash槽:
    需要给新节点进行hash槽分配,这样该主节才可以存储数据(如果有数据记得提前先将数据同步然后在从其他节点迁移槽到新节点。)
    > ./redis-cli —cluster reshard 10.0.0.129:7006
    ………………..
    How many slots do you want to move (from 1 to 16384)?
    #输入你想添加得槽数量
    What is the receiving node ID?
    #输入你想添加到得节点ID
    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.
    输入:all
    然后输入yes确认
    再次查看:
    >./redis-cli -h 10.0.0.130 -c -p 7000
    >cluster nodes
    或者:
    >./redis-cli —cluster info 10.0.0.130:7000
    给新添加的主节点添加从节点:
    >cd /data/redis/src
    > ./redis-cli —cluster add-node 10.0.0.129:7007 10.0.0.129:7006 —cluster-slave —cluster-master-id 308320db4284c9b203aff1d3d9a145616856f681
    #注意后跟的是要添加从节点的master端的ID
    随后查看节点信息是否添加成功:
    >./redis-cli -h 10.0.0.130 -c -p 7000
    >cluster nodes
    或者:
    >./redis-cli —cluster info 10.0.0.130:7000
    平衡各个节点的槽值:
    任意一台集群内的服务器:
    >cd /data/redis/src
    > ./redis-cli —cluster rebalance —cluster-threshold 1 10.0.0.130:7000
    登录测试:
    >./redis-cli -h 192.168.116.175 -c -p 7007
    >ping
    Pong
    >get name #之前创建的key
    Wanger
    有值则成功
    删除节点:
    如果要下线节点6,节点7,请务必先下线从节点,并且节点6的slot的迁移到其他节点了,如果先线下节点6的话 会发产生故障切换,节点7成主节点了
    在移除某个redis节点之前,首先不能在登入该节点当中,否则不能正常移除该节点.
    退出所有链接的客户端,然后在任意一台机器执行:
    > ./redis-cli —cluster del-node 10.0.0.129:7007 dbad32bd47cc177de61109b96447d1f1ef6db2fc
    #删除的是7006主节点的从节点 后跟ID是7007
    的ID.
    进行查验:
    >./redis-cli -h 10.0.0.130 -c -p 7000
    >cluster nodes
    或者:
    >./redis-cli —cluster info 10.0.0.130:7000
    删除主节点:
    查看每个节点槽的数量:
    >./redis-cli —cluster info 10.0.0.130:7000
    将需要删除的节点的槽迁移到其他节点:
    ip+port:要移除的节点
    cluster-from:移除节点的id
    cluster-to:接受槽主节点的id,需要将4096平均移动到不同的主节点,需要写不同接受槽的主节点id
    cluster-slots:移除槽的数量
    退出所有链接的客户端,然后在任意一台机器执行:
    > ./redis-cli —cluster reshard 10.0.0.129:7006 —cluster-from 308320db4284c9b203aff1d3d9a145616856f681 —cluster-to e0370608cd33ddf5bb6de48b5627799e181de3b6 —cluster-slots 1365 —cluster-yes
    #第一个ID指定的是移除节点的id,第二个id指定的是接受槽的id,1365是迁移的槽数量。
    > ./redis-cli —cluster reshard 10.0.0.129:7006 —cluster-from 308320db4284c9b203aff1d3d9a145616856f681 —cluster-to de5b4b2f6a559362ed56d4de1e3994fd529917b5 —cluster-slots 1366 —cluster-yes
    #继续进行迁移,直到槽值清空,可以选择其他的被转移槽,保证槽值分配的平衡。
    > ./redis-cli —cluster reshard 10.0.0.129:7006 —cluster-from 308320db4284c9b203aff1d3d9a145616856f681 —cluster-to 60e3755761c9cbdacb183f59e3d6205da5335e86 —cluster-slots 1365 —cluster-yes
    随后进行查看:
    >./redis-cli -h 10.0.0.130 -c -p 7000
    >cluster nodes
    或者:
    >./redis-cli —cluster info 10.0.0.130:7000
    看到如果还有剩余槽,则继续进行槽转移,如果没有了槽,继续进行删除
    删除master节点:
    >./redis-cli —cluster del-node 10.0.0.129:7006 308320db4284c9b203aff1d3d9a145616856f681
    后跟的ID是被删除的节点对应的ID
    进行查验:
    >./redis-cli -h 10.0.0.130 -c -p 7000
    >cluster nodes
    或者:
    >./redis-cli —cluster info 10.0.0.128:7000
    注意:
    如果报错:
    >./redis-cli —cluster del-node 10.0.0.129:7006 308320db4284c9b203aff1d3d9a145616856f681
    >>> Removing node 308320db4284c9b203aff1d3d9a145616856f681 from cluster 10.0.0.129:7006
    [ERR] Node 10.0.0.129:7006 is not empty! Reshard data away and try again.
    解决方案:
    需要重新查看一下槽有没有全部移动完成。如果没有需要重新指定数量移动。这是因为还有槽不能直接移除master。
    主从切换:
    测试将某个主节点手动down掉,会不会进行主从切换:
    将集群内的7000端口down掉,查看对应的slave节点会不会自动切换:
    在reids-1服务器上进行操作:
    >ps -ef | grep redis
    或者
    >ss -antpl | grep redis
    找到对应7000的pid
    >kill -9 15591
    进行查验:
    >./redis-cli -h 10.0.0.128 -c -p 7002
    >cluster nodes
    或者:
    >./redis-cli —cluster info 10.0.0.128:7002
    随后如果自动切换再将主节点启动,查看会不会切换
    > cd /data/redis/src/
    > ./redis-server ../cluster/7000/redis.conf
    查看节点信息:
    >./redis-cli -h 10.0.0.128 -c -p 7002
    >cluster nodes
    或者:
    >./redis-cli —cluster info 10.0.0.128:7002