Redis主从复制、哨兵机制、集群分片
目录
一.主从复制
1.概述
2.主从架构相比于单点架构的优势
3.主从复制原理和工作流程
第一次同步
第一阶段:建立链接、协商同步
第二阶段:主服务器同步数据给从服务器
第三阶段:主服务器发送新写操作命令给从服务器
基于长连接的命令传播
分摊主服务器的压力
问题引入
"经理"角色的从服务器
增量复制
问题引入
编辑增量复制过程
主服务器怎么知道要将哪些增量数据发送给从服务器的
调整repl_backlog_buffer 缓冲区
4.主从架构的缺点
二.哨兵机制
1.概念
2.哨兵的作用
3.哨兵如何判断节点真的故障
主观下线
客观下线
4.由哪个哨兵进行主从故障转移
谁是候选者
如何选出Leader
5.主从故障转移的过程
步骤一:选出新主节点
第一轮考察:优先级最高的从节点胜出
第二轮考察:复制进度最靠前的从节点胜出
第三轮考察:ID 号小的从节点胜出
步骤二:将从节点指向新主节点
步骤三:通知客户的主节点已更换
步骤四:将旧主节点变为从节点
6.哨兵集群是如何组成的
7.哨兵集群如何知道从节点的信息
三.集群分片
1.问题引入
2.集群模式的特征
3.数据分区理论
①节点取余分区
②一致性Hash分区
③哈希槽分区
4.在集群中执行命令
5.重新分派槽位
6. MOVED错误与ASK错误
MOVED错误
ASK错误
ASK错误与MOVED错误的区别
7. Redis Cluser是如何保证高可用的
故障检测
故障转移
步骤一:选出新主节点
步骤二:将已下线主节点的槽派给新节点
步骤三:通知集群主节点已更换
步骤四:将旧主节点变为从节点
一.主从复制
1.概述
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave)。
- 数据的复制是单向的,只能由主节点到从节点
- 默认情况下,每台Redis服务器都是主节点,且一个主节点可以有多个从节点(或没有从节点),从节点也可以拥有从节点,但一个从节点只能有一个主节点
- 主节点可写可读,主要负责写操作,从节点只能进行读操作
- 主机shutdown后,从机数据可以正常使用,即仍能提供读,等待主机重启动归来
2.主从架构相比于单点架构的优势
- 数据冗余和高可用性:为了避免单点Redis服务器故障,准备多台服务器,互相连通。将数据复制多个副本保存在不同的服务器上,连接在一起,并保证数据是同步的。即使有其中一台服务器宕机,其他服务器依然可以继续提供服务。
-
负载均衡提高读取性能:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
3.主从复制原理和工作流程
第一次同步
当从服务器 salve 启动后,会与主服务器进行第一次同步。主从服务器间的第一次同步的过程可分为三个阶段:
- 第一阶段是建立链接、协商同步;
- 第二阶段是主服务器同步数据给从服务器;
- 第三阶段是主服务器发送新写操作命令给从服务器。
第一阶段:建立链接、协商同步
- 执行了 replicaof 命令后,从服务器就会给主服务器发送 psync 命令,表示要进行数据同步。psync 命令包含两个参数,分别是主服务器的 runID 和复制进度 offset。
- 主服务器收到 psync 命令后,会用 FULLRESYNC 作为响应命令返回给对方。并且这个响应命令会带上两个参数:主服务器的 runID 和主服务器目前的复制进度 offset。从服务器收到响应后,会记录这两个值。
FULLRESYNC 响应命令的意图是采用全量复制的方式,也就是主服务器会把所有的数据都同步给从服务器。
第二阶段:主服务器同步数据给从服务器
- 接着,主服务器会执行 bgsave 命令来生成 RDB 文件,然后把文件发送给从服务器。
- 从服务器收到 RDB 文件后,会先清空当前的数据,然后载入 RDB 文件。
这里有一点要注意,主服务器生成 RDB 这个过程是不会阻塞主线程的,因为 bgsave 命令是产生了一个子进程来做生成 RDB 文件的工作,是异步工作的,这样 Redis 依然可以正常处理命令。
但是,这期间的写操作命令并没有记录到刚刚生成的 RDB 文件中,这时主从服务器间的数据就不一致了。那么为了保证主从服务器的数据一致性,主服务器在下面这三个时间间隙中将收到的写操作命令,写入到 replication buffer 缓冲区里:
- 主服务器生成 RDB 文件期间;
- 主服务器发送 RDB 文件给从服务器期间;
- 「从服务器」加载 RDB 文件期间;
第三阶段:主服务器发送新写操作命令给从服务器
- 在主服务器生成的 RDB 文件发送完,从服务器收到 RDB 文件后,丢弃所有旧数据,将 RDB 数据载入到内存。完成 RDB 的载入后,会回复一个确认消息给主服务器。
- 接着,主服务器将 replication buffer 缓冲区 里所记录的写操作命令发送给从服务器,从服务器执行来自主服务器 replication buffer 缓冲区 里发来的命令,这时主从服务器的数据就一致了。
至此,主从服务器的第一次同步的工作就完成了。
基于长连接的命令传播
主从服务器在完成第一次同步后,双方之间就会维护一个 TCP 连接。而且这个连接是长连接的,目的是避免频繁的 TCP 连接和断开带来的性能开销。
后续主服务器可以通过这个连接继续将写操作命令传播给从服务器,然后从服务器执行该命令,使得与主服务器的数据库状态相同。
Redis 通过互相的 ping-pong 心态检测机制来 判断节点是否正常工作,如果有一半以上的节点去 ping 一个节点的时候没有 pong 回应,集群就会认为这个节点挂掉了,会断开与这个节点的连接。
Redis 主从节点发送的心态间隔是不一样的,而且作用也有一点区别:
- Redis 主节点默认每隔 10 秒对从节点发送 ping 命令,判断从节点的存活性和连接状态,可通过参数repl-ping-slave-period控制发送频率。
- Redis 从节点每隔 1 秒发送 replconf ack{offset} 命令,给主节点上报自身当前的复制偏移量,目的是为了:
- 实时监测主从节点网络状态;
- 上报自身复制偏移量, 检查复制数据是否丢失, 如果从节点数据丢失, 再从主节点的复制缓冲区中拉取丢失数据。
分摊主服务器的压力
问题引入
在前面的分析中,我们可以知道主从服务器在第一次数据同步的过程中,主服务器会做两件耗时的操作:生成 RDB 文件 和 传输 RDB 文件。
主服务器是可以有多个从服务器的,如果从服务器数量非常多,而且都与主服务器进行全量同步的话,就会带来两个问题:
- 由于是通过 bgsave 命令来生成 RDB 文件的,那么主服务器就会忙于使用 fork() 创建子进程,如果主服务器的内存数据非大,在执行 fork() 函数时是会阻塞主线程的,从而使得 Redis 无法正常处理请求;
- 传输 RDB 文件会占用主服务器的网络带宽,会对主服务器响应命令请求产生影响。
这种情况就好像,刚创业的公司,由于人不多,所以员工都归老板一个人管,但是随着公司的发展,人员的扩充,老板慢慢就无法承担全部员工的管理工作了。
"经理"角色的从服务器
要解决这个问题,老板就需要设立经理职位,由经理管理多名普通员工,然后老板只需要管理经理就好。
Redis 也是一样的,从服务器可以有自己的从服务器,我们可以把拥有从服务器的从服务器当作经理角色,它不仅可以接收主服务器的同步数据,自己也可以同时作为主服务器的形式将数据同步给从服务器,但是它仍然不可以执行写操作,组织形式如下图:
通过这种方式,主服务器生成 RDB 和传输 RDB 的压力可以分摊到充当经理角色的从服务器。
增量复制
问题引入
主从服务器在完成第一次同步后,就会基于长连接进行命令传播。如果主从服务器间的网络连接断开了,那么就无法进行命令传播了,这时从服务器的数据就没办法和主服务器保持一致了,客户端就可能从「从服务器」读到旧的数据。
- 在 Redis 2.8 之前,如果主从服务器在命令同步时出现了网络断开又恢复的情况,从服务器就会和主服务器重新进行一次全量复制,很明显这样的开销太大了,必须要改进一波。
- 所以,从 Redis 2.8 开始,网络断开又恢复后,从主从服务器会采用增量复制的方式继续同步,也就是只会把网络断开期间主服务器接收到的写操作命令,同步给从服务器。
网络恢复后的增量复制过程如下图:
增量复制过程
- 从服务器在恢复网络后,会发送 psync 命令给主服务器,此时的 psync 命令里的 offset 参数不是 -1;
- 主服务器收到该命令后,然后用 CONTINUE 响应命令告诉从服务器接下来采用增量复制的方式同步数据;
- 然后主服务将主从服务器断线期间,所执行的写命令发送给从服务器,然后从服务器执行这些命令。
主服务器怎么知道要将哪些增量数据发送给从服务器的
答案藏在这两个东西里:
- repl_backlog_buffer:是一个「环形」缓冲区,用于主从服务器断连后,从中找到差异的数据;在主服务器进行命令传播时,不仅会将写命令发送给从服务器,还会将写命令写入到 repl_backlog_buffer 缓冲区里,因此 这个缓冲区里会保存着最近传播的写命令。
- replication offset:标记上面那个缓冲区的同步进度,主从服务器都有各自的偏移量,主服务器使用 master_repl_offset 来记录自己「写」到的位置,从服务器使用 slave_repl_offset 来记录自己「读」到的位置。
网络断开后,当从服务器重新连上主服务器时,从服务器会通过 psync 命令将自己的复制偏移量 slave_repl_offset 发送给主服务器,主服务器根据自己的 master_repl_offset 和 slave_repl_offset 之间的差距,然后来决定对从服务器执行哪种同步操作:
- 如果判断出从服务器要读取的数据还在 repl_backlog_buffer 缓冲区里,那么主服务器将采用增量同步的方式;
- 相反,如果判断出从服务器要读取的数据已经不存在 repl_backlog_buffer 缓冲区里,那么主服务器将采用全量同步的方式。
当主服务器在 repl_backlog_buffer 中找到主从服务器差异(增量)的数据后,就会将增量的数据写入到 replication buffer 缓冲区,这个缓冲区我们前面也提到过,它是缓存将要传播给从服务器的命令。
repl_backlog_buffer 缓行缓冲区的默认大小是 1M,并且由于它是一个环形缓冲区,所以当缓冲区写满后,主服务器继续写入的话,就会覆盖之前的数据。因此,当主服务器的写入速度远超于从服务器的读取速度,缓冲区的数据一下就会被覆盖。
因此,为了避免在网络恢复时,主服务器频繁地使用全量同步的方式,我们应该调整下 repl_backlog_buffer 缓冲区大小,尽可能的大一些,减少出现从服务器要读取的数据被覆盖的概率,从而使得主服务器采用增量同步的方式。
调整repl_backlog_buffer 缓冲区
repl_backlog_buffer 最小的大小可以根据这面这个公式估算。
- second 为从服务器断线后重新连接上主服务器所需的平均 时间(以秒计算)。
- write_size_per_second 则是主服务器平均每秒产生的写命令数据量大小。
举个例子,如果主服务器平均每秒产生 1 MB 的写命令,而从服务器断线之后平均要 5 秒才能重新连接主服务器。那么 repl_backlog_buffer 大小就不能低于 5 MB,否则新写地命令就会覆盖旧数据了。
当然,为了应对一些突发的情况,可以将 repl_backlog_buffer 的大小设置为此基础上的 2 倍,也就是 10 MB。
4.主从架构的缺点
在 Redis 的主从架构中,由于主从模式是读写分离的,如果主节点(master)挂了,那么将没有主节点来服务客户端的写操作请求,也没有主节点给从节点(slave)进行数据同步了。
这时如果要恢复服务的话,需要人工介入,选择一个「从节点」切换为「主节点」,然后让其他从节点指向新的主节点,同时还需要通知上游那些连接 Redis 主节点的客户端,将其配置中的主节点 IP 地址更新为「新主节点」的 IP 地址。
二.哨兵机制
1.概念
在 Redis 的主从架构中,如果主节点(master)挂了,必须要人工介入。因此我们需要一个能监控「主节点」状态 的节点;当发现主节点挂了 ,它能根据投票数自动将一个「从节点」切换为「主节点」,并且把新主节点的相关信息通知给从节点和客户端,这就是哨兵机制。
2.哨兵的作用
- 主从监控:监控主从redis库运行是否正常
- 故障转移:如果Master异常,则会进行主从切换,将其中一个Slave作为新Master
- 消息通知:哨兵可以将故障转移的结果发送给客户端
- 配置中心:客户端通过连接哨兵来获得当前Redis服务的主节点地址
注:哨兵只做监控和维护集群,不存放数据
3.哨兵如何判断节点真的故障
哨兵会每隔 1 秒给所有主从节点发送 PING 命令,当主从节点收到 PING 命令后,会发送一个响应命令给哨兵,这样就可以判断它们是否在正常运行。
如果主节点或者从节点没有在规定的时间内响应哨兵的 PING 命令,哨兵就会将它们标记为「主观下线」。这个「规定的时间」是配置项 down-after-milliseconds 参数设定的,单位是毫秒。
主观下线
「主观下线」是针对于单个的哨兵来讲的,因为网络拥堵等原因很可能会误判,所以为了减少误判的情况,哨兵在部署的时候不会只部署一个节点,而是用多个节点部署成哨兵集群(最少需要三台机器来部署哨兵集群),通过多个哨兵节点一起判断,就可以就可以避免单个哨兵因为自身网络状况不好,而误判主节点下线的情况。同时,多个哨兵的网络同时不稳定的概率较小,由它们一起做决策,误判率也能降低。
客观下线
有主观就有客观,当一个哨兵判断主节点为「主观下线」后,就会向其他哨兵发起命令,其他哨兵收到这个命令后,就会根据自身和主节点的网络状况,做出赞成投票或者拒绝投票的响应。
当这个哨兵的赞同票数达到哨兵配置文件中的 quorum 配置项设定的值后,这时主节点就会被该哨兵标记为「客观下线」。
例如,现在有 3 个哨兵,quorum 配置的是 2,那么一个哨兵需要 2 张赞成票,就可以标记主节点为“客观下线”了。这 2 张赞成票包括哨兵自己的一张赞成票和另外两个哨兵的赞成票。
注:quorum 的值一般设置为哨兵个数的二分之一加1,例如 3 个哨兵就设置 2。而且哨兵节点的数量应该是奇数。
哨兵判断完主节点客观下线后,哨兵就要开始在多个「从节点」中,选出一个从节点来做新主节点。
4.由哪个哨兵进行主从故障转移
我们之前提到过,采用哨兵机制那么一定要部署哨兵集群,那么当master挂了之后,由谁去进行主从故障转移呢?一起去吗?很显然不是。
当主节点被判断客观下线以后,各个哨兵节点会进行协商先选举出一个领导者哨兵节点并由该领导者节点进行failover (故障迁移)
谁是候选者
哪个哨兵节点判断主节点为「客观下线」,这个哨兵节点就是候选者,所谓的候选者就是想当 Leader 的哨兵。
举个例子,假设有三个哨兵。当哨兵 B 先判断到主节点「主观下线后」,就会给其他实例发送 is-master-down-by-addr 命令。接着,其他哨兵会根据自己和主节点的网络连接情况,做出赞成投票或者拒绝投票的响应。
当哨兵 B 收到赞成票数达到哨兵配置文件中的 quorum 配置项设定的值后,就会将主节点标记为「客观下线」,此时的哨兵 B 就是一个Leader 候选者。
如何选出Leader
候选者会向其他哨兵发送命令,表明希望成为 Leader 来执行主从切换,并让所有其他哨兵对它进行投票。选举使用的算法是Raft算法;Raft算法的基本思路是先到先得: 即在一轮选举中,哨兵B向A发送成为领导者的申请,如果A没有同意过其他哨兵,则会同意B成为领导者。
每个哨兵只有一次投票机会,如果用完后就不能参与投票了,可以投给自己或投给别人,但是只有候选者才能把票投给自己。
在投票过程中,任何一个「候选者」,要满足两个条件才会被选举为Leader:
- 第一,拿到半数以上的赞成票;
- 第二,拿到的票数同时还需要大于等于哨兵配置文件中的 quorum 值。
举个例子,假设哨兵节点有 3 个,quorum 设置为 2,那么任何一个想成为 Leader 的哨兵只要拿到 2 张赞成票,就可以选举成功了。如果没有满足条件,就需要重新进行选举。
如果某个时间点,刚好有两个哨兵节点判断到主节点为客观下线,每位候选者都会先给自己投一票,然后向其他哨兵发起投票请求。如果投票者先收到「候选者 A」的投票请求,就会先投票给它,如果投票者用完投票机会后,收到「候选者 B」的投票请求后,就会拒绝投票。这时,候选者 A 先满足了上面的那两个条件,所以「候选者 A」就会被选举为 Leader。
5.主从故障转移的过程
在哨兵集群中通过投票的方式,选举出了哨兵 leader 后,就可以进行主从故障转移的过程了,如下图:
主从故障转移操作包含以下四个步骤:
- 第一步:在已下线主节点(旧主节点)属下的所有「从节点」里面,挑选出一个从节点,并将其转换为主节点。
- 第二步:让已下线主节点属下的所有「从节点」修改复制目标,修改为复制「新主节点」;
- 第三步:将新主节点的 IP 地址和信息,通过「发布者/订阅者机制」通知给客户端;
- 第四步:继续监视旧主节点,当这个旧主节点重新上线时,将它设置为新主节点的从节点;
步骤一:选出新主节点
故障转移操作第一步要做的就是在已下线主节点属下的所有「从节点」中,挑选出一个状态良好、数据完整的从节点,然后向这个「从节点」发送 SLAVEOF no one 命令,将这个「从节点」转换为「主节点」。
挑选过程如下:
- 第一轮考察:哨兵首先会根据从节点的优先级来进行排序,优先级越小排名越靠前,
- 第二轮考察:如果优先级相同,则查看复制的下标,哪个从「主节点」接收的复制数据多,哪个就靠前。
- 第三轮考察:如果优先级和下标都相同,就选择从节点 ID 较小的那个。
第一轮考察:优先级最高的从节点胜出
Redis 有个叫 slave-priority 配置项,可以给从节点设置优先级。每一台从节点的服务器配置不一定是相同的,我们可以根据服务器性能配置来设置从节点的优先级。
比如,如果 「 A 从节点」的物理内存是所有从节点中最大的, 那么我们可以把「 A 从节点」的优先级设置成最高。这样当哨兵进行第一轮考虑的时候,优先级最高的 A 从节点就会优先胜出,于是就会成为新主节点。
第二轮考察:复制进度最靠前的从节点胜出
如果在第一轮考察中,发现优先级最高的从节点有两个,那么就会进行第二轮考察,比较两个从节点哪个复制进度。
之前我们在讲主从架构的时候就提到过复制进度:主从架构中,主节点会将写操作同步给从节点,在这个过程中,主节点会用 master_repl_offset 记录当前的最新写操作在 repl_backlog_buffer 中的位置(如下图中的「主服务器已经写入的数据」的位置),而从节点会用 slave_repl_offset 这个值记录当前的复制进度(如下图中的「从服务器要读的位置」的位置)。
如果某个从节点的 slave_repl_offset 最接近 master_repl_offset,说明它的复制进度是最靠前的,于是就可以将它选为新主节点。
第三轮考察:ID 号小的从节点胜出
如果在第二轮考察中,发现有两个从节点优先级和复制进度都是一样的,那么就会进行第三轮考察,比较两个从节点的 ID 号, ID 号是用来唯一标识从节点的,ID 号小的从节点胜出。
在选举出从节点后,哨兵 leader 向被选中的从节点发送 SLAVEOF no one 命令,让这个从节点解除从节点的身份,将其变为新主节点。如下图,哨兵 leader 向被选中的从节点 server2 发送 SLAVEOF no one 命令,将该从节点升级为新主节点。
在发送 SLAVEOF no one 命令之后,哨兵 leader 会以每秒一次的频率向被升级的从节点发送 INFO 命令(没进行故障转移之前,INFO 命令的频率是每十秒一次),并观察命令回复中的角色信息,当被升级节点的角色信息从原来的 slave 变为 master 时,哨兵 leader 就知道被选中的从节点已经顺利升级为主节点了。
如下图,选中的从节点 server2 升级成了新主节点:
步骤二:将从节点指向新主节点
当新主节点出现之后,哨兵 leader 下一步要做的就是,让已下线主节点属下的所有「从节点」指向「新主节点」,这一动作可以通过向「从节点」发送 SLAVEOF 命令来实现。
如下图,哨兵 leader 向所有从节点(server3和server4)发送 SLAVEOF,让它们成为新主节点的从节点。
步骤三:通知客户的主节点已更换
经过前面一系列的操作后,哨兵集群终于完成主从切换的工作,那么新主节点的信息要如何通知给客户端呢?
这主要通过 Redis 的发布者/订阅者机制来实现的。每个哨兵节点提供发布者/订阅者机制,客户端可以从哨兵订阅消息。哨兵提供的消息订阅频道有很多,不同频道包含了主从节点切换过程中的不同关键事件,几个常见的事件如下:
客户端和哨兵建立连接后,客户端会订阅哨兵提供的频道。主从切换完成后,哨兵就会向 +switch-master 频道发布新主节点的 IP 地址和端口的消息,这个时候客户端就可以收到这条信息,然后用这里面的新主节点的 IP 地址和端口进行通信了。
通过发布者/订阅者机制机制,有了这些事件通知,客户端不仅可以在主从切换后得到新主节点的连接信息,还可以监控到主从节点切换过程中发生的各个重要事件。这样,客户端就可以知道主从切换进行到哪一步了,有助于了解切换进度。
步骤四:将旧主节点变为从节点
故障转移操作最后要做的是,继续监视旧主节点,当旧主节点重新上线时,哨兵集群就会向它发送 SLAVEOF 命令,让它成为新主节点的从节点,如下图:
至此,整个主从节点的故障转移的工作结束。
6.哨兵集群是如何组成的
事实上,在我们配置哨兵的信息时,只需要填下面这几个参数:设置主节点名字、主节点的 IP 地址和端口号以及 quorum 值。
sentinel monitor <master-name> <ip> <redis-port> <quorum>
不需要填其他哨兵节点的信息,它们是如何感知对方的呢?
实际上也是通过Redis 的发布者/订阅者机制来相互发现的。在主从集群中,主节点上有一个名为__sentinel__:hello 的频道,不同哨兵就是通过它来相互发现,实现互相通信的。
在下图中,哨兵 A 把自己的 IP 地址和端口的信息发布到__sentinel__:hello 频道上,哨兵 B 和 C 订阅了该频道。那么此时,哨兵 B 和 C 就可以从这个频道直接获取哨兵 A 的 IP 地址和端口号。然后,哨兵 B、C 可以和哨兵 A 建立网络连接。
通过这个方式,哨兵 B 和 C 也可以建立网络连接,这样一来,哨兵集群就形成了。
7.哨兵集群如何知道从节点的信息
主节点知道所有「从节点」的信息,所以哨兵会每 10 秒一次的频率向主节点发送 INFO 命令来获取所有「从节点」的信息。
如下图所示,哨兵 B 给主节点发送 INFO 命令,主节点接受到这个命令后,就会把从节点列表返回给哨兵。接着,哨兵就可以根据从节点列表中的连接信息,和每个从节点建立连接,并在这个连接上持续地对从节点进行监控。哨兵 A 和 C 可以通过相同的方法和从节点建立连接。
通过 Redis 的发布者/订阅者机制,哨兵之间可以相互感知,然后组成集群,同时,哨兵又通过 INFO 命令,在主节点里获得了所有从节点连接信息,于是就能和从节点建立连接,并进行监控了。
三.集群分片
1.问题引入
哨兵模式解决了高可用、高并发读的问题,但仍然有缺点:
- 哨兵模式下每台 Redis 服务器都存储相同的数据,很浪费内存,难以存储海量数据
- 哨兵模式下只有一个Master,难以支持高并发写
于是引出了集群模式
2.集群模式的特征
- Redis集群支持多个Master,每个Master又可以挂载多个slave,并且每一个Master保存不同的数据
- Master之间通过ping互相检测彼此的健康状态,相互选举,不再依赖sentinel
- 客户端与Redis的节点连接,不再需要连接集群中所有的节点,只需要任意连接集群中的一个可用节点即可,因为最终都会被转发到正确节点上
注:集群节点保存键值对以及键值对过期时间的处理方式与Redis单机模式是一样的,唯一不同就是节点只能使用0号数据库,而单机Redis服务器则没有限制。
3.数据分区理论
①节点取余分区
用户每次读写操作都是根据公式:hash(key) % N个机器台数,计算出哈希值,用来决定数据映射到哪一个节点上。
这种方案存在一个问题:当节点数量变化时,如扩容或收缩节点,数据节点映射关系需要重新计算,会导致数据的重新迁移,缓存将全部失效
②一致性Hash分区
这里我就不展开讲了,推荐去看好刚老师的视频,讲的贼好
好刚: 7分钟视频详解一致性hash 算法_哔哩哔哩_bilibili
③哈希槽分区
Redis集群没有使用一致性hash,而是引入了哈希槽的概念。
哈希槽(slot)位于数据和节点之间,用于管理数据和节点之间的关系,就相当于节点上放的是槽,槽里放的是数据。
槽解决的是粒度问题,相当于把粒度变大了,这样便于数据移动。哈希解决的是映射问题,使用key的哈希值来计算所在的槽,便于数据分配。
Redis集群有16384个哈希槽,先计算每个key的CRC16哈希值,然后对16384取模来决定放置在哪个槽。集群的每个节点负责一部分的hash槽。比如当前集群如果有3个节点,那么:
4.在集群中执行命令
对数据库中的16384个槽都进行了指派后,集群就会进入上线状态,这是客户端就可以向集群中的节点发送数据命令,需要进行计算出命令要处理的键是属于哪个槽的,并检查是否指派给了自己。
如果键所在的槽正好指派当前节点,那么节点直接执行这个命令:
如果键所在的槽并没有指派给当前节点,那么节点会向客户端返回一个MOVED错误(集群模式下,MOVED错误是回被隐藏的,不会显示的,而是直接显示Redirected),指引客户端转向(redirect)至正确的节点,并再次发送之前想要执行的命令。
5.重新分派槽位
当Redis集群进行扩容/缩容时,会重新分派槽位。
- Redis集群重新分派槽位操作可以将任意数量的已经指派给某个节点的槽改为指派给另一个节点(目标节点),并且相关槽所属的键值对也会从源节点被移动到目标节点上。
- 重新分片操作可以在线进行,在重新分片过程中,集群不需要下线,并且源节点和目标节点都可以继续处理命令请求。
- Redis集群的重新分派槽位操作是由集群管理软件redis-trib负责执行的,Redis提供了进行重新分片所需要的所有命令,redis-trib则通过向源节点和目标节点发送命令来重新分片操作。
6. MOVED错误与ASK错误
MOVED错误
之前讲过,如果键所在的槽并没有指派给当前节点,那么节点会向客户端返回一个MOVED错误(会被隐藏)。
MOVED错误的格式为:
MOVED <slot> <ip>:<port>
其中slot为键所在的槽,而ip和port则是负责处理槽slot的节点的IP地址和端口号
# 表示槽10086正由127.0.0.1,端口号为7002的节点负责。MOVED 10086 127.0.0.1:7002
一个集群客户端通常会与集群中的多个节点创建套接字(Socket)连接,而所谓的节点转向实际上就是换一个套接字(Socket)来发送命令。
如果客户端尚未与想要转向的节点创建套接字(Socket)连接,那么客户端会先根据MOVED错误提供的IP地址和端口号来连接节点,然后再进行转向。
ASK错误
在进行重新分片期间,源节点向目标节点迁移一个槽的过程中,可能会出现这样一种情况:属于被迁移槽的一部分键值对保存在源节点里面,而另一部分键值对则保存在目标节点里面。
当客户端向源节点发送一个与数据库键有关的命令,并且命令要处理的数据库键恰好就属于正在被迁移的槽时:
- 源节点会先在自己的数据库里面查找指定的键,如果找到的话,就直接执行客户端发送的命令。
- 相反地,如果源节点没能在自己的数据库里面找到指定的键,那么这个键有可能已经被迁移到了目标节点,源节点将向客户端返回一个ASK错误(也会被隐藏),指引客户端转向正在导入槽的目标节点,并再次发送之前想要执行的命令。
和接到MOVED错误时的情况类似,集群模式的redis-cli在接到ASK错误时也不会打印错误,而是自动根据错误提供的IP地址和端口进行转向动作。
ASK错误与MOVED错误的区别
- MOVED错误表示槽的负责权已经从一个节点转移到另外的节点。
- ASK错误则是表示两个节点在迁移槽过程中对key处理的负责权。
7. Redis Cluser是如何保证高可用的
Redis Cluster保证高可用主要还是依靠:故障检测与故障转移两种策略。实际上这两种策略的处理过程与哨兵模式的处理类似。
故障检测
集群中每个节点都会定期地向集群中的其他节点发送PING消息,以此检测对方是否在线;如果接收PING消息的节点没有在规定的时间内,向发送PING消息的节点返回PONG消息,那么发送PING消息的节点就会将PING消息节点标记为疑似下线(possible fail,PFAIL)。
如果在集群中,超过半数以上负责处理槽的主节点都将某个节点X标记为PFAIL,则某个主节点就会将这个主节点X就会被标记为已下线(FAIL),并且广播到这条消息,这样其他所有的节点都会立即将主节点X标记为FAIL。
假设:
- Redis Cluster有四个主节点:7000-7003,两个从节点:7004与7005
- 此时7000已下线,并且主节点7001认为主节点7000进入PFAIL
- 同时主节点7002、7003也认为主节点7000进入下线状态
这样一来超过半数的主节点都认为7000节点FAIL,那么7001便会标记7000为FAIL状态,并向集群广播主节点7000已经FAIL消息。
故障转移
步骤一:选出新主节点
- 在该下线主节点的所有从节点中,选择一个做主节点。这里的选举方法也与哨兵类似。
- 被选中的从节点会执行 SLAVEOF no one 命令,成为新的主节点;
步骤二:将已下线主节点的槽派给新节点
新的主节点会撤销对所有对已下线主节点的槽指派,并将这些槽全部派给自己。
步骤三:通知集群主节点已更换
新的主节点向集群广播一条PONG消息,让其他节点知道“我已经变成主节点了,并且我会接管已下线节点负责的处理的槽”;
新主节点开始接收和自己负责处理的槽有关的命令请求
步骤四:将旧主节点变为从节点
举例说明:包含7000、7001、7002、7003四个主节点的集群,我们此时加入7004、7005两个节点,并当做7000的主节点的两个从节点。
如果此时主节点7000下线(宕机),那么集群中仍然有几个主节点将在节点7000的两个从节点7004、7005中选择一个节点作为主节点,比如选择了7004则这个新节点将接管原来节点7000负责处理的槽,并继续处理客户端发送的请求,而7005此时作为7004的从节点。
如果下线的7000节点,又重新上线的话,那它将作为节点7004的从节点。
相关文章:

