🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] ## 对redis集群架构的理解 redis cluster 着眼于可扩展。当单个redis不足时,使用cluster进行分片存储。 >Redis Cluster 使用分片机制,在内部分为 16384 个 slot 插槽,分布在所有 master 节点上,每个 master 节点负责一部分 slot。数据操作时按 key 做 CRC16 来计算在哪个 slot,由哪个 master 进行处理。数据的冗余是通过 slave 节点来保障。 Redis 的高可用。Redis 支持主从同步,提供 Cluster 集群部署模式,通过 Sentine l哨兵来监控 Redis 主服务器的状态。当主挂掉时,在从节点中根据一定策略选出新主,并调整其他从 slaveof 到新主。 选主的策略简单来说有三个: >* slave 的 priority 设置的越低,优先级越高; >* 同等情况下,slave 复制的数据越多优先级越高; >* 相同的条件下 runid 越小越容易被选中。 在 Redis 集群中,sentinel 也会进行多实例部署,sentinel 之间通过 Raft 协议来保证自身的高可用。 # 集群方案 https://mp.weixin.qq.com/s/p5WCAA10OLxKAsE5CrD09w https://mp.weixin.qq.com/s/fBShKZbuR54yaIzzMR3R7g https://mp.weixin.qq.com/s/_54mD0EiS9R3CvpDXzN0lg https://mp.weixin.qq.com/s?__biz=MzkzMDI1NjcyOQ==&mid=2247487789&idx=1&sn=7f8245f8b4e4a98aa0a717011f7b7e24&source=41#wechat_redirect cluster https://blog.51cto.com/lxw1844912514/2943798 三种模式,包括配置 ## 主从模式 主从模式中,Redis部署了多台机器,有主节点,负责读写操作,有从节点,只负责读操作。从节点的数据来自主节点,实现原理就是**主从复制机制** > 全量复制的时候,数据量很大时,就会对主从节点和网络造成很大的开销,也就是常说的`复制风暴` 主从复制包括全量复制,增量复制两种。一般当slave第一次启动连接master,或者认为是第一次连接,就采用**全量复制**,全量复制流程如下: ![](https://img.kancloud.cn/d6/ce/d6ced10065d88c8db0dc848ecd2ab6bd_1080x853.png) ~~~ 1.slave发送psync命令到master。 2.master接收到SYNC命令后,返回runid和offect偏移量 3.从节点保存主节点的runid和偏移量 4.执行bgsave命令,生成RDB全量文件。 5.master使用缓冲区,记录RDB快照生成期间的所有写命令。 6.master执行完bgsave后,向所有slave发送RDB快照文件。 7.slave收到RDB快照文件后,载入、解析收到的快照。 8.master向slave发送缓冲区中的写命令; 9.salve接受命令请求,并执行来自master缓冲区的写命令 ~~~ ### 主从复制的作用: * 数据冗余,实现数据的热备份 * 故障恢复,避免单点故障带来的服务不可用 * 读写分离,负载均衡。主节点负载读写,从节点负责读,提高服务器并发量 * 高可用基础,是哨兵机制和集群实现的基础 ### 优点 * Master/Slave 角色方便水平扩展,QPS 增加,增加 Slave 即可; * 降低 Master 读压力,转交给 Slave 节点; * 主节点宕机,从节点作为主节点的备份可以随时顶上继续提供服务; ### 缺点 * 可靠性保证不是很好,主节点故障便无法提供写入服务; * 没有解决主节点写的压力; * 数据冗余(为了高并发、高可用和高性能,一般是允许有冗余存在的); * **一旦主节点宕机,从节点晋升成主节点,需要修改应用方的主节点地址,还需要命令所有从节点去复制新的主节点,整个过程需要人工干预;** * 主节点的写能力受到单机的限制; * 主节点的存储能力受到单机的限制。 ## 哨兵模式 sentinel模式是建立在主从模式的基础上,只是增加了哨兵集群 ![](https://img.kancloud.cn/fd/4a/fd4abd1067d135980b55c47de672e917_1080x715.jpg) 每个sentinel以每秒钟一次的频率向它所知的master,slave以及其他sentinel实例发送一个 PING 命令 ### 哨兵机制 * 如果一个实例距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被sentinel标记为主观下线。 * 如果一个master被标记为主观下线,则正在监视这个master的所有sentinel要以每秒一次的频率确认master的确进入了主观下线状态 * 当有足够数量的sentinel(大于等于配置文件指定的值)在指定的时间范围内确认master的确进入了主观下线状态, 则master会被标记为客观下线 * 在一般情况下, 每个sentinel会以每 10 秒一次的频率向它已知的所有master,slave发送 INFO 命令 * 当master被sentinel标记为客观下线时,sentinel向下线的master的所有slave发送 INFO 命令的频率会从 10 秒一次改为 1 秒一次 * 若没有足够数量的sentinel同意master已经下线,master的客观下线状态就会被移除; * 若master重新向sentinel的 PING 命令返回有效回复,master的主观下线状态就会被移除 ### 哨兵的作用或能力 * 集群监控,即时刻监控着redis的master和slave进程是否是在正常工作。 * 消息通知,就是说当它发现有redis实例有故障的话,就会发送消息给管理员 * 故障自动转移,如果redis master 节点宕机了的话,它就会将请求转到slave 节点上,slave升为master。 * 充当配置中心,如果发生了故障转移,它会通知将master的新地址写在配置中心告诉客户端。 ### 故障转移流程 * 第一步:在已下线主节点(旧主节点)属下的所有「从节点」里面,挑选出一个从节点,并将其转换为主节点。 * 第二步:让已下线主节点属下的所有「从节点」修改复制目标,修改为复制「新主节点」; * 第三步:将新主节点的 IP 地址和信息,通过「发布者/订阅者机制」通知给客户端; * 第四步:继续监视旧主节点,当这个旧主节点重新上线时,将它设置为新主节点的从节点; ### 故障转移由哨兵leader来完成,成为leader的条件? (候选者:最初认为master下线的哨兵) * 第一,拿到半数以上的赞成票; * 第二,拿到的票数同时还需要大于等于哨兵配置文件中的 quorum 值。 ### 哨兵之间是如何知道彼此? 哨兵与 master 建立通信,利用 master 提供发布/订阅机制发布自己的信息,比如IP、端口…… master 有一个`__sentinel__:hello`的专用通道,用于哨兵之间发布和订阅消息。**这就好比是`__sentinel__:hello`微信群,哨兵利用 master 建立的微信群发布自己的消息,同时关注其他哨兵发布的消息**。 ### 哨兵之间虽然建立连接了,如何知道 slave 并监控他们的? 关键还是利用 master 来实现,哨兵向 master 发送`INFO`命令, master 掌门自然是知道自己门下所有的 salve 小弟的。所以 master 接收到命令后,便将 slave 列表告诉哨兵。 哨兵根据 master 响应的 slave 名单信息与每一个 salve 建立连接,并且根据这个连接持续监控哨兵。 ### 定时任务   Sentinel 内部有 3 个定时任务,分别是: * 每 1 秒每个 Sentinel 对其他 Sentinel 和 Redis 节点执行`PING`操作(监控),这是一个**心跳检测**,是失败判定的依据。 * 每 2 秒每个 Sentinel 通过 Master 节点的 channel 交换信息(Publish/Subscribe); * 每 10 秒每个 Sentinel 会对 Master 和 Slave 执行`INFO`命令,这个任务主要达到两个目的: * 发现 Slave 节点; * 确认主从关系。 Redis 哨兵机制如何实现故障自动转移? ### sentinel 集群通过主观下线和客观下线判断redis节点是否失效 默认情况下,每个 Sentinel 节点会以每秒一次的频率对Redis 节点和其它的Sentinel 节点发送 PING 命令,并通过节点的 回复 来判断节点是否在线。 * 主观下线 主观下线 适用于所有 主节点 和 从节点。如果在 down-after-milliseconds 毫秒内,Sentinel 没有收到 目标节点 的有效回复,则会判定 该节点 为 主观下线。 * 客观下线 客观下线 只适用于 主节点。如果 主节点 出现故障,Sentinel 节点会通过 sentinel is-master-down-by-addr 命令,向其它 Sentinel 节点询问对该节点的 状态判断。如果超过 个数的节点判定 主节点 不可达,则该 Sentinel 节点会判断 主节点为客观下线。 当判断某个Redis节点是客观下线后,Sentinel会把master转移到另外的slave节点,让它充当新的master接受请求,从而保证高可用性。 ### 优点  * 哨兵模式是基于主从模式的,所有主从的优点,哨兵模式都有; * 主从可以自动切换,系统更健壮,可用性更高; * Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。 ### 缺点 * 主从切换需要时间,会丢失数据; * 还是没有解决主节点写的压力; * 主节点的写能力,存储能力受到单机的限制; * 动态扩容困难复杂,对于集群,容量达到上限时在线扩容会变得很复杂。 ### 什么是脑裂 redis的主从模式下脑裂是指因为网络问题,导致redis master节点跟redis slave节点和sentinel集群处于不同的网络分区,此时因为sentinel集群无法感知到 master 的存在,就会将某一个 slave 节点提升为 master 节点。此时就存在两个不同的 master节点,就像一个大脑分裂成了两个。 集群脑裂问题中,如果客户端还在基于原来的 master 节点继续写入数据,那么新的master 节点将无法同步这些数据,当网络问题解决之后,sentinel 集群将原先的master节点降为 slave 节点,此时再从新的 master 中同步数据,将会造成大量的数据丢失。 ### 脑裂的解决方案 min-slaves-to-write 3 min-slaves-max-lag 10 第一个参数表示至少 master 要有1个slave节点,才允许写入 第二个参数表示 slave 连接到 master 的最大延迟时间 ## Cluster集群模式 ### 什么是 Cluster 集群 Redis 集群是一种分布式数据库方案,集群通过分片(sharding)来进行数据管理(「分治思想」的一种实践),并提供复制和故障转移功能。 将数据划分为 16384 的 slots,每个节点负责一部分槽位。槽位的信息存储于每个节点中。 它是去中心化的,如图所示,该集群有三个 Redis 节点组成,每个节点负责整个集群的一部分数据,每个节点负责的数据多少可能不一样。 ![](https://img.kancloud.cn/ab/86/ab86f5626bc5e53a0a94907adbe6efac_1080x836.webp) 三个节点相互连接组成一个对等的集群,它们之间通过`Gossip`协议相互交换集群信息,最后每个节点都保存着其他节点的 slots 分配情况。 一般情况下,节点的负责的slot的数量是 16384/N 个, 还可以使用`cluster addslots`命令,指定每个实例上的哈希槽个数 ![](https://img.kancloud.cn/51/ef/51ef9ad68a5ab385a90c91513c5c0eee_760x309.png) ### Cluster 如何实现故障转移? Redis 集群节点采用`Gossip`协议来广播自己的状态以及自己对整个集群认知的改变。比如一个节点发现某个节点失联了 (PFail),它会将这条信息向整个集群广播,其它节点也就可以收到这点失联信息。 如果一个节点收到了某个节点失联的数量 (PFail Count) 已经达到了集群的大多数,就可以标记该节点为确定下线状态 (Fail),然后向整个集群广播,强迫其它节点也接收该节点已经下线的事实,并立即对该失联节点进行主从切换。 ### 客户端又怎么确定访问的数据分布在哪个实例上呢? Redis 实例会将自己的哈希槽信息通过 Gossip 协议发送给集群中其他的实例,实现了哈希槽分配信息的扩散。 这样,集群中的每个实例都有所有哈希槽与实例之间的映射关系信息。 ### 什么是 Redis 重定向机制? 哈希槽与实例之间的映射关系由于新增实例或者负载均衡重新分配导致改变了,**客户端将请求发送到实例上,这个实例没有相应的数据,该 Redis 实例会告诉客户端将请求发送到其他的实例上**。 Redis 通过 MOVED 错误和 ASK 错误告诉客户端。 > MOVED **MOVED**错误(负载均衡,数据已经迁移到其他实例上):当客户端将一个键值对操作请求发送给某个实例,而这个键所在的槽并非由自己负责的时候,该实例会返回一个 MOVED 错误指引转向正在负责该槽的节点。 同时,**客户端还会更新本地缓存,将该 slot 与 Redis 实例对应关系更新正确**。 > ASK > 如果某个 slot 的数据比较多,部分迁移到新实例,还有一部分没有迁移。 如果请求的 key 在当前节点找到就直接执行命令,否则时候就需要 ASK 错误响应了。 > ask 和move move是已经迁移完成 ask 是正在迁移 ### Redis Cluster 模式的优缺点? 答案:实现了Redis的分布式存储,即每台节点存储不同的内容,来解决在线扩容的问题。 1、优点: * 无中心架构,数据按照slot分布在多个节点 * 集群中的每个节点都是平等的,每个节点都保存各自的数据和整个集群的状态。每个节点都和其他所有节点连接,而且这些连接保持活跃,这样就保证了我们只需要连接集群中的任意一个节点,就可以获取到其他节点的数据。 * 可线性扩展到1000个节点,节点可动态添加或删除 * 能够实现自动故障转移,节点之间通过`gossip协议`交换状态信息,用投票机制完成slave到master的角色转换 缺点: * 数据通过异步复制,不保证数据的强一致性 * slave充当 “冷备”,不对外提供读、写服务,只作为故障转移使用。 * 批量操作限制,目前只支持具有相同slot值的key执行批量操作,对mset、mget、sunion等操作支持不友好 * key事务操作支持有限,只支持多key在同一节点的事务操作,多key分布在不同节点时无法使用事务功能 * 不支持多数据库空间,一台redis可以支持16个db,集群模式下只能使用一个,即`db 0`。 Redis Cluster模式不建议使用pipeline和multi-keys操作,减少max redirect产生的场景。 ### 增加和删除节点 > 数据来源:https://www.yisu.com/zixun/146512.html 查看集群启动情况:`ps -ef | grep redis` 查看集群的slots分配情况以及节点之间的主从关系:   首先登陆节点7000:redis-cli -p 7000 -h 192.168.182.132 -c //注意不要丢了-c 仅把7000节点当作命令行窗口 #### 增加 **eg:** >1、准备新的节点 在集群目录redis\_cluster目录下增加redis7006和redis7007目录 mkdir redis7006 mkdir redis7007 ![](https://img.kancloud.cn/3b/9e/3b9ea9a15abbe57dd397b2686b0b3905_821x352.png) 复制端口7000的redis.conf配置文件到redis7006和redis7007目录下,并修改配置文件中的端口为对应目录的端口号。 例如redis7006下的redis.conf文件的内容为: ``` ~~~ port 7006 bind 192.168.182.132 //本机IP daemonize yes //设置为后台运行 pidfile /var/run/redis-7006.pid cluster-enabled yes //开启集群 cluster-config-file node-7006.conf cluster-node-timeout 15000 appendonly yes ~~~ ``` >2、启动两个新的redis节点 ``` redis-server redis7006/redis.conf redis-server redis7007/redis.conf ps -ef | grep redis //查看新的redis节点是否启动成功 ``` 登录新的节点 ~~~ redis-cli -p 7006 -h 192.168.182.132 -c cluster nodes ~~~ ![](https://img.kancloud.cn/22/e1/22e106c9f0c02e1e923a340c546e761e_1021x102.png) >3、添加主节点 * 向集群中添加节点7006,注意一定要保证节点里面没有添加过任何数据,不然添加会报错 ~~~ cd /usr/local/redis/redis/src ./redis-trib.rb add-node 192.168.182.132:7006 192.168.182.132:7000  //第一次节点为新增的节点 第二个节点为集群中的节点 ~~~ * 为新增的主节点增加slot ~~~ cd /usr/local/redis/redis/src ./redis-trib.rb reshard 192.168.182.132:7001 //可以为任意的节点 在此登录的7001只是作为客户端去访问的 ~~~ ![](https://img.kancloud.cn/dc/8f/dc8f7cd19ff7c704f6bc5607e79e285a_1284x624.png) 执行后:   因为我们增加7006为主节点后,一共存在四个主节点,为了平均分配我们需要给7006分配16384除以4等于4096个节点,所以我们输入4096,按enter继续: ![](https://img.kancloud.cn/e0/af/e0af199eccd133d8ebf5b37e5d527b96_919x210.png) 输入7006的节点ID,按enter继续: ![](https://img.kancloud.cn/df/2a/df2a6e1887eb637bbf411b5177b3155e_775x207.png) 从哪些主节点抽取槽到新节点中:all为所有主节点,done:指定节点,在这里我们输入all,按enter继续: ![](https://img.kancloud.cn/20/d7/20d75e9a8f33bc7e381ad583fad45249_970x285.png) 输入yes后按enter开始给7006分配虚拟槽,分配完成后 ~~~ redis-cli -p 7000 -h 192.168.182.132 -c cluster nodes ~~~ ![](https://img.kancloud.cn/b9/b8/b9b88f1dd94b66c756b59cc55e186bee_1440x223.png) > 4、添加从节点7007 * 使用add-node添加新节点 ~~~  cd /usr/local/redis/redis/src ./redis-trib.rb add-node 192.168.182.132:7007 192.168.182.132:7000 ~~~ ![](https://img.kancloud.cn/bd/72/bd72871103bd229adcbf86bf517fcc81_1464x344.png) * 将7007变为7006的 从节点 ~~~ redis-cli -p 7007 -h 192.168.182.132 //后面的字符串为节点7006的节点ID cluster replicate 52d169e7011ccdf10f99c1d83f92409dcc37ab55 ~~~ #### 删除 删除节点的话,要先删除该节点的从节点,避免从节点顶替成主节点 只要使用del-node命令即可: ~~~ ./redis-trib del-node 127.0.0.1:7000 <node-id> 第一个参数是任意一个节点的地址,第二个节点是你想要移除的节点地址。 ~~~ 使用同样的方法移除主节点,不过在移除主节点前,需要确保这个主节点是空的. 如果不是空的,需要将这个节点的数据重新分片到其他主节点上. >1、删除从节点 删除节点用del-node命令。此命令需要制定删除节点的ip和端口,以及节点的id。 ~~~ cd /usr/local/redis/redis/src ./redis-trib.rb del-node 192.168.182.132:7007 7007节点ID ~~~ >2、删除主节点 * 将主节点7006的slots分配到其他主节点上 ~~~ cd /usr/local/redis/redis/src ./redis-trib.rb reshard 192.168.182.132:7006 ~~~ ![](https://img.kancloud.cn/71/b6/71b6bbfea1db1e78893401ad221a3a36_1261x691.png) 此时是可以选择要把slot转移到具体的节点,或者是选择all(代表所以节点) * 使用del-node命令来删除7006主节点。 ~~~ cd /usr/local/redis/redis/src ./redis-trib.rb del-node 192.168.182.132:7006 52d169e7011ccdf10f99c1d83f92409dcc37ab55 ~~~