系统环境:
在 Redis 3.0 版本后正式推出 Redis 集群模式,该模式是 Redis 的分布式的解决方案,是一个提供在多个 Redis 节点间共享数据的程序集,且 Redis 集群是去中心化的,它的每个 Master 节点都可以进行读写数据,每个节点都拥有平等的关系,每个节点都保持各自的数据和整个集群的状态。
Redis 集群设计的主要目的是让 Redis 数据存储能够线性扩展,通过分区来提供一定程度的可用性,在实际环境中当某个节点宕机或者不可达的情况下其可以继续处理命令。但是,如果发生较大故障(例如,大多数主站不可用时)时集群会停止运行,即 redis 集群不能保证数据的强一致性。
Redis 集群没有使用一致性 Hash, 而是引入了”哈希槽”的概念。Redis 集群有 16384 个哈希槽,每个 key 通过 CRC16 校验后对 16384 取模来决定放置哪个槽,集群的每个节点负责一部分 hash 槽。
举个例子,比如当前集群有3个节点,那么:
这种结构很容易”添加”或者”删除”节点. 比如如果我想新添加个节点 D,我需要从节点 A, B, C 中得部分槽转移到节点 D 上, 如果我想移除节点 A,则需要将 A 中的槽移到 B 和 C 节点上,然后将没有任何槽的 A 节点从集群中移除即可。由于从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态。
Redis 支持多个 key 操作,只要这些 key 在一个单个命令中执行(或者一个事务,或者 Lua 脚本执行),那么它们就属于相同的 Hash 槽。你也可以用 hash tags 命令强制多个 key 都在相同的 hash 槽中。
在 Redis 集群模式下,为了防止集群部分节点因宕机等情况造成不可用,故而 Redis 集群使用了主从复制模式。在该模式下要求 Redis 集群至少要存在六个节点,其中三个节点为主节点,能够对外提供读写。还有三个节点为从节点,会同步其对应的主节点的数据。当某个主节点出现问题不可用时,Redis 将通过选举算法从主节点对应的从节点中选择一个节点(主节点存在多个从节点的情况下),将其更改为一个新的主节点,且能够对外提供服务。
例如,在存在 A,B,C 三个主节点和其对应的 (A1、A2),(B1、B2),(C1、C2) 六个从节点,共九个节点中,如果节点 A 节点挂掉,那么其对应的从节点 A1、A2 节点将通过选举算法,选择其中一个节点提升为主节点,以确保集群能够正常服务。不过当 A1、A2 两个从节点或者或者半数以上主节点不可用时,那么集群也是不可用的。
在部署 Redis 集群模式时,至少需要六个节点组成集群才能保证集群的可用性。
(1)、全量复制与增量复制
在 Redis 主从复制中,分为”全量复制”和”增量复制”两种数据同步方式:
(2)、记录复制位置的偏移量
info replication
命令查询 master_repl_offset
偏移量信息。info replication
命令查询 slave_repl_offset
偏移量信息。(3)、复制积压缓冲区
(4)、节点运行的 ID
(1)、psync 命令
(2)、psync 触发全量复制
从节点使用 psync 命令完成部分复制和全量复制功能:
当从节点第一次连接主节点时候,执行全量复制:
(2)、psync 触发增量复制
当从节点与主节点发送中断后,从节点会重新连接主节点,这时会触发增量复制,过程如下:
Redis 在官方文档中提及,其并不能保证数据的强一致性,即 Redis 集群在特定的条件下写入的数据可能会丢失,主要原因是因为 Redis 集群使用了异步复制模式,其写入的操作过程如下:
执行顺序如下:
Redis 集群对性能和一致性之间做出权衡,设置主节点在接收到客户端写命令后再将执行的命令发送到各个从节点进行同步,这个过程是异步操作,且主节点不会等待从节点回复信息就立即回复客户端命令的执行状态。这样减少同步操作,可以很大提高系统的执行速度,避免等待从节点回复消息这个过程成为系统性能的瓶颈。
然而,因为主节点不等待从节点收到信息后进行回复,就将命令的执行状态回复给了客户端,那么在节点出现问题时很可能导致出现数据丢失问题,比如客户端向主节点发送一条命令,然后主节点将该命令异步发送到它的从节点进行数据备份,然后立即回复客户端命令的执行状态。如果在这个过程中,主节点出现问导致宕机,且从节点在处理主节点发送过来的同步数据时,也发生错误。这时正好赶上主节点宕机等不可用情况,那么从节点将转换为新的主节点,在之前主节点执行的命令将丢失。
在 Redis 集群中,数据节点提供两个 TCP 端口,在配置防火墙时需要同时开启下面两类端口:
集群的节点之间通讯采用 Gossip 协议,节点根据固定频率(每秒10次)定时任务进行判断,当集群状态发生变化,如增删节点、槽状态变更时,会通过节点间通讯同步集群状态,使集群收敛。集群间发送的 Gossip 消息有下面五种消息类型:
在 Redis 集群模式下也不可能百分百保证集群可用性,当发生不可预知的事件导致 Redis 集群将进入失败状态,在这种状态下 Redis 集群将不能正常提供服务。其中进入失败状态的条件主要为:
Redis 集群重新分片(新增/移除节点)机制:
但这些操作是需要手动完成的,可以在不停止服务器的情况下执行。
我们即将创建一个示例集群部署。在继续之前,让我们介绍Redis Cluster在redis.conf文件中引入的配置参数。
cluster-node-timeout
*
cluster-slave-validity-factor
得到的时间,是从节点与主节点失联后,此从节点数据有效的最长时间,超过这个时间,从节点不会启动故障迁移。假设 cluster-node-timeout=5
,cluster-slave-validity-factor=10
,则如果从节点跟主节点失联超过50秒,此从节点不能成为主节点。这里对待部署的 Redis 集群的节点进行分配,将其部署到不同的机器上,安排如下:
提前创建好用于存储 Redis 的配置文件和持久化数据的目录:
第一台服务器 192.168.2.11
中执行创建存储目录命令:
$ mkdir -p /var/lib/redis/7000 & mkdir -p /var/lib/redis/7003
第二台服务器 192.168.2.12
中执行创建存储目录命令:
$ mkdir -p /var/lib/redis/7001 & mkdir -p /var/lib/redis/7004
第三台服务器 192.168.2.13
中执行创建存储目录命令:
$ mkdir -p /var/lib/redis/7002 & mkdir -p /var/lib/redis/7005
第一台服务器 192.168.2.11
配置文件:
## 7000 端口配置文件
$ cat > /var/lib/redis/7000/redis.conf << EOF
port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
daemonize no
protected-mode no
pidfile /data/redis.pid
EOF
## 7003 端口配置文件
$ cat > /var/lib/redis/7003/redis.conf << EOF
port 7003
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
daemonize no
protected-mode no
pidfile /data/redis.pid
EOF
第二台服务器 192.168.2.12
配置文件
## 7001 端口配置:redis.conf
$ cat > /var/lib/redis/7001/redis.conf << EOF
port 7001
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
daemonize no
protected-mode no
pidfile /data/redis.pid
EOF
## 7004 端口配置:redis-7004.conf
$ cat > /var/lib/redis/7004/redis.conf << EOF
port 7004
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
daemonize no
protected-mode no
pidfile /data/redis.pid
EOF
第三台服务器 192.168.2.13
配置文件
## 7002 端口配置:redis-7002.conf
$ cat > /var/lib/redis/7002/redis.conf << EOF
port 7002
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
daemonize no
protected-mode no
pidfile /data/redis.pid
EOF
## 7005 端口配置:redis-7005.conf
$ cat > /var/lib/redis/7005/redis.conf << EOF
port 7005
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
daemonize no
protected-mode no
pidfile /data/redis.pid
EOF
三台服务器中提前拉取 Redis 镜像,避免在执行运行时候还得拉取镜像,导致运行慢。
$ docker pull redis:6.0.8
三台服务器分别执行 Docker 运行命令来启动 Redis 镜像。这里需要注意的是,不同服务器间的 Docker 是不能相互通信的,所有这里我们设置启动的容器网络模式为 host 模式,这样容器不会创建虚拟网卡,而是使用宿主机的网络。
第一台服务器 192.168.2.11
执行如下命令
## 运行 Redis 镜像 7000 端口
$ docker run -d -v /var/lib/redis/7000:/data \
--cpus=1 --memory=2GB --memory-swap=0 \
--privileged=true \
--restart=always \
--net host \
--name redis-7000 \
redis:6.0.8 redis-server /data/redis.conf
## 运行 Redis 镜像 7003 端口
$ docker run -d -v /var/lib/redis/7003:/data \
--cpus=1 --memory=2GB --memory-swap=0 \
--privileged=true \
--restart=always \
--net host \
--name redis-7003 \
redis:6.0.8 redis-server /data/redis.conf
第二台服务器 192.168.2.12
执行如下命令
## 运行 Redis 镜像 7001 端口
$ docker run -d -v /var/lib/redis/7001:/data \
--cpus=1 --memory=2GB --memory-swap=0 \
--privileged=true \
--restart=always \
--net host \
--name redis-7001 \
redis:6.0.8 redis-server /data/redis.conf
## 运行 Redis 镜像 7004端口
$ docker run -d -v /var/lib/redis/7004:/data \
--cpus=1 --memory=2GB --memory-swap=0 \
--privileged=true \
--restart=always \
--net host \
--name redis-7004 \
redis:6.0.8 redis-server /data/redis.conf
第三台服务器 192.168.2.13
执行如下命令:
## 运行 Redis 镜像 7002 端口
$ docker run -d -v /var/lib/redis/7002:/data \
--cpus=1 --memory=2GB --memory-swap=0 \
--privileged=true \
--restart=always \
--net host \
--name redis-7002 \
redis:6.0.8 redis-server /data/redis.conf
## 运行 Redis 镜像 7005 端口
$ docker run -d -v /var/lib/redis/7005:/data \
--cpus=1 --memory=2GB --memory-swap=0 \
--privileged=true \
--restart=always \
--net host \
--name redis-7005 \
redis:6.0.8 redis-server /data/redis.conf
随意进入一台服务器,使用 Redis 镜像的 redis-cli 工具执行创建集群命令使各个 Redis 组成集群,这里本人进入第一台服务器 192.168.2.11
中,使用端口为 7000
的 Redis
端镜像,可以执行下面命令:
$ docker exec -it redis-7000 \
redis-cli -p 7000 --cluster create \
192.168.2.11:7000 192.168.2.12:7001 192.168.2.13:7002 \
192.168.2.11:7003 192.168.2.12:7004 192.168.2.13:7005 \
--cluster-replicas 1
然后会看到下面信息:
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 192.168.2.12:7004 to 192.168.2.11:7000
Adding replica 192.168.2.13:7005 to 192.168.2.12:7001
Adding replica 192.168.2.11:7003 to 192.168.2.13:7002
M: 5e50824c55d4df42db4d2987796f0c0b468c273f 192.168.2.11:7000
slots:[0-5460] (5461 slots) master
M: 36565e0273fd62921aa1f2d85c5f7ac98a5b9466 192.168.2.12:7001
slots:[5461-10922] (5462 slots) master
M: 0cc1aaf960defae7332e9256dd25ee5e5c99e65f 192.168.2.13:7002
slots:[10923-16383] (5461 slots) master
S: 42d6e3979395ba93cd1352b6d17044f6b25d9379 192.168.2.11:7003
replicates 0cc1aaf960defae7332e9256dd25ee5e5c99e65f
S: ac5d34b57a8f73dabc60d3a56469055ec64fcde7 192.168.2.12:7004
replicates 5e50824c55d4df42db4d2987796f0c0b468c273f
S: 470b7ff823f10a309fb07311097456210506f6d8 192.168.2.13:7005
replicates 36565e0273fd62921aa1f2d85c5f7ac98a5b9466
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
.
>>> Performing Cluster Check (using node 192.168.2.11:7000)
M: 5e50824c55d4df42db4d2987796f0c0b468c273f 192.168.2.11:7000
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 470b7ff823f10a309fb07311097456210506f6d8 192.168.2.13:7005
slots: (0 slots) slave
replicates 36565e0273fd62921aa1f2d85c5f7ac98a5b9466
S: 42d6e3979395ba93cd1352b6d17044f6b25d9379 192.168.2.11:7003
slots: (0 slots) slave
replicates 0cc1aaf960defae7332e9256dd25ee5e5c99e65f
S: ac5d34b57a8f73dabc60d3a56469055ec64fcde7 192.168.2.12:7004
slots: (0 slots) slave
replicates 5e50824c55d4df42db4d2987796f0c0b468c273f
M: 0cc1aaf960defae7332e9256dd25ee5e5c99e65f 192.168.2.13:7002
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
M: 36565e0273fd62921aa1f2d85c5f7ac98a5b9466 192.168.2.12:7001
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
进入 Redis 镜像内部并折佣 redis-cli 命令:
$ docker exec -it redis-7000 redis-cli -p 7000 -c
查看集群信息:
> 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:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:866
cluster_stats_messages_pong_sent:854
cluster_stats_messages_sent:1720
cluster_stats_messages_ping_received:849
cluster_stats_messages_pong_received:866
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:1720
查看集群节点信息:
> cluster nodes
470b7ff823f10a309fb07311097456210506f6d8 192.168.2.13:7005@17005 slave 36565e0273fd62921aa1f2d85c5f7ac98a5b9466 0 1600267217000 2 connected
42d6e3979395ba93cd1352b6d17044f6b25d9379 192.168.2.11:7003@17003 slave 0cc1aaf960defae7332e9256dd25ee5e5c99e65f 0 1600267218171 3 connected
ac5d34b57a8f73dabc60d3a56469055ec64fcde7 192.168.2.12:7004@17004 slave 5e50824c55d4df42db4d2987796f0c0b468c273f 0 1600267216161 1 connected
0cc1aaf960defae7332e9256dd25ee5e5c99e65f 192.168.2.13:7002@17002 master - 0 1600267218070 3 connected 10923-16383
36565e0273fd62921aa1f2d85c5f7ac98a5b9466 192.168.2.12:7001@17001 master - 0 1600267217163 2 connected 5461-10922
5e50824c55d4df42db4d2987796f0c0b468c273f 192.168.2.11:7000@17000 myself,master - 0 1600267217000 1 connected 0-5460
作者:超级小豆丁
http://www.mydlq.club/article/93
参考地址: