ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Redis 之 【高可用架构】(从主从复制、哨兵到集群)

Redis 之 【高可用架构】(从主从复制、哨兵到集群) 目录1.主从复制Replication高可用的基石1.1 配置建立复制info replication断开复制切主操作安全性只读传输延迟1.2 拓扑1.3 PSYNC 机制与数据同步复制过程分为三个阶段全量复制部分复制复制积压缓冲区实时复制与心跳1.4 解决的问题与缺点1.5 QA2. 哨兵机制Sentinel自动化的故障转移2.1 监控与下线判定 (SDOWN vs ODOWN)什么是脑裂怎么解决2.2 故障转移与选举原理2.3 注意事项2.4 QA3. Redis Cluster 集群海量数据的终极解法3.1 数据分片算法哈希槽3.2 集群故障判定与迁移3.3 集群扩容3.4 QA4. 选型对比1.主从复制Replication高可用的基石主从复制解决了单节点可用性低和性能有限的问题通过主写从读、多副本和多种拓扑提升读性能与数据可靠性但它不能自动故障转移主节点宕机后仍需人工或额外组件介入1.1 配置建立复制参与复制的 Redis 实例划分为主节点master和从节点slave。每个从结点只能有⼀个主节点而⼀个主节点可以同时具有多个从结点。复制的数据流是单向的只能由主节点到从节点。配置复制的方式有以下三种在配置文件中加入 slaveof {masterHost} {masterPort}随 Redis 启动生效在 redis-server 启动命令时加入 --slaveof {masterHost} {masterPort} 生效直接使用 Redis 命令slaveof {masterHost} {masterPort} 生效注意修改配置主要是修改从机的配置主机配置不变info replicationinfo replication 是 Redis 中用来查看主从复制复制/Replica状态信息的命令它不是独立命令而是 INFO 命令的一个 section redis-cli info replication 进入 redis-cli 后也可以直接 info replication在主机上执行127.0.0.1:6379 info replication # Replication role:master connected_slaves:1 slave0:ip127.0.0.1,port6380,stateonline,offset100,lag0 master_replid:2fbd35a8b8401b22eb92ff49ad5e42250b3e7a06 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:100 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:100字段含义role当前节点角色为主节点connected_slaves当前主节点已连接的从节点数量为 1 个slave0第 1 个从节点信息IP 为 127.0.0.1端口 6380状态在线复制偏移量 100延迟 0 秒master_replid主节点当前复制 ID用于标识当前复制流master_replid2第二个复制 ID用于故障转移后保留旧主节点的复制历史以支持部分同步全 0 表示当前没有master_repl_offset主节点当前复制偏移量second_repl_offset第二个复制 ID 对应的偏移量-1表示未使用repl_backlog_active复制积压缓冲区是否启用1表示启用repl_backlog_size复制积压缓冲区大小单位字节即 1MBrepl_backlog_first_byte_offset复制积压缓冲区中第一个字节的偏移量repl_backlog_histlen复制积压缓冲区中有效历史数据长度从机上执行127.0.0.1:6380 info replication # Replication role:slave master_host:127.0.0.1 master_port:6379 master_link_status:up master_last_io_seconds_ago:1 master_sync_in_progress:0 slave_repl_offset:170 slave_priority:100 slave_read_only:1 connected_slaves:0 master_replid:2fbd35a8b8401b22eb92ff49ad5e42250b3e7a06 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:170 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:170字段含义role当前节点角色为从节点。Redis 5.0 后也常用replica表示master_host主节点 IP 地址master_port主节点端口master_link_status与主节点连接状态正常down表示断开master_last_io_seconds_ago最近一次与主节点通信距今 1 秒master_sync_in_progress是否正在进行全量同步0表示没有slave_repl_offset从节点当前的复制偏移量slave_priority从节点优先级供 Sentinel 选主使用数值越小优先级越高0表示不会被提升为主节点slave_read_only从节点是否只读1表示只读connected_slaves当前节点作为主节点时连接的从节点数量这里从节点下面没有从节点所以为 0master_replid从节点记录的主节点复制 IDmaster_replid2第二个复制 ID全 0 表示没有master_repl_offset从节点记录的主节点复制偏移量表示已处理到的位置second_repl_offset第二个复制 ID 对应偏移量-1表示未使用repl_backlog_active复制积压缓冲区是否启用1表示启用repl_backlog_size复制积压缓冲区大小单位字节即 1MBrepl_backlog_first_byte_offset复制积压缓冲区中第一个字节的偏移量repl_backlog_histlen复制积压缓冲区中有效历史数据长度断开复制在从节点执行slaveof no one可断开与主节点的复制关系。断开复制流程断开与主节点的复制关系从节点晋升为主节点特点从节点断开复制后不会抛弃原有数据只是无法再获取主节点上的后续数据变化切主操作在从节点执行slaveof {newMasterIp} {newMasterPort}可将当前从节点的数据源切换到另一个主节点。切主操作流程断开与旧主节点的复制关系与新主节点建立复制关系删除从节点当前所有数据从新主节点进行复制操作安全性对数据比较重要的节点主节点可通过 requirepass 设置密码验证设置后所有客户端访问都必须使用 AUTH 命令进行校验从节点与主节点之间的复制连接是通过一个特殊标识的客户端完成的因此从节点需要配置 masterauth并与主节点密码保持一致只有这样从节点才能正确连接主节点并发起复制流程只读默认情况下从节点使用 slave-read-onlyyes即只读模式复制方向只能从主节点到从节点对从节点的任何修改主节点都无法感知修改从节点会造成主从数据不一致所以线上环境不建议修改从节点的只读模式传输延迟主从节点一般部署在不同机器上网络延迟需要重点考虑Redis 提供 repl-disable-tcp-nodelay 参数用于控制是否禁用 TCP_NODELAY默认值为 no即不禁用也就是开启 TCP_NODELAY 功能repl-disable-tcp-nodelayno 时主节点产生的命令数据无论大小都会及时发送给从节点主从延迟变小但会增加网络带宽消耗适用于网络环境良好的场景如同机房部署repl-disable-tcp-nodelayyes 时主节点会合并较小的 TCP 数据包从而节省带宽默认发送时间间隔取决于 Linux 内核一般约为 40 毫秒节省了带宽但会增大主从之间的延迟适用于网络环境复杂的场景如跨机房部署1.2 拓扑拓扑结构特点优点注意/缺点适用场景一主一从一个主节点 一个从节点简单支持故障转移主节点关闭持久化时需避免宕机后自动重启简单复制、故障转移一主多从一个主节点 多个从节点星形读写分离读命令可负载均衡写并发高时主节点需多次发送写命令负载加重读多写少树形主从从节点可继续作为下层的主节点降低主节点负载减少数据传输量结构更复杂管理成本更高从节点较多、需要分层减压1.3 PSYNC 机制与数据同步Redis 使用PSYNC命令完成主从数据同步PSYNC 的语法格式PSYNC replicationid offset如果 replicationid 设为 ? 并且 offset 设为 -1 此时就是在尝试进行全量复制.如果 replicationid offset 设为了具体的数值, 则是尝试进行部分复制.PSYNC 运行流程从节点向主节点发送PSYNC replid offset第一次复制时从节点没有主节点复制ID 和复制偏移量所以发送PSYNC ? -1主节点根据 PSYNC 参数和自身数据情况决定响应结果FULLRESYNC replid offset需要进行全量复制CONTINUE可以进行部分复制-ERR主节点版本过低不支持 PSYNC从节点改用 SYNC 进行全量复制使用特点PSYNC 一般不需要手动执行Redis 在主从复制模式下会自动调用SYNC 会阻塞 Redis Server 处理其他请求PSYNC 不会阻塞复制过程分为三个阶段建立连接从节点保存主节点信息 - 建立 TCP 连接 - 发送 PING - 权限验证数据同步首次连接触发全量复制网络闪断后触发部分复制命令传播后续主节点的写命令持续同步给从节点首次先全量复制再实时复制断线后先尝试部分复制再实时复制部分复制不行就退回全量复制然后继续实时复制。它们共同组成完整的主从复制流程核心概念Replid复制ID标识一个数据集。每个主节点重启或从节点晋升都会生成新的 replid。从节点会记录主节点的 master_replid 和 master_replid2用于网络分区后的数据找回Offset偏移量主从节点分别维护。主节点每执行一条写命令offset 累加从节点每秒上报自身 offset。replid offset 共同标识了唯一的数据集全量复制全量复制是 Redis 最早支持的复制方式也是主从第一次建立复制时必须经历的阶段全量复制流程从节点发送 PSYNC ? -1 给主节点主节点解析后判断需要全量复制回复 FULLRESYNC从节点接收并保存主节点的运行信息主节点执行 BGSAVE生成 RDB 文件主节点将 RDB 文件发送给从节点从节点保存 RDB 数据到本地硬盘主节点将“生成 RDB 到从节点接收完成”期间执行的写命令暂存到主节点上为该从节点维护的输出缓冲区中。等从节点保存并加载完 RDB 后主节点再把这些写命令发送给从节点从节点按顺序执行从而与主节点保持一致从节点清空自身原有旧数据从节点加载 RDB 文件得到与主节点一致的数据如果从节点开启了 AOF 持久化加载 RDB 完成后会执行 BGREWRITEAOF得到最新的 AOF 文件全量复制成本很高主节点 BGSAVE 时间RDB 网络传输时间从节点清空旧数据时间从节点加载 RDB 时间结论应尽量避免对已有大量数据集的 Redis 进行全量复制无磁盘复制主节点 bgsave 时不落盘直接通过网络发送 RDB节省磁盘 IO大内存场景慎用可能撑爆网卡部分复制部分复制是 Redis 针对全量复制过高开销做出的优化措施使用命令PSYNC replicationId offset适用场景从节点正在复制主节点时出现网络闪断或命令丢失等异常情况从节点向主节点要求补发丢失的命令数据部分复制流程主从节点之间网络中断超过 repl-timeout 时间后主节点认为从节点故障并中断复制连接主从连接中断期间主节点依然响应命令但这些复制命令无法及时发送给从节点于是暂时滞留在复制积压缓冲区中网络恢复后从节点再次连上主节点从节点将之前保存的 replicationId 和复制偏移量作为 PSYNC 参数发送给主节点请求部分复制主节点接到 PSYNC 请求后进行必要验证根据 offset 去复制积压缓冲区查找合适数据并回复 CONTINUE主节点将需要从节点同步的数据发送给从节点最终完成一致性复制积压缓冲区定义保存在主节点上的一个固定长度的队列默认大小为 1MB当主节点有连接的从节点时被创建写入机制主节点响应写命令时不但会把命令发送给从节点还会写入复制积压缓冲区作用保存最近已复制的数据用于部分复制和复制命令丢失的数据补救相关统计信息可通过主节点 INFO replication 查看repl_backlog_active:1 // 开启复制缓冲区 repl_backlog_size:1048576 // 缓冲区最大长度 repl_backlog_first_byte_offset:7479 // 起始偏移量 repl_backlog_histlen:1048576 // 已保存数据的有效长度可用偏移量范围[repl_backlog_first_byte_offset, repl_backlog_first_byte_offset repl_backlog_histlen]本质相当于一个基于数组实现的环形队列上述区间中的值就是“数组下标”重要限制如果当前从节点需要的数据已经超出主节点积压缓冲区的范围则无法进行部分复制只能全量复制实时复制与心跳实时复制主从节点建立复制连接后主节点会把自己收到的修改操作通过 TCP 长连接源源不断传输给从节点从节点根据这些请求同步修改自身数据从而保持与主节点数据一致心跳机制这种长连接需要通过心跳包维护连接状态这里的心跳是应用层自己实现的心跳。主从节点彼此都有心跳检测机制各自模拟成对方的客户端进行通信主节点心跳主节点默认每隔 10 秒对从节点发送 PING 命令用于判断从节点的存活性和连接状态从节点心跳从节点默认每隔 1 秒向主节点发送REPLCONF ACK {offset} 给主节点上报自身当前的复制偏移量超时判定如果主节点发现从节点通信延迟超过 repl-timeout 配置的值默认 repl-timeout 为 60 秒则判定从节点下线断开复制客户端连接从节点恢复连接后心跳机制继续1.4 解决的问题与缺点问题单点可用性问题单个 Redis 节点可用性不高故障后服务容易中断单点性能问题单个 Redis 节点性能有限读请求压力大时容易成为瓶颈通过主从复制可以为数据提供多个副本提升可用性和读性能缺点从节点过多时复制数据延迟会非常明显主节点挂掉后从节点不会自动升级为主节点只能通过人工干预恢复或额外引入 Sentinel、Redis Cluster 等机制实现自动故障转移1.5 QAQ主从复制是怎么实现的Redis 主从复制基于 PSYNC 命令实现从节点发送 PSYNC replid offset主节点根据复制 ID 是否一致、偏移量是否还在复制积压缓冲区内决定走全量复制还是部分复制同步完成后进入命令传播阶段主节点通过 TCP 长连接持续把写命令发给从节点从节点执行并累加自身偏移量同时主节点默认每 10 秒发 PING、从节点每秒回 REPLCONF ACK {offset}通过心跳维护连接和检测数据丢失Q全量复制和部分复制怎么选首次连接、复制 ID 不一致或者从节点要的 offset 已经不在主节点复制积压缓冲区内时只能走全量复制主节点 BGSAVE 生成 RDB 发给从节点期间写命令暂存到该从节点对应的输出缓冲区等从节点加载完 RDB 再补发如果复制 ID 一致且 offset 仍在复制积压缓冲区内就走部分复制只补发缺失的命令开销远小于全量复制Q频繁全量复制怎么办核心是调大复制积压缓冲区 repl-backlog-size它默认是 1MB 的环形队列网络抖动断连后如果从节点 offset 仍落在缓冲区内就能触发部分复制而不是全量复制可按“峰值写入速率 × 最大重连时间 × 2”估算生产环境常设为 64MB256MB高写入场景可更大同时可开无磁盘复制加快全量复制速度Q主从延迟怎么解决Redis 默认 repl-disable-tcp-nodelay no即开启 TCP_NODELAY主节点产生命令会立即发送给从节点减小延迟但跨机房部署时建议改为 yes 以节省带宽代价是内核合并小包默认约 40ms 延迟。此外还可优化网络、控制写入峰值、提升从节点性能、调大 repl-backlog-size并用 min-slaves-max-lag 等参数在延迟过大时拒绝写入避免主从差距继续扩大2. 哨兵机制Sentinel自动化的故障转移2.1 监控与下线判定 (SDOWN vs ODOWN)哨兵节点独立进程 redis-sentinel定期监控所有 Redis 数据节点其余哨兵节点是否可达主观下线 (SDOWN)单个哨兵发现主节点超过 down-after-milliseconds默认30秒没响应。此时可能仅仅是该哨兵网络不通客观下线 (ODOWN)该哨兵向其他哨兵发起投票sentinel monitor quorum 法定票数。票数 quorum 才能判定主节点真的挂了建议哨兵节点为奇数个至少3个因为 Raft 选举需要半数以上同意奇数可以避免平票提高选举效率防止脑裂什么是脑裂怎么解决脑裂是指网络分区后原 Master 与哨兵失联但客户端仍能连上原 Master 写入哨兵选出新 Master导致集群出现两个 Master 同时写。网络恢复后原 Master 降为 Slave期间写入的数据可能丢失缓解方案是在主节点配置min-replicas-to-write 1和min-replicas-max-lag 10表示如果健康从节点少于 1 个或从节点同步延迟超过 10 秒主节点就拒绝写命令。这能降低旧主在分区期间继续写入的概率但不能完全杜绝脑裂也不能保证数据绝对安全。还需要配合哨兵奇数部署、合理 quorum、客户端正确连接等措施2.2 故障转移与选举原理当主节点被判定为 ODOWN 后触发故障转移选举 Leader 哨兵哨兵之间选出 Leader。可以简单的认为谁先发起拉票网络延迟最小谁就大概率当选Leader 挑选新主节点从所有从节点中筛选规则依次为过滤掉长时间未通信的节点保证数据相对完整slave-priority 最高数值最小复制偏移量 offset 最大数据最新run id 最小字典序避免平票执行转移向新主发送 slaveof no one向其他从发送 slaveof newMaster并通知客户端旧主恢复旧主重启后会被哨兵作为从节点加入集群2.3 注意事项哨兵节点不能只有一个否则哨兵节点挂了也会影响系统可用性哨兵节点最好是奇数个方便选举 Leader得票更容易超过半数哨兵节点不负责存储数据仍然由 Redis 主从节点负责存储哨兵 主从复制解决的问题是“提高可用性”不能解决“数据极端情况下写丢失”的问题哨兵 主从复制不能提高数据的存储容量当需要存储的数据接近或超过机器的物理内存时这种结构就难以胜任。为了能存储更多数据就引入了集群2.4 QAQ哨兵是怎么判定主节点下线的Sentinel 会定期向主节点发送 PING如果超过 down-after-milliseconds 没收到有效回复单个 Sentinel 先把主节点标记为主观下线随后它会向其他 Sentinel 发送 is-master-down-by-addr 询问当认为主节点下线的 Sentinel 数量达到配置的 quorum 时就判定为客观下线进而触发故障转移流程Q客观下线后谁来做故障转移怎么选主客观下线后Sentinel 集群会先选举出一个 Leader Sentinel 来执行故障转移选举类似 Raft需要获得多数票且达到 quorum然后 Leader 从从节点中挑选新主节点优先级依次是slave-priority 最高的、复制偏移量 offset 最大的数据最新、runid 最小的选中后向该从节点发送 SLAVEOF NO ONE 将其提升为主节点再让其他从节点复制新主节点并通知客户端更新主节点地址Q哨兵集群节点数量为什么建议是奇数因为 Sentinel 判定客观下线和选举 Leader 都需要超过半数的同意奇数个节点更容易达到多数避免平票同时在容错能力和成本之间更优比如 3 个节点允许挂 1 个4 个节点虽然多了一个但多数仍是 3挂 2 个就失去多数所以奇数个更划算Q哨兵模式能解决写压力吗不能。哨兵 主从复制主要解决的是高可用性即主节点故障时自动切换写操作仍然只能在主节点执行从节点默认只读增加从节点或哨兵并不能分担写压力。要解决写压力需要引入分片集群比如 Redis Cluster3. Redis Cluster 集群海量数据的终极解法3.1 数据分片算法哈希槽Redis Cluster 引入多组 Master/Slave每组存一部分数据实现分布式存储哈希槽算法hash_slot crc16(key) % 16384。将所有数据映射到 16384 个槽位为什么是 16384节点间心跳包需要携带槽位信息位图。16384 个槽位使用 16384 / 8 2048 字节 (2KB) 的位图即可表示如果使用 65536则需要 8KB。频繁的心跳包中2KB 的开销是可接受的8KB 则过大Redis 作者建议集群节点不应超过 1000 个16384 个槽位足够 1000 个节点分配不用一致性哈希 一致性哈希容易产生数据倾斜且扩容时较难均匀分配不用简单哈希求余扩容时 N 变化几乎所有的 Key 都需要重新映射数据迁移成本极高3.2 集群故障判定与迁移故障判定节点间周期性 PING/PONG若 A 连不上 BA 标记 B 为 PFAIL主观下线A 向其他节点八卦若超过半数主节点认为 B 是 PFAIL则 B 标记为 FAIL客观下线集群宕机条件某个分片的主从全挂 / 某分片主挂且无从 / 超过半数的主节点挂掉故障迁移从节点检查自身是否有资格与主节点断连时间未超阈值休眠时间计算500ms [0, 500ms] 随机时间 rank * 1000msrank 与 offset 相关offset 越大rank 越小休眠越短越容易先拉票先醒来的从节点向所有主节点拉票获得半数以上投票后晋升为新 Master接管原 Master 的槽位3.3 集群扩容扩容add-node 加入新主节点 - reshard 重新分配槽位从旧节点搬运部分槽位给新节点 - add-node --cluster-slave 为新主添加从节点重定向机制客户端访问 Key 不在当前节点时会收到 MOVED 重定向。如果正在发生槽位迁移则可能收到 ASK 重定向临时重定向客户端需先发送 ASKING 命令。客户端必须使用 -c 参数启动Hash Tag如果业务需要多个 Key 原子的操作如事务、Lua 脚本必须让这些 Key 落在同一个槽位。可以使用 {user:1000}:name 和 {user:1000}:ageRedis 只计算 {} 内的内容确保它们落在同一个分片3.4 QAQ数据量暴增单机内存扛不住了怎么办官方方案是 Redis Cluster它把整个数据集划分为 16384 个哈希槽每个节点负责一部分槽位客户端根据 key 计算槽位并路由到对应节点Redis Cluster 是官方原生支持、去中心化的能同时解决容量和写压力问题Q一致性哈希有什么缺陷Redis 怎么分片一致性哈希虽然能在节点增减时只影响相邻数据但存在数据倾斜问题节点少时容易分布不均需要引入虚拟节点来平衡而且实现和运维相对复杂Redis 没有采用一致性哈希而是用哈希槽分片对 key 做 CRC16 计算然后对 16384 取模得到槽位每个节点负责一部分槽位槽位可以在节点间迁移扩容缩容时只需移动部分槽位客户端通过集群协议获取槽位映射实现去中心化路由Q为什么是 16384 个槽位主要权衡了心跳包大小和集群规模Redis Cluster 节点间通过 Gossip 协议交换信息心跳包里会携带槽位位图16384 个槽位用位图表示是 2KB而 65536 个槽位是 8KB心跳包太大会浪费带宽同时集群节点数通常不超过 1000 个16384 个槽位足够均匀分配槽位少也便于压缩、传播和迁移所以 16384 是带宽、规模和效率之间的最佳平衡点QCluster 集群怎么判定节点故障怎么扩容节点间通过 Gossip 协议定期发送 PING/PONG如果某节点超过 cluster-node-timeout 未响应其他节点先标记它为 PFAIL疑似下线当多数主节点都认为该节点 PFAIL 时就升级为 FAIL客观下线然后触发故障转移从节点发起选举获得多数主节点投票后提升为新主节点。扩容时先用 redis-cli --cluster add-node 把新节点加入集群再用 reshard 把部分槽位从旧节点迁移到新节点迁移过程中槽位有 MIGRATING 和 IMPORTING 状态客户端访问时通过 ASK 重定向保证数据正确4. 选型对比架构模式解决的核心问题核心优势致命缺点 / 边界主从复制数据热备份、读性能扩展简单、易配置、读写分离主节点宕机需人工介入写/存储受单机限制哨兵机制主从架构的自动化故障转移高可用HA无需人工干预客户端透明未解决写压力与存储容量配置略复杂Cluster 集群海量数据存储、高并发写入分布式分片水平扩展加机器即可高可用架构复杂客户端需支持重定向跨槽位操作受限
返回列表