1.复制
执行slaveof
命令或者设置slaveof
选项,让一个服务器去复制另外一个服务器。
旧版复制功能的实现(Redis 2.8 之前的版本)
复制功能分为同步
和命令传播
两个操作。
同步(sync)
用于将从服务器的数据库状态更新至主服务器当前所处的数据库状态。sync
命令执行了以下操作:
- 从向主发送
sync
命令; - 主收到
sync
命令后执行bgsave
命令,在后台生成一个 RDB 文件,并使用一个缓冲区记录从现在开始的所有写命令; - 主将生成的 RDB 文件发送给从,从接收并载入这个 RDB 文件;
- 主将记录在缓冲区里的所有写命令发送给从服务器,从服务器执行这些写命令,最终与主的服务器状态一致
命令传播(command propagate)
用于在主服务器的数据库状态被修改,导致主从服务器的数据库状态不一致时,让主从服务器的数据库重新回到一致状态。
旧版复制的缺陷
从对主的复制可以分为两种情况:初次复制和断线后重复制。断线重复制是向主发送sync
命令来获取和载入 RDB 文件进行数据同步,所以效率不高。
新版复制功能的实现
新版使用psync
命令来替代旧版sync
命令。psync
命令具有完整重同步和部分重同步两种模式。完整重同步与sync
命令的步骤基本一样。部分重同步用于处理断线重复制的情况,当断线后连上主后,如果条件允许,主可以将主从连接断开期间执行的写命令发送给从,从只要接收并执行这些写命令,就可以将数据库更新主当前的状态。
部分重同步的实现:
- 复制偏移量
- 判断主从是否一致
- 判断复制写命令的起始位置
- 复制积压缓冲区
- 要合理配置缓冲区大小,太小会导致断开时找到对应的偏移量,引起全量同步
- 服务器运行 ID
复制的实现
复制命令:slaveof 127.0.0.1 6379
- 设置主服务器的地址和端口
- 建立套接字连接
- 发送
ping
命令 - 身份验证
- 发送端口信息
- 同步
- 命令传播
心跳检测
命令传播阶段,从会默认以每秒一次的频率,向主发送命令:replconf ack <replication_offset>
主要有三个作用:
- 检测主从的网络连接状态
- 辅助实现
min-slaves
配置选项 - 检测命令丢失
2. Sentinel
Sentinel 是 Redis 的 HA 解决方案:由一个或多个 Sentinel 实例组成的 Sentinel 系统(system)可以监视任意多个主服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器下的某个从服务器升级为新的主服务器,然后由新的主服务器代替已下线的主服务器继续处理命令请求。
启动并初始化 Sentinel
一个 Sentinel 启动时需要执行以下步骤:
- 初始化化服务器
- 将普通的Redis服务器使用的代码替换成 Sentinel 专用代码
- 初始化 Sentinel 状态
- 根据给定的配置文件,初始化化 Sentinel 的监视主服务器列表
- 创建连向主服务器的网络连接
初始化服务器
Sentinel 本质上只是一个运行在特殊模式下的 Redis 服务器,所以启动 Sentinel 的第一步,就是初始化一个普通的 Redis 服务器,因为 Sentinel 执行的工作和普通的 Redis 服务器不同,所以 Sentinel 的初始化化过程和普通的 Redis 服务器的初始化化过程并不完全相同。
使用 Sentinel 专用代码
将一部分普通 Redis 服务器使用的代码替换成 Sentinel 专用代码,比如命令表,所以 Sentinel 模式下有自己的专用的命令。
初始化 Sentinel 状态
服务器会初始化一个sentinel.c/sentinelState
结构,这个结构保存了服务器所有和 Sentinel 功能有关的状态。
初始化 Sentinel 状态的 master 属性
Sentinel 状态中的masters
字典记录了所有被 Sentinel 监视的主服务器的相关信息。
创建面向主服务器的网络链接
Sentinel 将成为主服务器的客户端,它可以向主服务器发送命令,并从命令中获取相关的信息,每个被 Sentinel 监视的主服务器来说,Sentinel 会创建两个连向主服务器的异步网络连接:
- 命令连接:向主服务器发送命令,接收命令回复。
- 订阅连接:订阅主服务器的__sentinel__:hello频道。
获取主服务器信息
Sentinel 每 10 秒通过命令连接向被监视的主服务器发送INFO
命令,并通过分析命令的回复来获取主服务器的当前信息。
获取从服务器信息
当 Sentinel 发现主服务器有新的从服务器时,Sentinel 除了会为了这个新的服务器创建相应的实例结构外,Sentinel 还会创建连接到从服务器的命令连接和订阅莉娜洁。
向主服务器和从服务器发送信息
默认情况下 Sentinel 会以每秒一次的频率通过命令连接向所有被监视的主服务器和从服务器发送以下格式的命令:
PUBLISH __sentinel__hello: "<s_ip>,<s_port>,<s_runid>,<s_epoch>,<m_name>,<m_ip>,<m_port>,<m_epoch>"
这条命令向服务器的__sentinel__:hello 频道发送一条信息。
接收来自主服务器和从服务器的频道信息
当 Sentinel 与一个服务器建立起订阅连接后,Sentinel 会通过订阅连接,想服务器发送命令:
SUBSCRIBE __sentilnel__:hello
更新 sentinels 字典
当 Sentinel 接到其他 Sentinel 发来的信息时,会从信息中分析并维护 sentinels 字典。
创建连向其他 Sentinel 的命令连接
当 Sentinel 通过频道信息发现一个新的 Sentinel 时,它不仅会创建一个连接新 Sentinel 的命令连接,而新的 Sentinel 也同样会创建一个连向这个 Sentinel 的命令连接。
检测主观下线状态
默认情况下,Sentinel 会以每秒向所有与它创建了命令连接的实例发送 PING 命令来判断实例是否在线。Sentinel 配置文件中的 down-after-milliseconds 选项指定了 Sentinel 判断实例进入主观下线所需的时间长度。
检测客观下线状态
当 Sentinel 将一个主服务器判断为主观下线后,为了确认这个主服务器是真的下线,它会向同样监视这一主服务器的其他 Sentinel 进行询问,当从其他 Sentinel 那里接收到足够数量的已下线判断后,Sentinel 就会将主服务器判定为客观下线,并对主服务器执行故障转移操作。
选举领头 Sentinel
当一个主服务器被判断为客观下线时,监视这个下线服务器的各个 Sentinel 会进行协商选出一个领头 Sentinel对下线主服务器执行故障转移操作。
故障转移
包含以下步骤:
- 从已下线主服务器属下的所有从服务器中选出新的主服务器
从所有的从服务器中,跳一个状态良好、数据完整的从服务器,向其发送 SLAVEOF no one命令,将这个从服务器转为主服务器。 - 让所有服务器改为复制新的服务器
- 已下线的主服务器在重新上线后成为新主服务器的从服务器
3. 集群
集群通过分片来进行数据共享,并提供复制和故障转移功能。
Redis Cluster是使用去中心的数据存储架构。
节点
通过CLUSTER MEET
命令连接各节点,组成可以工作的集群。Redis 服务器会根据 cluster-enabled 配置选项是否为 yes 来决定是否开启服务器集群模式。
槽指派
集群的整个数据库被分为 16384 个 slot,每个键都在其中的一个 slot 中。
在集群中执行命令
当客户端向节点发送与数据库键有关的命令时,接收命令的节点会计算出命令要处理的数据库属于哪个 slot,并检查这个 slot 是否指派给自己。
重新分片
重新分片可以将任意数量已经指派给某个节点的 slot 改为指派给另一个节点,并且相关 slot 所属的键值对也会从源节点移动到目标节点。重新分片操作过程中不需要下线,并且源节点和目标节点都可以继续处理命令请求。
ASK 错误
在重新分片期间,与数据库键有关的命令恰好属于正在被迁移的 slot时,源节点会先在自己的库里查找,找到直接执行,找不到则向客户端返回 ASK 错误,指引客户端转向正在导入 slot 的目标节点,并再次发送之前想要执行的命令。
复制与故障转移
从节点在被复制的主节点下线时,代替下线主节点继续处理命令请求。下线的节点上线后,成为新主节点的从节点。跟 Sentinel 机制很像。
消息
集群中的各个节点发送和接收消息通信,发送方节点为发送者,接收方节点为接收者。发送的消息有:MEET消息、PING 消息、PONG 消息、FAIL 消息、PUBLISH 消息。一条消息由消息头和消息正文组成。