Redis设计与实现——分布式Redis

简介: Redis Sentinel 和 Cluster 是 Redis 高可用与分布式架构的核心组件。Sentinel 提供主从故障检测与自动切换,通过主观/客观下线判断及 Raft 算法选举领导者完成故障转移,但存在数据一致性和复杂度问题。Cluster 支持数据分片和水平扩展,基于哈希槽分配数据,具备自动故障转移和节点发现机制,适合大规模高并发场景。复制机制包括全量同步和部分同步,通过复制积压缓冲区优化同步效率,但仍面临延迟和资源消耗挑战。两者各有优劣,需根据业务需求选择合适方案。

Redis Sentinel(哨兵)

Sentinel 的工作机制

  • 故障检测(Failure Detection)

    • 主观下线(Subjective Down):单个 Sentinel 实例检测到主节点在30 秒内无响应,标记其为 SDOWN

    • 客观下线(Objective Down):当超过 quorum的 Sentinel 实例确认主节点不可达,标记为 ODOWN,触发故障转移。

  • 领导者选举(Leader Election)

    • Raft 算法:Sentinel 使用类似 Raft 的算法选举领头 Sentinel(Leader),由 Leader 执行故障转移。

    • 选举条件:获得多数 Sentinel 实例的投票(> N/2 + 1,N 为 Sentinel 总数);避免多个 Sentinel 同时发起故障转移。

  • 故障转移(Failover)流程

    • 选择新主节点:Leader Sentinel 根据规则从从节点中选出新主节点:

      优先级(slave-priority 配置);复制偏移量最大(数据最新);运行 ID 字典序最小(最终裁决条件)。

    • 提升新主节点:向目标从节点发送 SLAVEOF NO ONE,使其成为主节点;等待新主节点确认角色切换。

    • 切换从节点复制关系:向其他从节点发送 SLAVEOF 命令,使其复制新主节点。

    • 更新配置:Sentinel 更新监控的主节点地址,并通知客户端。

  • 客户端服务发现

    • 连接流程:客户端向 Sentinel 查询当前主节点地址;客户端直接连接主节点,失败时重试查询。

    • SDK 支持:主流 Redis 客户端库(如 Jedis、Lettuce)内置 Sentinel 集成。

Sentinel 的架构与部署

  • Sentinel 部署模式

    • 推荐配置:至少部署 3 个 Sentinel 实例(奇数个,避免脑裂),分布在独立物理节点。

    • 网络拓扑:Sentinel 实例与 Redis 节点部署在同一网络,避免分区误判;Sentinel 之间通过 Gossip 协议通信,共享节点状态。

  • Sentinel 与 Redis 节点的关系

    • 监控对象:每个 Sentinel 监控 一个主节点及其所有从节点

    • 自动发现:Sentinel 通过主节点获取从节点列表,并持续监控其状态。

Sentinel 的优缺点

  • 优点

    • 自动化容灾:无需人工干预即可完成故障转移。

    • 高可用:Sentinel 自身多实例部署,避免单点故障。

    • 客户端透明:客户端通过 Sentinel 自动发现主节点,业务代码无需硬编码地址。

  • 缺点

    • 数据一致性:异步复制可能导致故障转移后数据丢失(已提交但未同步到从节点的数据)。

    • 复杂度:需部署多个 Sentinel 实例,配置和维护成本较高。

    • 脑裂风险:网络分区可能导致多个主节点并存(需合理配置 quorum 和节点分布)。

生产环境建议

  • 部署最佳实践

    • Sentinel 数量:至少 3 个实例,部署在独立物理节点或可用区。

    • 网络优化:确保 Sentinel 与 Redis 节点间低延迟通信,避免跨地域部署。

    • 监控告警:监控 Sentinel 日志和 INFO Sentinel 输出,关注 odown 事件和故障转移次数。

  • 避免脑裂的配置

    • 合理设置quorum:通常设为 N/2 + 1(N 为 Sentinel 总数)。

    • 调整min-slaves-to-write:主节点需至少同步到指定数量的从节点才接受写操作。

  • 客户端容错

    • 重试策略:客户端应实现重试逻辑,处理故障转移期间的短暂不可用。

    • 多语言 SDK:选择支持 Sentinel 的客户端库(如 Java 的 Jedis、Python 的 redis-py)。

常用命令

  • SENTINEL masters:查看主节点信息。
  • SENTINEL slaves {master}:查看从节点信息。
  • SENTINEL failover {master}:强制触发故障转移(无需ODOWN)。
  • SENTINEL sentinels {master}:查看 Sentinel 节点列表。

Redis集群