Redis主从复制、哨兵机制、集群分片
目录 一.主从复制 1.概述 2.主从架构相比于单点架构的优势 3.主从复制原理和工作流程 第一次同步 第一阶段:建立链接、协商同步 第二阶段:主服务器同步数据给从服务器 第三阶段:主服务器发送新写操作命令给从服务器 基于长连接的命…...

字段填充策略 FieldFill
实体类中有如下属性,通过上面的自动填充属性,我们可以实现在进行插入(insert)操作时对添加了注解TableField(fill FieldFill.INSERT)的字段进行自动填充(解释:后面会写配置自动填充的配置类,该…...
Docker run 启动容器报错
今天在Windows下启动docker容器发现的三个错误: Ports are not available: exposing port TCP 0.0.0.0:1521 -> 0.0.0.0:0: listen tcp 0.0.0.0:1521: bind: Only one usage of each socket address (protocol/network address/port) is normally permitted. 端口…...

Golang之路---03 面向对象——类型断言
类型断言 作用 检查 i 是否为 nil检查 i 存储的值是否为某个类型 使用方式 第一种: t : i.(T)这个表达式可以断言一个接口对象(i)里不是 nil,并且接口对象(i)存储的值的类型是 T,如果断言成…...

Atcoder 做题记录
My OI Blog A R C 155 F \mathbb{ARC \ 155 \ F} ARC 155 F E, F 先咕着,做一些多项式题,这篇题解是我人工翻译的 [1] Double Counting 双重计数 考虑从叶子节点开始,用唯一的方式(如果有的话)来构造出一棵满足条件的树…...

C++之观察者模式(发布-订阅)
目录 模式简介 介绍 优点 缺点 代码实现 场景说明 实现代码 运行结果 模式简介 观察者模式(Observer Pattern),也叫我们熟知的发布-订阅模式。 它是一种行为型模式。 介绍 观察者模式主要关注的是对象的一对多的关系, …...

无头单链表,有完整测试程序
🍟无头单链表 👻无头单链表的所有结点都存储有效信息 👻无头单链表相对带头单链表,在有些涉及更改头节点的函数上需要传二级指针 🍟头文件list.h #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #includ…...

2023年第四届“华数杯”数学建模思路 - 案例:FPTree-频繁模式树算法
## 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 算法介绍 FP-Tree算法全称是FrequentPattern Tree算法,就是频繁模式树算法,他与Apriori算法一样也是用来挖掘频繁项集的,…...

MySQL做分布式锁
分布式锁mysql实现方式 方式1:唯一索引 创建锁表,内部存在字段表示资源名及资源描述,同一资源名使用数据库唯一性限制。多个进程同时往数据库锁表中写入对某个资源的占有记录,当某个进程成功写入时则表示其获取锁成功其他进程由于…...
Python学习笔记:变量类型、字符串基本操作
1.注释 单行注释 # 单行注释 多行注释 """ 多行注释 """2.变量类型 # 基本变量类型 a 1 # integer b 1.5 # float c string # String d "string" # string e False # boolean # list\tuple\dictionar…...

JVM的组件、自动垃圾回收的工作原理、分代垃圾回收过程、可用的垃圾回收器类型
详细画的jvm模型图 https://www.processon.com/diagraming/64c8aa11c07d99075d934311 官方网址 https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html 相关概念 年轻代是所有新对象被分配和老化的地方。当年轻代填满时,这会导致m…...

【elementui】解决el-select组件失去焦点blur事件每次获取的是上一次选中值的问题
目录 【问题描述】 【问题摘要】 【分析问题】 【完整Test代码】 【封装自定义指令】 ↑↑↑↑↑↑↑↑↑↑↑↑ 不想看解决问题过程的可点击上方【封装自定义指令】目录直接跳转获取结果即可~~~ 【问题描述】 一位朋友遇到这么一个开发场景:在表格里面嵌入el-…...
通过了PMP考试,还有什么证书值得考?
自从7月24号公布了PMP成绩后,不少伙伴私信小编:通过PMP后还有哪些证书可以提升自己?一来是多份高含金量的证书可以多点竞争力,二来是加持自己的职业发展!今天小编就来给大家捋一捋! 一.NPDP认证 2016 年 4…...

页面技术基础-html
页面技术基础-html 环境准备:在JDBC中项目上完成代码定义 1. 新建一个 Module:filr->右键 -》Module -》Java-》next->名字(html_day1)->finish 2. 在 Moudle上右键-》第二个选项:add framework .. -> 选择JavaEE下第一个选项 Web Apllicat…...

/lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.28‘ not found
某项目中,我要给别人封装一个深度学习算法的SDK接口,运行在RK3588平台上,然后客户给我的交叉编译工具链是 然后我用他们给我的交叉编译工具链报下面的错误: aarch64-buildroot-linux-gnu-gcc --version /data/chw/aarch64/bin/cca…...

解决SVN或GIT忽略提交文件的问题
背景 使用IDEA 的SVN插件提交文件是总是会提交一些不需要提交的文件; 我们可以通过一些简单设置忽略这些文件。 git 在项目根目录新建文本文件,修改后缀为.gitignore 文件中添加内容 *.iml .project .gradle/ .idea/ target/ build/ .vscode/ .settings/ .facto…...

Django框架之路由用法
简介 路由简单的来说就是根据用户请求的 URL 链接来判断对应的处理程序,并返回处理结果,也就是 URL 与 Django 的视图建立映射关系。 Django 路由在 urls.py 配置,urls.py 中的每一条配置对应相应的处理方法。 Django 不同版本 urls.py 配…...
回文链表 LeetCode热题100
题目 给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。 思路 利用快慢指针找到链表中间节点,反转后半段链表。前半段从头节点开始与后半段比较。 注意当链表节点…...

如何在群晖NAS中使用cpolar内网穿透
如何在群晖nas中使用cpolar内网穿透 文章目录 如何在群晖nas中使用cpolar内网穿透 今天,我们来为大家介绍,如何在群晖系统中,使用图形化界面的cpolar。 cpolar经过图形化改造后,使用方法已经简便了很多,基本与其他应用…...

无头单向不循环链表和带头双向循环链表的创建
Lei宝啊:个人主页 愿所有美好不期而遇 前言: 接下来我们将会了解最基础的链表--->单链表 以及最方便也是最爽的链表--->带头双向循环链表。 若有看不懂之处,可画图或者借鉴这里:反转单链表,对于数据结构而言&am…...

地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...

练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

剑指offer20_链表中环的入口节点
链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...