概述
实现高可用,并且有分区功能
实现监控和自动故障恢复功能
一、创建redis容器
1.拉取镜像
docker pull redis:6.0.10
2.创建容器
创建 6 个 docker 容器
docker run -d --privileged=true -p 6381:6379 --name redis-6381 redis:6.0.10 redis-server /data/redis.conf
docker run -d --privileged=true -p 6382:6379 --name redis-6382 redis:6.0.10 redis-server /data/redis.conf
docker run -d --privileged=true -p 6383:6379 --name redis-6383 redis:6.0.10 redis-server /data/redis.conf
docker run -d --privileged=true -p 6384:6379 --name redis-6384 redis:6.0.10 redis-server /data/redis.conf
docker run -d --privileged=true -p 6385:6379 --name redis-6385 redis:6.0.10 redis-server /data/redis.conf
docker run -d --privileged=true -p 6386:6379 --name redis-6386 redis:6.0.10 redis-server /data/redis.conf
此时,是启动失败的,因为容器内没有 redis 配置文件
3.修改配置
修改 redis.conf 如下,支持集群
# bind 127.0.0.1
port 6379
cluster-enabled yes
cluster-config-file "node-6379.conf"
pidfile /var/run/redis_6379.pid
logfile ""
dbfilename dump.rdb
dir ./
protected-mode no
把 redis.conf 复制到每个节点
docker cp redis.conf redis-6381:/data/
docker cp redis.conf redis-6382:/data/
docker cp redis.conf redis-6383:/data/
docker cp redis.conf redis-6384:/data/
docker cp redis.conf redis-6385:/data/
docker cp redis.conf redis-6386:/data/
4.启动容器
docker start redis-6381 redis-6382 redis-6383 redis-6384 redis-6385 redis-6386
此时每个节点都是主节点,且没有关联
二、创建 redis 集群
进入其中一个节点,创建集群,本文redis版本为6.0
方式一
redis-cli --cluster create 172.17.0.2:6379 172.17.0.3:6379 172.17.0.4:6379 172.17.0.5:6379 172.17.0.6:6379 172.17.0.7:6379 --cluster-replicas 1
redis-cli后面跟着redis实例的地址列表,--cluster-replicas 1参数表示希望每个主服务器都有一个从服务器,redis集群会尽量把主从服务器分配在不同机器上
方式二
命令
集群
cluster info
:打印集群的信息cluster nodes
:列出集群当前已知的所有节点( node),以及这些节点的相关信息
节点
cluster meet <ip> <port>
:将 ip 和 port 所指定的节点添加到集群当中,让它成为集群的一份子cluster forget <node_id>
:从集群中移除 node_id 指定的节点cluster replicate <master_node_id>
:将当前从节点设置为 node_id 指定的master节点的slave节点。只能针对slave节点操作cluster saveconfig
:将节点的配置文件保存到硬盘里面。
槽(slot)
cluster addslots <slot> [slot ...]
:将一个或多个槽( slot)指派( assign)给当前节点cluster delslots <slot> [slot ...]
:移除一个或多个槽对当前节点的指派。cluster flushslots
:移除指派给当前节点的所有槽,让当前节点变成一个没有指派任何槽的节点。cluster setslot <slot> node <node_id>
:将槽 slot 指派给 node_id 指定的节点,如果槽已经指派给
另一个节点,那么先让另一个节点删除该槽>,然后再进行指派。cluster setslot <slot> migrating <node_id>
:将本节点的槽 slot 迁移到 node_id 指定的节点中。cluster setslot <slot> importing <node_id>
:从 node_id 指定的节点中导入槽 slot 到本节点。cluster setslot <slot> stable
:取消对槽 slot 的导入( import)或者迁移( migrate)。
键
cluster keyslot <key>
:计算键 key 应该被放置在哪个槽上。cluster countkeysinslot <slot>
:返回槽 slot 目前包含的键值对数量。cluster getkeysinslot <slot> <count>
:返回 count 个 slot 槽中的键 。
1.建立集群
我们需要将各个独立的节点连接起来,构成一个包含多个节点的集群,使用 CLUSTER MEET
命令
root@c04509c0752e:/data# redis-cli -h 172.17.0.2 -c
172.17.0.2:6379> cluster meet 172.17.0.3 6379
OK
172.17.0.2:6379> cluster meet 172.17.0.4 6379
OK
172.17.0.2:6379> cluster meet 172.17.0.5 6379
OK
172.17.0.2:6379> cluster meet 172.17.0.6 6379
OK
172.17.0.2:6379> cluster meet 172.17.0.7 6379
OK
2.分配哈希槽
redis-cli -h 172.17.0.2 cluster addslots {0..5000}
redis-cli -h 172.17.0.3 cluster addslots {5001..10000}
redis-cli -h 172.17.0.4 cluster addslots {10001..16383}
3.主从复制
redis-cli -h 172.17.0.6 cluster replicate 09756ea21494c4ef967fd0e788371aa2d77a10cc
redis-cli -h 172.17.0.7 cluster replicate ce1ed80a4a0bc79eb6788c134d49470b2064ec07
redis-cli -h 172.17.0.5 cluster replicate 732aeb97724583ba807713ac9097bff18cd37a2a
三、查看集群
任意节点执行
redis-cli -h 172.17.0.2 cluster nodes
结果:
可以看出,有三对主从节点。主节点 172.17.0.2,从节点 172.17.0.6;主节点 172.17.0.3,从节点 172.17.0.7;主节点 172.17.0.4,从节点 172.17.0.5
一共 16383 个哈希槽,分配到三个主节点,172.17.0.2 是 0-5460,172.17.0.3 是 5461-10922,172.17.0.4 是 10923-16383。从节点是不分配哈希槽的
c6a2e9648c3ea70e9deb53411e97956335be7dad 172.17.0.8:6379@16379 master - 0 1645516603120 0 connected
6a9d139c3cc5a683a7b8586ab4d46246f587497a 172.17.0.5:6379@16379 slave 732aeb97724583ba807713ac9097bff18cd37a2a 0 1645516602000 3 connected
732aeb97724583ba807713ac9097bff18cd37a2a 172.17.0.4:6379@16379 master - 0 1645516602117 3 connected 10923-16383
2bade4248dcf270fc6abc4a952ebcf0933c2b9ac 172.17.0.7:6379@16379 slave ce1ed80a4a0bc79eb6788c134d49470b2064ec07 0 1645516601000 2 connected
d01c5c39ea96bf842ef3186792faa58508d620be 172.17.0.6:6379@16379 slave 09756ea21494c4ef967fd0e788371aa2d77a10cc 0 1645516601000 1 connected
09756ea21494c4ef967fd0e788371aa2d77a10cc 172.17.0.2:6379@16379 myself,master - 0 1645516602000 1 connected 0-5460
ce1ed80a4a0bc79eb6788c134d49470b2064ec07 172.17.0.3:6379@16379 master - 0 1645516601114 2 connected 5461-10922
四、操作集群
redis-cli -h 172.17.0.2 -c
-c
表示 连接RedisCluster的参数,可以防止moved和ask异常
五、添加节点
1.添加主节点
redis-cli --cluster add-node <新增节点ip:port> <现存节点ip:port>
<现存节点 ip:port>
可以取当前集群中任一节点的 IP 和端口号,此时没有分配哈希槽,是不会存值到新节点的
例如:
redis-cli --cluster add-node 172.17.0.8:6379 172.17.0.2:6379
2.分配哈希槽
redis-cli --cluster reshard 172.17.0.2:6379
根据提示分配输入分配哈希槽个数
3.添加从节点
redis-cli --cluster add-node <新增从节点ip:port> <现存节点ip:port> --cluster-slave --cluster-master-id <主节点ID>
<现存节点ip:port>
可以取当前集群中任一节点的 IP 和端口号
例如:
redis-cli --cluster add-node 172.17.0.9:6379 172.17.0.2:6379 --cluster-slave --cluster-master-id c6a2e9648c3ea70e9deb53411e97956335be7dad
六、删除节点
redis-cli --cluster del-node <任一节点ip:port> <被删除节点ID>
对于从节点,可以直接删除;对于主节点,哈希槽移动到其他主节点上,才可以删除它,否则会提示删除失败
七、节点间的内部通信机制
1.集中式、Gossip 协议
集群元数据的维护有两种方式:集中式、Gossip 协议。 redis cluster 节点间采用 gossip 协议进行通信。
gossip 协议包含多种消息,包含 ping,pong,meet,fail 等等。
- meet:某个节点发送 meet 给新加入的节点,让新节点加入集群中,然后新节点就会开始与其它节点进行通信。
- ping:每个节点都会频繁给其它节点发送 ping,其中包含自己的状态还有自己维护的集群元数据,互相通过 ping 交换元数据。
- pong:返回 ping 和 meeet,包含自己的状态和其它信息,也用于信息广播和更新。
- fail:某个节点判断另一个节点 fail 之后,就发送 fail 给其它节点,通知其它节点说,某个节点宕机啦。
redis cluster 每个节点每秒会执行 10 次 ping,每次会选择 5 个最久没有通信的其它节点。
2.redis 节点间通信端口
在 redis cluster 架构下,每个 redis 要放开两个端口号,比如一个是 6379,另外一个就是 加1w 的端口号,比如 16379。
cluster bus 使用 gossip 协议,用于节点间进行高效的数据交换,占用更少的网络带宽和处理时间。
在 redis cluster 架构下,每个节点每隔一段时间都会往另外几个节点发送 ping 消息,同时其它几个节点接收到 ping 之后返回 pong。所有节点都持有一份元数据,不同的节点如果出现了元数据的变更,就不断将元数据发送给其它的节点,让其它节点也进行元数据的变更。
八、一致性 hash 算法
一致性 hash 算法将整个 hash 值空间组织成一个虚拟的圆环,整个空间按顺时针方向组织,下一步将各个 master 节点(使用服务器的 ip 或主机名)进行 hash。这样就能确定每个节点在其哈希环上的位置。
来了一个 key,首先计算 hash 值,并确定此数据在环上的位置,从此位置沿环顺时针“行走”,遇到的第一个 master 节点就是 key 所在位置。
在一致性哈希算法中,如果一个节点挂了,受影响的数据仅仅是此节点到环空间前一个节点(沿着逆时针方向行走遇到的第一个节点)之间的数据,其它不受影响。增加一个节点也同理。
一致性哈希算法在节点太少时,容易因为节点分布不均匀而造成缓存热点的问题。为了解决这种热点问题,一致性 hash 算法引入了虚拟节点机制,即对每一个节点计算多个 hash,每个计算结果位置都放置一个虚拟节点。这样就实现了数据的均匀分布,负载均衡。
redis cluster 有固定的 16384 个 hash slot,对每个 key 计算 CRC16 值,然后对 16384 取模,可以获取 key 对应的 hash slot。
redis cluster 中每个 master 都会持有部分 slot,hash slot 让 node 的增加和移除很简单,增加一个 master,就将其他 master 的 hash slot 移动部分过去,减少一个 master,就将它的 hash slot 移动到其他 master 上去。
任何一台机器宕机,另外两个节点,不影响的。因为 key 找的是 hash slot,不是机器。
九、redis cluster 的高可用与主备切换
1.判断节点宕机
如果一个节点认为另外一个节点宕机,那么就是 pfail,主观宕机。如果多个节点都认为另外一个节点宕机了,那么就是 fail,客观宕机,跟哨兵的原理几乎一样,sdown,odown。
在 cluster-node-timeout 内,某个节点一直没有返回 pong,那么就被认为 pfail。
如果一个节点认为某个节点 pfail 了,那么会在 gossip ping 消息中,ping 给其他节点,如果超过半数的节点都认为 pfail 了,那么就会变成 fail。
2.从节点过滤
对宕机的 master node,从其所有的 slave node 中,选择一个切换成 master node。
检查每个 slave node 与 master node 断开连接的时间,如果超过了 cluster-node-timeout * cluster-slave-validity-factor,那么就没有资格切换成 master。
3.从节点选举
每个从节点,都根据自己对 master 复制数据的 offset,来设置一个选举时间,offset 越大(复制数据越多)的从节点,选举时间越靠前,优先进行选举。
所有的 master node 开始 slave 选举投票,给要进行选举的 slave 进行投票,如果大部分 master node(N/2 + 1)都投票给了某个从节点,那么选举通过,那个从节点可以切换成 master。
- 每个slave会与自己的master通讯,当slave发现自己的master变为fail时。每个slave都会参与竞争,推举自己为master。
- 增加currentEpoch的值,并且每个slave向集群中的其他所有节点广播FAILOVER_AUTH_REQUEST。
- 其他master会受到多个slave的广播,但是只会给第一个slave回复FAILOVER_AUTH_ACK。
- slave接收到ack之后,会使用过半机制开始统计。即:当前有多少master给自己ack,如果超过一半的master发送ack,则成为master。
- 广播PONG,通知给集群中的其节点。
4.替换主节点
- 当前从节点取消复制变成主节点。(slaveof no one)
- 执行cluster del slot撤销故障主节点负责的槽,并执行cluster add slot把这些槽分配给自己
- 向集群广播自己的pong消息,表明已经替换了故障从节点