集群的架构与数据分片

  • 哈希槽(Hash Slot)分配

    • 分片规则:对键的 CRC16 值取模(CRC16(key) % 16384)确定所属槽位。
    • 哈希标签(Hash Tag):使用 {} 指定部分键名计算哈希值,强制相关键分配到同一槽。

    • 槽分配管理:集群启动时,槽均匀分配到主节点,可通过 CLUSTER ADDSLOTS 手动分配或自动平衡。

  • 节点角色

    • 主节点(Master):负责处理槽的读写请求,参与故障选举。

    • 从节点(Replica):复制主节点数据,主节点故障时接替其槽。

    • 集群模式节点:所有节点默认开启集群模式(cluster-enabled yes)。

  • 集群拓扑

    • 最小部署:至少 3 个主节点(每个主节点至少 1 个从节点),共 6 个节点。

    • 节点发现:节点通过 Gossip 协议交换状态信息(如 MEET 命令将节点加入集群)。

集群的工作机制

  • 客户端请求路由

    • Smart Client:客户端缓存槽与节点的映射关系,直接向目标节点发送请求,若节点返回MOVED重定向错误,更新缓存并重试。

    • Dumb Client:依赖代理(如 Redis Proxy)转发请求,客户端无需感知集群拓扑。

  • 数据读写流程

    • 键存在目标槽:直接由负责该槽的节点处理。

    • 键不在目标槽:返回 MOVED 错误,客户端重定向到正确节点。

    • 槽迁移中:返回 ASK 错误,客户端临时重定向到迁移目标节点。

  • 故障转移(Failover)

    • 主观下线(PFAIL):节点 A 在 cluster-node-timeout(默认 15 秒)内未收到节点 B 的响应,标记 B 为 PFAIL

    • 客观下线(FAIL):超过半数主节点确认节点 B 不可达,标记为 FAIL,触发故障转移。

    • 从节点选举:从节点发起选举,获得多数主节点投票后成为新主节点;接管原主节点的槽,并广播更新集群配置。

  • 数据迁移与平衡

    • 手动迁移:使用 CLUSTER SETSLOT <slot> IMPORTING/MIGRATING 命令迁移槽。

    • 自动平衡:通过 redis-cli --cluster rebalance 自动调整槽分布,均衡负载。

集群的优缺点

  • 优点

    • 水平扩展:支持 TB 级数据和高并发访问。

    • 高可用:自动故障转移,数据多副本存储。

    • 去中心化:无单点故障,节点自治。

  • 缺点

    • 功能限制:不支持跨槽事务、部分命令受限(如 KEYS *)。

    • 运维复杂度:需管理分片、迁移、节点扩缩容。

    • 客户端兼容性:需使用集群感知的客户端或代理。

生产环境建议

  • 部署与配置

    • 节点规划:主节点至少 3 个,跨物理机或可用区部署,从节点数 ≥ 主节点数。

    • 网络优化:确保节点间低延迟通信,避免跨地域部署。

    • 内存管理:监控节点内存使用,避免数据倾斜导致单个节点过载。

  • 数据均衡

    • 预分片:设计键时使用哈希标签,确保相关数据集中。

    • 定期平衡:使用 redis-cli --cluster rebalance 调整槽分布。

  • 监控与故障排查

    • 关键指标

      CLUSTER INFO        # 查看集群健康状态
      CLUSTER NODES       # 查看节点角色、槽分配、状态
      INFO memory         # 监控内存使用
      
    • 日志分析:关注 CLUSTERDOWN 告警和节点超时事件。

常用集群命令

  • CLUSTER NODES:查看集群节点信息。
  • CLUSTER INFO:检查集群状态。
  • CLUSTER FAILOVER:手动故障转移(从节点执行)。

Sentinel 与 Cluster 的对比

特性 Sentinel Cluster
数据分片 不支持,需客户端或代理分片。 内置分片(16384 槽)。
高可用 主从复制 + 故障转移。 每个分片主从复制,自动故障转移。
扩展性 垂直扩展(主节点性能瓶颈)。 水平扩展(支持大规模数据集和高吞吐)。
适用场景 中小规模,非分片架构。 大规模数据,高并发场景。

Redis复制

