复制模式:用户通过执行SLAVEOF命令或者设置slaveof选项,让一个服务器区复制另一个服务器。
Redis的复制功能分为同步(sync)和命令传播(command propagate);
同步操作用于将从服务器的数据状态更新至主服务器当前所在的数据库状态;
命令传播操作则用于在主服务器的数据库状态被修改,导致主从服务器状态不一致的情况时,通过命令传递,将主从服务器的数据库状态从新回到一致状态;
从服务器对主服务器的同步操作需要通过向主服务器发送SYNC命令来完成;
主从服务器之间存在网络延时,当主服务器的数据库状态因为客户端的命令更新时,从服务器需要通过主服务器的命令传播获取客户端的命令来更新自身的数据库状态。
redis中,从服务器对主服务器的复制主要有两种:
旧版复制功能对断线重连这种情况的支持不友好,每次断线重连都要重新生成RDB文件,这样不符合高性能的特点,应该需要区分断线重连是否丢失很多数据,如果中间丢失了较大部分的数据,这时候需要全量复制,否则应该为增量复制。
Redis2.8版本开始,通过PSYNC命令替代SYNC命令来实现复制时的同步操作;
PSYNC主要分为完整重同步(full resynchronization)和部分重同步(partial resynchronization)
完整重同步主要作用:主服务器创建并发送RDB文件给从服务器,向从服务器发送保存在缓冲区中的写命令;
部分重同步主要作用:根据从服务器断线时,缺失的记录位置,将缺失的命令发送给从服务器;
部分重同步功能由三部分构成:
复制积压缓冲区:由主服务器维护的一个固定长度(fixed-size)先进先出(FIFO)队列,默认大小为1MB;
从服务器的复制偏移量在主服务器的偏移量offset之后,进行部分复制;
复制积压缓冲区的最小大小计算公式:second*write_size_per_second
second:从服务器断线后重新连接上主服务器所需的平均时间;
write_size_per_second:主服务器平均每秒产生的写命令数据量
主服务器运行ID:每隔Redis服务器都有自己运行的ID,运行ID在服务器启动时自动生成,由40个随机的十六进制字符组成;
从服务器在对主服务器初次复制时,主服务器会将自己的运行ID传送给从服务器,从服务器将次ID保存,当从服务器断线重连时,将保存的ID发送主服务器,主服务器通过ID比对确定从服务器部分重同步还是完整重同步。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hyuux6XS-1633517443612)(…/pic/image-20210917234509974.png)]
min-slaves-to-write:从服务器少于设定数量,主服务器拒绝执行写命令
min-slaves-max-lag:从服务器的延时大于或等于设定数量时,主服务器拒绝执行写命令
Redis高可用性的解决方案:由一个或多个Sentinel实例组成的Sentinel系统可以监视任意多个主服务器,以及主服务器下的从服务器,当被监视的主服务器下线的时候,自动从其所管理的从服务器中选举一台出来升级为主服务器。
启动命令:redis-sentinel /sentinel.conf 或者 redis-server /sentinel.conf --sentinel
Sentinel启动的步骤:
Sentinel默认会以每十秒一次的频率,通过命令连接向被监视的服务器发送INFO命令,并通过解析INFO命令的回复来获取主服务器当前的信息。
当Sentinel发现主服务器有新的从服务器出现时,Sentinel除为新的从服务器创建相应的结构实例外,Sentinel还会创建连接到从服务器的命令连接和订阅连接。
默认情况下,Sentinel以每两秒一次的频率,向被监视主服务器和从服务器的Sentinel发送信息,起到信息通知的作用;
当Sentinel和主服务器或者从服务器建立订阅连接后,Sentinel会接收_ Sentinel _:hello频道的内容,并根据内容更新服务器实例的信息结构。
Sentinel为主服务器创建的实例结构中的Sentinels字典保存了除Sentinel本身之外,同样保存其他监视主服务器的Sentinel信息,当Sentinel从订阅频道中接收到其他的Sentinel的信息时,会根据信息内容更新实例结构中的信息内容。
当Sentinel通过频道消息发现一个新的Sentinel时,会将新的Sentinel在Sentinels字典中创建相应的实例结构,Sentienl之间也会创建命令连接,最终监视同一个主服务器的多个Sentinel之间连成了相互连接的网络。
Sentinel之间只创建命令连接不创建订阅连接,Sentinel通过命令连接来进行主观下线和客观下线,通过订阅连接获取主从服务器的Sentinel信息
默认情况下,Sentinel以每秒一次的频率向所有与它创建了命令连接的实例(主服务器,从服务器,其他Sentinel在内)发送PING命令,并通过实例返回的PING命令回复来判断实例是否在线。当发现某个实例在down-after-milliseconds时间内没有响应,则认为实例主观下线。
当一个Sentinel对某个主服务器认为下线后,会咨询其他监听同一个主服务器的Sentinel对主服务器状态的判断,当收集到足够多的确认后,Sentinel会认为该主服务器已下线,对主服务器进行故障转移操作。
通过此命令询问其他Sentinel是否同意主服务器下线
当前Sentinel接收到其他Sentinel发送的SENTINEL is-master-down-by-addr 127.0.0.1 6379 0 *(IP地址、端口号、配置纪元、runid)命令后,会解析命令中得 主服务器地址和端口,然后检擦主服务器是否下线,再向原Sentinel返回信息Sentinel is-master-down-by
客观下线得判断条件:当认为主服务器客观下线的Sentinel的数量达到Sentinel配置中设置的数量,那么就认为主服务器客观下线;
sentinel monitor master 127.0.0.1 6379:认为主服务器客观下线的Sentinel的数量为2(包含当前Sentitnel)
不同的Sentinel可能设置的客观下线数量不一样,这是允许的
当主服务器被判断为客观下线后,监视这个主服务器的各个Sentinel会进行协商,选举出一个领头Sentinel;
选举规则:
操作步骤:
过滤规则:
过滤后的从服务器执行SLAVEOF no one ,升级为主服务器;
将其余的从服务器设置为新的主服务器的从服务器,SLAVEOF 新主服务器。
Redis集群时Redis提供的分布式数据库方案,集群通过分片进行数据共享,并提供复制和故障转移功能。
节点是一个独立的单元,多个节点通讯构成集群。一个节点只能属于一个集群。
集群内节点通过CLUSTER MEET 命令将不属于集群的节点添加到集群中
Redis服务器启动时会根据配置项的cluster-enabled
判断是否开启服务器的集群模式,如果为false则按单机模式启动。
节点使用redisServer结构保存服务器的状态,使用redisClient结构保存客户端的状态
clusterNode结构保存节点的当前状态
struct clusterNode{ //创建节点时间 mstime_t ctime; //节点名称 char name[REDIS_CLUSTER_NAMELEN]; //节点标识 int flags; //节点当前的配置纪元,用于实现故障转移 unit64_t configEpoch; //节点的IP地址 char ip[REDIS_IP_STR_LEN] //节点的端口号 int port; //保存连接节点所需的有关信息 clusterLink *link; }
typedef struct cluster clusterLink{ //连接的创建时间 mstime_t ctime; //TCP 套接字描述符 int fd; //输出缓冲区 sds sndbuf; //输入缓冲区 sds rcvbuf; //与当前连接关联的节点 struct clusterNode *node; }clusterLink
RedisClient结构中的套接字和缓冲区用于连接客户端,clusterLink结构中的套接字和缓冲区用于连接节点。
clusterState结构记录当前节点保存的集群状态
typedef struct clusterState{ //指向当前节点的指针 clusterNode *myself; //集群当前配置纪元 uint64_t currentEpoch; //集群当前状态 int state; //集群中至少处理着一个槽的节点的数量 int size; //集群节点名单 dict *nodes; }clusterState
Redis集群通过分片的方式来保存数据库中的键值对:集群的整个数据库被分为16384个槽(slot),数据库中的每个键都属于16384个槽中的一个,集群中的每个节点可以处理0个或最多16384个槽。
通过向节点发送CLUSTER ADDSLOTS [slot …]命令,可以将一个或多个槽指派给节点负责
clusterNode结构的slots属性和numslot属性记录节点负责处理槽的信息
struct clusterNode{ unsigned char slots[16384/8]; int numslots; }
如果slots数组在索引
i
上的二进制位的值为1,表示节点负责处理槽i
;
clusterState.slots数组记录了集群中所有槽的指派信息,当程序需要将某个节点的槽指派信息通过消息发送给其他节点,程序只需要将相应节点的clusterNode.slots数组整个发送出去。
clusterState.slots数组记录了集群中所有槽的指派信息,clusterNode.slots数组只记录了clusterNode结构所代表的节点的槽指派信息
当客户端向节点发送与数据库键相关的命令时,接收命令的节点会计算出命令要处理的数据库键属于哪个槽,并检查这个槽是否指派给自己,如果槽所对应的节点为当前节点,则当前节点执行命令;否则,节点向客户端返回MOVED错误,指引客户端转向至正确的节点,并再次发送之前想要执行的命令。
计算方法为:CRC16(key)& 16384
通过命令CLUSTER KEYSLOT 命令可以查看一个给定键属于哪个槽。
首先通过clusterState中的slots数组,判断槽是否由当前节点负责,如果不是,从数组中获取clusterNode节点信息,向客户端返回MOVED错误,指引客户端转向负责槽的节点。
集群模式的redis-cli客户端在接收到MOVED错误后,不会打印MOVED错误,而是根据MOVED错误自动进行节点转向,并打印转向信息。
redis-trib对集群的单个slot进行重新分片步骤
当客户端向源节点发送一个数据库键相关的命令,并且命令要处理的数据库键恰好处于被迁移的槽,服务器会向客户端返回一个ACK错误,reids-cli的单机模式会打印这个异常。
ASK错误和MOVED错误的区别
MOVED错误代表槽的负责权从一个节点转移到另一个节点。
ASK错误是两个节点在迁移槽的过程中使用的一种临时措施。
Redis集群中的节点分为主节点和从节点,主节点负责处理槽,从节点负责复制主节点,并在主节点下线后,代替下线主节点继续处理命令请求。
向节点发送命令CLUSTER REPLICATE <node_id>使当前节点称为目标节点的从节点。
集群中的每个节点都会定期向集群中的其他节点发送PING消息,来检测对方是否在线,如果目标节点没在规定时间内回复PONG消息,那么将会被标记为疑似下线(probable fail,PFAIL)
集群中的各个节点会通过相互发送信息的方式来交换来集群中各个节点的状态信息。当主节点收到其他主节点的目标节点下线状态报告,会将状态报告保存在clusterNodeFailReport中。
如果在一个集群里面,半数以上负责处理槽的主节点都将某个主节点x报告为疑似下线,那么这个主节点x将被标记为下线(FAIL),将主节点x标记为已下线的节点会向集群广播一条关于主节点x的FAIL消息,所有收到这条FAIL消息的节点都会立即将主节点x标记为已下线。
当一个从节点发现正在复制的主节点已进入下线状态,从节点将开始对下线的主节点进行故障转移
规则:
集群中各个节点通过发送和接收消息来进行信息交互。
消息类型如下: