文章目录
- 前言
- 一、主从复制
-
- 1、主从配置
- 2、建立连接
- 3、数据同步
-
- 3.1、全量同步
- 3.2、全量同步配置
- [3.3、 增量同步](#3.3、 增量同步)
- [3.4、 增量同步配置](#3.4、 增量同步配置)
- [二、redis sentinel](#二、redis sentinel)
- [二、redis cluster](#二、redis cluster)
前言
Redis 的分布式方案主要依赖于 Redis Cluster 和 Redis Sentinel 来实现。这两种部署方案都旨在解决单点故障问题,确保数据的高可用性和持续服务能力
一、主从复制
Redis 主从复制(Master-Slave Replication)是 Redis 提供的一种数据冗余和容灾保护机制。通过主从复制,可以将主节点的数据同步到一个或多个从节点,从而实现数据的高可用性和负载均衡。
- 主节点(Master):作为数据的写入点,所有数据变更操作都是在主节点上进行的。
- 从节点(Slave):从主节点同步数据,从节点通常只提供读操作,数据从主节点复制而来。
1、主从配置
假设我们有以下主节点和从节点:
主节点 IP:192.168.1.100
从节点 IP:192.168.1.101, 192.168.1.102
在主节点(192.168.1.100)配置 redis-master.conf:
port 6379
bind 0.0.0.0
protected-mode no
在从节点(192.168.1.101 和 192.168.1.102)配置 redis-replica.conf:
port 6380
bind 0.0.0.0
protected-mode no
replicaof 192.168.1.100 6379
主节点启动
redis-server redis-master.conf
从节点启动
redis-server redis-replica.conf
2、建立连接
首次成为从节点,执行replicaof ip port命令的时候就会保存服务器的IP与端口,建立心跳机制,保持正常的连接。
通过定时任务定时监听是否变更了主节点,需要重新同步。
3、数据同步
3.1、全量同步
- master服务器收到slave的命令后(psync),判断slave传给我的master_replid(持久化在磁盘)是否跟我的replid一致,如果不一致或者传的是个空的,那么就需要全量同步。
- slave首次关联master,从主同步数据,slave肯定是没有主的replid,所以需要进行全量同步
- 全量同步步骤
- master开始执行bgsave,生成一个RDB文件,并且把RDB文件和master的replid以及offerset传输给我们的slave
- slave接收到rdb文件后,清空slave自己内存中的数据,然后用rdb来加载数据。
- master生成RDB文件是用的bgsave生成,期间,是可以接收新的指令的。那么这些指令,我们需要找一个地方保存,等到slave加载完RDB文件以后要同步给slave。
- 在master生成rdb文件期间,会接收新的指令,这些新的指令会保存在一个内存区间,这个内存区间就是 replication_buffer。
- 这个空间不能太小,如果太小,为了数据安全,会关闭跟从库的网络连接。再次连接得重新全量同步,但是问题还在,会循环进行,导致崩溃
3.2、全量同步配置
replication_buffer配置详解
client-output-buffer-limit :
-
client-type:客户端类型(normal、replica 或 pubsub)。
-
hard-limit:绝对上限值,超过此值会立即关闭连接。
-
soft-limit 和 soft-seconds:软限制值和时间窗口,超过软限制在给定时间窗口后关闭连接。
client-output-buffer-limit replica 256mb 64mb 60
示例中,将从节点客户端的输出缓冲区硬限制设置为 256 MB,软限制设置为 64 MB,时间窗口为 60 秒。
3.3、 增量同步
- 命令传播:在主节点和从节点的连接建立后,主节点会将收到的所有写命令(比如 SET、LPUSH、SADD 等)传播给从节点,从节点则执行这些命令以便与主节点保持一致。
- 复制积压缓冲区:主节点维护一个固定大小的复制积压缓冲区(replication backlog),这个缓冲区保存了最近的写命令。这是一个环形缓冲区,即缓冲区内容会根据新命令不断覆盖旧命令。当从节点与主节点之间的连接暂时中断后重新连接时,可以使用复制积压缓冲区中保存的命令进行增量同步。
- 增量同步触发:如果从节点与主节点的连接中断,并在短时间内重新建立连接,主节点会根据复制积压缓冲区找到断开期间的命令,并将这些命令发送给从节点,从节点对这些命令进行重放(replay),从而完成增量同步。
- 无效的增量同步处理:如果复制积压缓冲区中的命令过期(即缓冲区被新命令覆盖)导致无法完成增量同步,主节点和从节点会重新执行全量同步(full synchronization),具体包括发送主节点当前所有数据快照(RDB 文件)并执行新的写命令。
3.4、 增量同步配置
-
repl-backlog-size:设置主节点复制积压缓冲区的大小。默认大小是 1 MB,可以根据需求适当调整。
repl-backlog-size 1048576 # 1 MB
-
repl-backlog-ttl:设置积压缓冲区在主节点与从节点断开连接后保留的时间(单位:秒)。在这个时间内,如果从节点重新连接主节点,则主节点将尝试进行增量同步。默认值是 3600 秒(即 1 小时)
repl-backlog-ttl 3600
二、redis sentinel
1、主要功能
- 监控(Monitoring):
Sentinel 实例会持续监控主节点和从节点,检测它们是否正常运转。 - 通知(Notification):
当检测到实例发生故障时,Sentinel 可以通知系统管理员或其他配置了通知功能的应用程序。 - 自动故障转移(Automatic Failover):
当主节点被判定为故障时,Sentinel 会自动选举一个从节点提升为新的主节点,并将其他从节点指向新的主节点。 - 配置服务(Configuration Provider):
客户端可以连接 Sentinel 来获取当前主节点的地址,以便动态调整连接。
2、sentinel配置
port 26379
bind 0.0.0.0
# 监控主节点(mymaster),2 为 quorum:代表最少 2 个 Sentinel 同意主节点不可用,才会进行故障转移。
sentinel monitor mymaster <master_ip> 6379 2
# 主节点在 5 秒内无响应则认为它主观下线(SDOWN)
sentinel down-after-milliseconds mymaster 5000
# 故障转移超时时间
sentinel failover-timeout mymaster 15000
# 同时最多允许多少个从节点与主节点进行同步
sentinel parallel-syncs mymaster 1
3、高可用
3.1、故障发现
- 主观下线:
当sentinel 跟master通信时(默认1s发送ping),发现我在一定时间内(down-after-milliseconds) 没有收到master的有效的回复,就会标记成
SDOWN(Subjectively Down condition )(主观下线), - 客观下线:
主观下线后,不会触发故障转移,而是去询问其他的sentinel,其他的sentinel是否能连上master,
如果超过Quorum(规定人数)的sentinel都标记SDOWN状态,这个时候,就会将master标为ODOWN(Objectively Down condition 客观下线)
3.2、故障转移
3.2.1、选举sentinel进行故障转移
- 当状态为ODWON的时候,我们就需要去触发故障转移,如果存在多个sentinel,我们需要选一个sentinel去做故障转移这件事情,然后其他sentinel不能进行故障转移。
- 多个sentinel,我们需要选举一个sentinel来做这件事情
- Quorum如果小于等于一半,那么必须超过半数的sentinel授权, 你才能去做故障迁移,比如5台sentinel,你配置的Quorum=2,那么选举的时候必须有3(5台的一半以上)人同意
- Quorum如果大于一半,那么必须Quorum的sentinel授权,故障迁移才能启动
3.2.2、选举从节点升级成主节点
1)、与master的断开连接时间
如果slave与主服务器断开的连接时间超过主服务器配置的超时时间(down-after-milliseconds)的十倍,被认为不适合成为master。直接去除资格
2)、从节点排序
按照以下优先级排序(优先级逐一比较)
- 与主节点连接状态良好的从节点
- 复制偏移量最大(与主节点数据最接近)的从节点
- 从节点 ID 较小的从节点(保证一致性,避免冲突)
3)、排序后的第一个从节点将被提升为新的主节点
3.2.3、执行故障转移
Leader Sentinel 自动执行以下步骤,将选定的从节点提升为新的主节点:
- 转换从节点为主节点:
通过命令 SLAVEOF NO ONE - 重新配置其他从节点:
通过命令 SLAVEOF <new_master_ip> <new_master_port> 重新配置其他从节点,以让它们同步新的主节点 - 通知客户端和其他服务:
更新 Sentinel 配置,通知客户端获取新的主节点信息,以确保所有读写操作指向新的主节点
4、sentinel引来的问题
4.1、配置小于三个的sentinel
- 如果只有1个sentinel实例,则这个实例挂了就不能保证sentinel的高可用性
- 如果只有2个sentinel实例,假设ip为127.0.0.1 为 sentinel1 和127.0.0.2 为 sentine2,那 sentinel1 跟 sentinel2 的网络断开,但是他们各自还是能正常运行的。也就是我们发生了分区容错,当quorum=1时
- sentinel2检测到master不可用,因为127跟128网络断开,这个时候会触发主观下线,同时,sentinel2只能连接到1个sentinel,也满足半数以上原则(只有1个sentinel2)
- sentinel2开启故障转移,将slave升级为master,就出现了2个master,这个情况也叫作脑裂。并且客户端会连接到2个master。(客户端连的是sentinel集群,sentinel1连到1.sentinel2 连到2)
- 2个master都会写入数据,当网络恢复后,sentinel1的 master 会变成从,同步sentinel2的master数据,这个时候sentinel1的master数据就会丢失
4.2、脑裂问题
配置三个sentinel也会出现脑裂问题,这是分区容错性的特性,例如三个就有可能出现这样的情况
解决方案
-
避免双主问题
- 自动化脚本和监控报警:结合监控系统检测网络分区和双主情况,自动化脚本以便手动恢复和协调主节点。
- Automatic Fence:在检测到原主节点恢复上线时,通过脚本或其他机制自动将其降级为从节点,避免双主冲突。
-
在Redis.cfg配置,通过配置这些参数,确保在任何无从节点可用的情况下,主节点停止写入,减少脑裂的可能性。
至少有1个从节点同步到我主节点的数据,但是由于是异步同步,所以是最终一致性 不会确保有数据写入
min-replicas-to-write 1
判断上面1个的延迟时间必须小于等于10s
min-replicas-max-lag 10
二、redis cluster
Redis Cluster 是 Redis 3.0 版本后引入的一种分布式存储架构,它解决了单个 Redis 实例在面对海量数据和高并发写操作时遇到的瓶颈问题。为了实现这一点,Redis Cluster 引入了哈希槽(hash slot)的概念,并且采用了去中心化的集群管理方式,没有使用代理服务。
Cluster 一般由多个节点组成,节点数量至少为 6 个才能保证组成完整高可用的集群,其中三个为主节点,三个为从节点。三个主节点会分配槽,处理客户端的命令请求,而从节点可用在主节点故障后,顶替主节点
如图:
1、虚拟槽
Redis Cluster 采用虚拟哈希槽分区,所有的键根据哈希函数映射到 0 ~ 16383 整数槽内,计算公式:
HASH_SLOT = CRC16(key) % 16384
每一个节点负责维护一部分槽以及槽所映射的键值数据。
扩容或缩容,需要重新分配槽,也需要重新迁移数据,此过程不需要正常运行的服务下线。
此出采用 一致性hash 的算法。
假如,这里有 3 个节点的集群环境如下:
- 节点 A 哈希槽范围为 0 ~ 5500;
- 节点 B 哈希槽范围为 5501 ~ 11000;
- 节点 C 哈希槽范围为 11001 ~ 16383。
此时,我们如果要存储数据,按照 Redis Cluster 哈希槽的算法,假设结果是: CRC16(key) % 16384 = 6782。 那么就会把这个 key 的存储分配到 B 节点。此时连接 A、B、C 任何一个节点获取 key,都会这样计算,最终通过 B 节点获取数据。
假如这时我们新增一个节点 D,Redis Cluster 会从各个节点中拿取一部分 Slot 到 D 上,比如会变成这样:
- 节点 A 哈希槽范围为 1266 ~ 5500;
- 节点 B 哈希槽范围为 6827 ~ 11000;
- 节点 C 哈希槽范围为 12288 ~ 16383;
- 节点 D 哈希槽范围为 0 ~ 1265,5501 ~ 6826,11001 ~ 12287
这种特性允许在集群中轻松地添加和删除节点。同样的如果我想删除节点 D,只需要将节点 D 的哈希槽移动到其他节点,当节点是空时,便可完全将它从集群中移除。
2、数据对应
2.1、全量key计算
我们会根据key 通过CRC16 取模 16383 得到一个0到16383的值 计算公式:slot = CRC16(key) & 16383,得到的值,就代表这个key在哪个虚拟槽
例如:
set k1 1:
CRC16(k1) & 16383=11901
set k2 1:
CRC16(k2) & 16383=10
set k3 1:
CRC16(k3) & 16383=6666
那么我们k1对应的槽是11901 k2对应的槽是10 k3对应的槽是666
key跟槽的关系是根据key算出来的,后续是不能变动。
2.2、key统一映射
如果想把相关key放入一个虚拟槽,也就是一个实例节点,我们可以采用{},那么就只会根据{}里面的内容计算hash槽。
例如:
set k1{123} 1:
CRC16(123) & 16383=11901
set k2{234} 1:
CRC16(234) & 16383=10
set k3{542} 1:
CRC16(542) & 16383=6666
3、数据迁移
3.1 扩容
扩容(添加新节点)过程中,数据迁移的主要步骤包含以下几个部分:
3.1.1、启动新节点
启动新的 Redis 实例作为集群的节点:
redis-cli --cluster add-node 127.0.0.1:6380 127.0.0.1:6379
3.1.2、将新节点加入集群
使用 redis-cli 命令将新的节点加入现有集群:
redis-cli --cluster add-node 127.0.0.1:6380 127.0.0.1:6379
3.1.3、重新分配槽(resharding)
使用 redis-cli 工具重新分配哈希槽,这会触发槽和数据的迁移:
redis-cli --cluster reshard 127.0.0.1:6379
交互过程中,指定将一定数量的哈希槽从现有节点迁移到新节点。
3.1.4、迁移数据:
redis-cli 自动处理哈希槽的迁移,并在后台进行数据的移动。此迁移是渐进式的,不会中断服务。
3.2 缩容
缩容(移除节点)过程中,数据迁移的主要步骤包含以下几个部分:
3.2.1、触发哈希槽转移:
首先,确定需要移除的节点ID,获取节点信息:
redis-cli --cluster nodes 127.0.0.1:6379
3.2.2、移动哈希槽到其他节点:
使用 redis-cli 工具将指定节点的槽迁移到其他节点:
redis-cli --cluster reshard 127.0.0.1:6379 --slots SLOT_NUMBER --from NODE_ID_TO_REMOVE --to TARGET_NODE_ID
3.2.3、关闭并移除节点:
当所有哈希槽和数据迁移完成后,安全地关闭并移除节点:
redis-cli --cluster del-node 127.0.0.1:6379 NODE_ID_TO_REMOVE
4、高可用性机制
Redis Cluster 通过以下机制确保系统的高可用性:
4.1、主从复制:
- 在 Redis Cluster 中,每个主节点(master)都有一个或多个从节点(slave)作为其副本。主节点负责处理读写请求,而从节点负责复制主节点的数据。
- 从节点持续异步地从主节点复制数据,以保证当主节点发生故障时,从节点能够接管其工作。
4.2、故障检测:
- Redis Cluster 使用 gossip 协议进行节点间的状态通信,定期发送 PING 和 PONG 消息以确认节点是否存活。
- 如果某节点未能在配置的超时时间(如 5 秒)内响应 PING 消息,那么该节点将被标记为疑似失效(PFAIL)。
- 若某节点被多个其他节点标记为疑似失效且达到一定数量(如超过半数节点),该节点将被标记为真正失效(FAIL)。
5、主从切换
当主节点失效时,Redis Cluster 自动执行主从切换,以恢复集群的正常运行。主从切换的步骤如下:
5.1、选择新的主节点
- Redis Cluster 会在原主节点的从节点中选取一个新的主节点。这个选择过程会基于从节点的优先级和副本状态(如复制的偏移量)进行。
- 优先级高且数据复制进度最接近主节点的从节点将被优先选择。
5.2、将从节点提升为主节点
- 选定新的主节点后,Redis Cluster 会将这个从节点提升为主节点。这个过程中,从节点需要执行步骤以更新其角色和状态,并接管原主节点负责的哈希槽。
- 提升过程中,集群状态将在其他节点的帮助下完成同步并宣布新的主节点。
5.3、恢复数据一致性和同步
- 当新的主节点被确认后,其他从节点将更新其复制源指向新的主节点,并开始从新的主节点重新同步数据。
- 同时,客户端连接将通过 MOVED 响应被重新路由到新的主节点所在的位置。