主从复制的建立流程

  • Redis 复制的建立分为 全量同步(Full Sync)部分同步(Partial Sync) 两种模式,优先尝试部分同步以减少资源消耗。

  • 从节点初始化连接

    • 命令触发:从节点执行 SLAVEOF <master-ip> <master-port>,开启复制流程。

    • 连接主节点:从节点向主节点发起连接,发送 PING 确认网络可达性。

  • 身份验证(可选):若主节点配置了 requirepass,从节点需发送 AUTH <password> 完成认证。

  • 同步数据集

    • 全量同步(SYNC)

      主节点生成当前数据的 RDB 快照,通过子进程写入磁盘。

      将 RDB 文件发送给从节点,同时缓存期间的写命令至 复制缓冲区(Replication Buffer)

      从节点接收 RDB 并加载到内存,再执行缓冲区中的写命令,追上主节点状态。

    • 部分同步(PSYNC)

      从节点发送 PSYNC <replid> <offset>,携带自身记录的复制 ID 和偏移量。

      主节点检查复制 ID 和偏移量是否匹配历史记录:

      匹配:发送 +CONTINUE,传输从偏移量之后的写命令(利用 复制积压缓冲区)。

      不匹配:触发全量同步(+FULLRESYNC)。

  • 命令传播(Command Propagation):同步完成后,主节点持续将写命令发送给从节点,保持数据一致。

复制的核心机制

  • 复制ID与偏移量

    • 复制ID(Replication ID):主节点的唯一标识,每次主节点重启或角色变更时生成新 ID。

    • 偏移量(Offset):主从节点各自维护一个偏移量,记录已复制的数据量。

  • 复制积压缓冲区(Replication Backlog)

    • 作用:主节点维护一个固定大小的环形缓冲区(默认 1MB),缓存最近的写命令。

    • 触发部分同步:若从节点的偏移量在缓冲区范围内,直接发送增量数据。

    • 配置参数

      repl-backlog-size 1mb     # 缓冲区大小
      repl-backlog-ttl 3600     # 主节点无连接时缓冲区保留时间(秒)
      
  • 心跳检测与断线重连

    • 心跳机制:主从节点定期确认存活状态和复制进度;主节点超时未收到心跳(默认 60 秒)则认为从节点下线。

    • 断线处理:从节点重连后尝试部分同步,失败则触发全量同步。

复制拓扑与高级特性

  • 级联复制(主-从-从)

    • 场景:主节点连接过多从节点时,可通过级联复制分摊压力。

    • 配置:将从节点(Slave A)作为另一从节点(Slave B)的主节点。

  • 延迟副本(Lagging Replica)

    • 作用:人为设置从节点延迟同步,用于误操作恢复(需第三方工具支持)。

    • 实现:通过 slave-repl-delay 配置延迟时间(Redis 自身不原生支持,需外部控制)。

复制的问题与优化

  • 全量同步的资源消耗

    • 问题:大数据集时生成和传输 RDB 文件会阻塞主节点并占用带宽。

    • 优化:增大 repl-backlog-size 减少全量同步概率;使用无盘同步(repl-diskless-sync yes),但需主节点内存充足。

  • 复制延迟

    • 原因:网络延迟、从节点负载过高或主节点写入压力大。

    • 监控:通过 INFO replicationslave_repl_offsetmaster_repl_offset 差值判断延迟。

    • 优化:提升网络带宽,减少主从节点跨地域部署;限制主节点写入速率(如使用管道批量写入)。

  • 数据不一致

    • 原因:主从网络中断导致部分数据未同步。

    • 检测:使用 redis-cli --slave 模拟从节点检查数据差异。

    • 修复:手动触发全量同步(SLAVEOF NO ONE + 重新配置复制)。

相关文章
|
3月前
|
数据采集 存储 数据可视化
分布式爬虫框架Scrapy-Redis实战指南
本文介绍如何使用Scrapy-Redis构建分布式爬虫系统,采集携程平台上热门城市的酒店价格与评价信息。通过代理IP、Cookie和User-Agent设置规避反爬策略,实现高效数据抓取。结合价格动态趋势分析,助力酒店业优化市场策略、提升服务质量。技术架构涵盖Scrapy-Redis核心调度、代理中间件及数据解析存储,提供完整的技术路线图与代码示例。
310 0
分布式爬虫框架Scrapy-Redis实战指南
|
29天前
|
数据采集 存储 NoSQL
基于Scrapy-Redis的分布式景点数据爬取与热力图生成
基于Scrapy-Redis的分布式景点数据爬取与热力图生成
175 67
|
4月前
|
NoSQL Java 中间件
【📕分布式锁通关指南 02】基于Redis实现的分布式锁
本文介绍了从单机锁到分布式锁的演变,重点探讨了使用Redis实现分布式锁的方法。分布式锁用于控制分布式系统中多个实例对共享资源的同步访问,需满足互斥性、可重入性、锁超时防死锁和锁释放正确防误删等特性。文章通过具体示例展示了如何利用Redis的`setnx`命令实现加锁,并分析了简化版分布式锁存在的问题,如锁超时和误删。为了解决这些问题,文中提出了设置锁过期时间和在解锁前验证持有锁的线程身份的优化方案。最后指出,尽管当前设计已解决部分问题,但仍存在进一步优化的空间,将在后续章节继续探讨。
711 131
【📕分布式锁通关指南 02】基于Redis实现的分布式锁
|
18天前
|
NoSQL 算法 安全
redis分布式锁在高并发场景下的方案设计与性能提升
本文探讨了Redis分布式锁在主从架构下失效的问题及其解决方案。首先通过CAP理论分析,Redis遵循AP原则,导致锁可能失效。针对此问题,提出两种解决方案:Zookeeper分布式锁(追求CP一致性)和Redlock算法(基于多个Redis实例提升可靠性)。文章还讨论了可能遇到的“坑”,如加从节点引发超卖问题、建议Redis节点数为奇数以及持久化策略对锁的影响。最后,从性能优化角度出发,介绍了减少锁粒度和分段锁的策略,并结合实际场景(如下单重复提交、支付与取消订单冲突)展示了分布式锁的应用方法。
82 3
|
1月前
|
数据采集 存储 NoSQL
分布式爬虫去重:Python + Redis实现高效URL去重
分布式爬虫去重:Python + Redis实现高效URL去重
|
18天前
|
存储 NoSQL Java
从扣减库存场景来讲讲redis分布式锁中的那些“坑”
本文从一个简单的库存扣减场景出发,深入分析了高并发下的超卖问题,并逐步优化解决方案。首先通过本地锁解决单机并发问题,但集群环境下失效;接着引入Redis分布式锁,利用SETNX命令实现加锁,但仍存在死锁、锁过期等隐患。文章详细探讨了通过设置唯一标识、续命机制等方法完善锁的可靠性,并最终引出Redisson工具,其内置的锁续命和原子性操作极大简化了分布式锁的实现。最后,作者剖析了Redisson源码,揭示其实现原理,并预告后续关于主从架构下分布式锁的应用与性能优化内容。
63 0
|
4月前
|
缓存 NoSQL 搜索推荐
【📕分布式锁通关指南 03】通过Lua脚本保证redis操作的原子性
本文介绍了如何通过Lua脚本在Redis中实现分布式锁的原子性操作,避免并发问题。首先讲解了Lua脚本的基本概念及其在Redis中的使用方法,包括通过`eval`指令执行Lua脚本和通过`script load`指令缓存脚本。接着详细展示了如何用Lua脚本实现加锁、解锁及可重入锁的功能,确保同一线程可以多次获取锁而不发生死锁。最后,通过代码示例演示了如何在实际业务中调用这些Lua脚本,确保锁操作的原子性和安全性。
228 6
【📕分布式锁通关指南 03】通过Lua脚本保证redis操作的原子性
|
3月前
|
NoSQL Redis
Redis分布式锁如何实现 ?
Redis分布式锁主要依靠一个SETNX指令实现的 , 这条命令的含义就是“SET if Not Exists”,即不存在的时候才会设置值。 只有在key不存在的情况下,将键key的值设置为value。如果key已经存在,则SETNX命令不做任何操作。 这个命令的返回值如下。 ● 命令在设置成功时返回1。 ● 命令在设置失败时返回0。 假设此时有线程A和线程B同时访问临界区代码,假设线程A首先执行了SETNX命令,并返回结果1,继续向下执行。而此时线程B再次执行SETNX命令时,返回的结果为0,则线程B不能继续向下执行。只有当线程A执行DELETE命令将设置的锁状态删除时,线程B才会成功执行S
|
4月前
|
运维 NoSQL 算法
【📕分布式锁通关指南 04】redis分布式锁的细节问题以及RedLock算法原理
本文深入探讨了基于Redis实现分布式锁时遇到的细节问题及解决方案。首先,针对锁续期问题,提出了通过独立服务、获取锁进程自己续期和异步线程三种方式,并详细介绍了如何利用Lua脚本和守护线程实现自动续期。接着,解决了锁阻塞问题,引入了带超时时间的`tryLock`机制,确保在高并发场景下不会无限等待锁。最后,作为知识扩展,讲解了RedLock算法原理及其在实际业务中的局限性。文章强调,在并发量不高的场景中手写分布式锁可行,但推荐使用更成熟的Redisson框架来实现分布式锁,以保证系统的稳定性和可靠性。
160 0
【📕分布式锁通关指南 04】redis分布式锁的细节问题以及RedLock算法原理
|
缓存 NoSQL Java
为什么分布式一定要有redis?
1、为什么使用redis 分析:博主觉得在项目中使用redis,主要是从两个角度去考虑:性能和并发。当然,redis还具备可以做分布式锁等其他功能,但是如果只是为了分布式锁这些其他功能,完全还有其他中间件(如zookpeer等)代替,并不是非要使用redis。
1417 0