redis-架构解析

在上一篇中,我们介绍了使用 C 语言编写的高性能服务网关 Nginx ,它负责统一管理门户系统的接入与流量调度。本篇我们将聚焦另一个同样基于 C 语言开发的高性能系统------Redis 。作为一款典型的 非关系型键值(Key-Value)数据库 ,Redis 常被用作缓存、消息队列或内存数据存储引擎,在高并发、低延迟场景中发挥着关键作用。

一、简介

Redis(Remote Dictionary Server)最初由意大利开发者 Salvatore Sanfilippo(网名 antirez)于 2009 年创建。其设计初衷、初始功能与架构充分体现了"简单、高效、实用"的工程哲学。

那么问题来了:当时已有成熟的缓存系统如 Memcached ,以及广泛使用的数据库如 MySQL ,为什么 antirez 还要从头打造一个全新的存储系统?

Redis 究竟在哪些方面超越了这些已有方案?它解决了什么实际痛点?

接下来,我们将深入探讨 Redis 诞生的背景及其相较于 Memcached 和传统关系型数据库的核心优势。

二、高性能架构

Redis 的高性能主要由以下三个方面共同决定:

1. 采用 C 语言开发,贴近底层

  • C 语言编译为机器码后执行效率极高,几乎没有运行时开销。
  • Redis 直接操作内存、系统调用和网络 I/O,避免了高级语言的垃圾回收(GC)停顿或虚拟机层损耗。
  • 代码高度优化,数据结构(如 SDS、ziplist、skiplist)均为自研,针对场景精简设计。

2. 基于内存的数据存储与访问

  • 所有数据默认驻留在内存中,读写延迟可达 微秒甚至纳秒级
  • 相比磁盘数据库(如 MySQL)需经历"磁盘 I/O → 内存缓存 → CPU 处理"的多层路径,Redis 省去了最慢的磁盘环节。
  • 这是 Redis 性能远超传统数据库的核心原因。

3. 单线程事件驱动模型,规避锁竞争

  • Redis 的命令执行阶段采用单线程(主线程),所有客户端请求串行处理。
  • 因为没有多线程并发修改共享数据,天然避免了锁机制、上下文切换和竞态条件,极大提升了执行效率和确定性。
  • 注意:从 Redis 6.0 起,网络 I/O(读写 socket)支持多线程 ,但命令解析与执行仍由主线程完成,核心逻辑保持单线程。

2.1 主流程解析

2.1.1 内存访问

首先,我们需要明确一个基本概念:不同存储介质的访问速度存在显著差异

在计算机体系结构中,访问速度从快到慢大致为:

寄存器 > 高速缓存(Cache) > 内存(RAM) > 硬盘(HDD/SSD)

其中,寄存器位于 CPU 内部,速度最快;内存次之,可被 CPU 快速读写;而硬盘(无论是机械硬盘还是固态硬盘)属于持久化存储,访问延迟远高于内存。

虽然高速缓存(L1/L2/L3 Cache)在实际系统中扮演着关键角色,但为了简化讨论,我们这里聚焦于 内存与硬盘 的性能差距------这也正是 Redis 这类内存数据库发挥价值的核心所在:将数据放在速度更快的内存中,以实现微秒级甚至纳秒级的响应能力

Redis 的高性能核心在于完全基于内存进行数据读写,避免了传统数据库频繁访问磁盘所带来的 I/O 延迟。正因如此,它在响应速度上远超大多数基于磁盘存储的关系型或非关系型数据库。

然而,也正因为数据主要驻留在易失性内存中,Redis 通常不适合作为系统唯一的持久化数据存储 。一旦发生宕机且未配置持久化机制,内存中的数据可能丢失。因此,在实际架构中,Redis 更常被用作缓存层,而将 MySQL、PostgreSQL 等具备强持久性和事务保障的数据库作为"真实数据源"。

为了高效利用有限的内存资源,Redis 支持为每个键(key)设置过期时间(TTL)。数据到期后会自动删除,这不仅防止内存无限增长,还能提升缓存命中率------通过淘汰冷数据,为热点数据腾出空间,从而在性能与资源之间取得良好平衡。

2.1.2 整体流程

下面用一幅图讲解整个redis的整个流程:

从上图可见,Redis 的核心是 单线程的事件循环(Event Loop),它统一处理两类事件:

  • 网络事件(File Events):负责接收客户端连接、读取命令请求、解析协议并执行命令;
  • 时间事件(Time Events):用于触发定时任务,如过期键清理、RDB 快照调度、AOF 同步等。

在持久化机制中:

  • RDB 快照 通过 fork() 创建一个独立的子进程来完成数据落盘,主线程仅负责发起和监控,不参与实际写入;
  • AOF 日志 的追加、缓冲区管理及同步(write/fsync)则完全由主线程串行执行。

这种设计的核心优势在于:

  • 避免多线程带来的锁竞争、上下文切换和内存一致性开销
  • 最大化利用 CPU 缓存局部性,提升单核执行效率
  • 命令处理模型简单:接收 → 执行 → 返回,无中间状态阻塞

因此,Redis 架构天然适合 高并发、低延迟、轻量级操作 的场景,尤其是 键值(KV)缓存、计数器、排行榜等简单数据模型

然而,这种单线程模型也存在局限性:

  • 不适合复杂业务逻辑,例如涉及多表关联、跨 key 事务、长时间计算等操作;
  • 一旦某个命令执行耗时过长(如 KEYS *、大 key 操作),会阻塞整个事件循环,影响其他请求
  • 无法充分利用多核 CPU 的并行能力(尽管 Redis 6.0+ 引入了 I/O 多线程,但命令执行仍为单线程)。

换句话说,Redis 的高性能是以 "简化执行模型"为前提 的。它并非通用数据库的替代品,而是作为 缓存层或特定场景的数据结构服务器 发挥最大价值。

流程简化下就是如下:

2.2 超时淘汰策略

Redis 的 超时(TTL)机制内存不足时的缓存淘汰策略(Eviction Policy) 是两个独立但互补的核心功能,共同保障 Redis 在有限内存下高效、稳定运行。

2.2.1 超时机制(Key Expiration / TTL)

自动删除"不再需要"的数据(如会话、临时令牌),避免内存无限增长。

实现方式:惰性删除 + 定期删除

1. 惰性删除(Lazy Expiration)
  • 触发时机:每次访问 key 时检查是否过期。
  • 优点:不浪费 CPU 周期在未访问的 key 上。
  • 缺点:长期不访问的过期 key 会残留,占用内存。

流程如下(在读取数据的时候判断是否超时):

2. 定期删除(Active Expiration)
  • 触发时机serverCron 定时任务(默认每秒 10 次)。
  • 策略:随机采样部分设置了 TTL 的 key,删除其中已过期的。
  • 防阻塞:每次最多执行 25ms(可配置),避免影响主线程。
  • 缺陷
    • 如果某个 key 长期未被访问(惰性删除不触发)
    • 在多次随机采样中都未被选中
    • 那么它即使已过期,仍会继续占用内存 ,直到:
      • 被随机采样命中,或
      • 被客户端访问(触发惰性删除),或
      • Redis 重启(RDB/AOF 不保存已过期但未删除的 key)。
复制代码

两者结合:既保证热点 key 及时清理,又防止冷 key 长期堆积。

2.2.2 内存不足时的缓存淘汰策略(Eviction Policy)

maxmemory 被设置且内存使用达到上限时,Redis 会在执行写命令前触发淘汰机制。

每个对象包含一个文件结构记录lru和lfu,这样来支撑淘汰策略

1、LFU(Least Frequently Used)
  • 每个对象有 24 位 LFU 计数器(高 16 位:上次衰减时间;低 8 位:访问频次)。
  • 频次会随时间衰减(避免老 key 永久驻留)。
  • 淘汰时:采样后选 频次最低 的 key。
2、近似 LRU(Approximated LRU)
  • Redis 不维护完整访问时间戳(内存开销大)。
  • 每个对象有一个 24 位 LRU 时钟redisObject.lru),记录最近访问的"逻辑时间"。
  • 淘汰时:随机采样 N 个 key(默认 5),选 LRU 时钟最小的淘汰
3、淘汰策略(8 种,通过 maxmemory-policy 配置)
策略 行为 适用场景
noeviction 不淘汰,写命令返回错误 禁止驱逐关键数据
allkeys-lru 从所有 key 中淘汰最近最少使用的 通用缓存
volatile-lru 仅从设置了 TTL 的 key 中 LRU 淘汰 混合持久化+缓存
allkeys-lfu 从所有 key 中淘汰最不常使用的(Redis 4.0+) 热点数据稳定
volatile-lfu 仅从带 TTL 的 key 中 LFU 淘汰 同上,但只针对临时数据
allkeys-random 随机淘汰任意 key 性能优先,不关心命中率
volatile-random 随机淘汰带 TTL 的 key 极简场景
volatile-ttl 优先淘汰剩余生存时间最短的 key 临时数据优先清理

2.2.3 总结

机制 触发条件 执行线程 是否主动 目的
超时(TTL) 时间到达 或 访问时 主线程 被动(惰性)+ 主动(定期) 清理临时数据
内存淘汰 内存 > maxmemory 主线程 主动(写命令前) 防止 OOM,保障服务

两者协同工作

  • TTL 管理"生命周期",
  • Eviction 管理"内存容量"。
    共同支撑 Redis 作为高性能缓存的核心能力。

2.3 持久化

RDB 和 AOF 是 Redis 提供的两种核心持久化机制,用于在服务重启后恢复数据

特性 RDB AOF
持久化方式 快照(二进制) 命令日志(文本)
数据安全性 较低(可能丢分钟级数据) 较高(可配最多丢 1 秒)
恢复速度 极快 较慢(需重放)
文件大小 大(但可重写压缩)
对性能影响 低(仅 fork 时) 中(everysec 可接受)
适用场景 备份、灾难恢复、主从同步 高可靠性要求、不能容忍数据丢失

2.3.1 RDB(Redis Database Backup)------ 快照式持久化

  • 在指定时间点,将内存中的完整数据集 生成一个二进制快照文件 (如 dump.rdb)。
  • 通过 fork() 创建子进程完成写入,主线程不阻塞 (仅 fork() 瞬间有轻微停顿)。

触发方式

方式 命令 说明
手动 SAVE 阻塞主线程,不推荐
手动 BGSAVE 后台异步执行,推荐
自动 配置 save 规则 save 900 1(900秒内至少1次修改则触发)
复制代码
# redis.conf 示例
save 900 1      # 15分钟内至少1次写入
save 300 10     # 5分钟内至少10次写入
save 60 10000   # 1分钟内至少10000次写入

优点:

  • 恢复速度快:直接加载二进制文件到内存。
  • 文件紧凑:适合备份、灾难恢复、主从全量同步。
  • 对性能影响小:子进程写入,主线程继续服务。

缺点:

  • 可能丢失数据:最后一次快照之后的写入会丢失(取决于 save 配置)。
  • 大数据集 fork 耗时 :内存越大,fork() 越慢(可能几十毫秒)

2.3.2 AOF(Append Only File)------ 日志式持久化

  • 每个写命令以 RESP 格式追加到 AOF 文件末尾。
  • 重启时重放所有命令重建数据集。

同步策略(appendfsync

策略 说明 安全性 性能
always 每次写命令都 fsync 到磁盘 最高(几乎不丢数据) 最低(磁盘 I/O 瓶颈)
everysec 每秒 fsync 一次 高(最多丢 1 秒数据) 推荐(平衡)
no 由操作系统决定 低(可能丢数秒数据) 最高
复制代码
# redis.conf
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec

优点:

  • 数据更安全:可配置为最多丢失 1 秒数据。
  • 可读性强:AOF 文件是文本格式,便于人工检查或修复。

缺点:

  • 文件体积大:尤其高频写入场景。
  • 恢复速度慢:需重放所有命令。
  • I/O 压力大 :尤其 always 模式。

AOF 重写(Rewrite)

Redis 会定期启动子进程生成精简版 AOF (只保留最新状态),避免文件无限增长。

例如:SET k 1 → SET k 2 → DEL k → 重写后为空。

2.3.3 混合

为兼顾两者优势,Redis 4.0 引入 RDB-AOF 混合模式

  • AOF 重写时,先写入 RDB 格式的全量数据,再追加增量 AOF 命令。

  • 重启时:先加载 RDB 部分,再重放 AOF 增量

    aof-use-rdb-preamble yes # 启用混合持久化(默认开启)

效果

  • 恢复速度接近 RDB;
  • 数据安全性接近 AOF。
机制 核心思想 适合谁
RDB "拍照存档" 追求速度、可容忍少量丢失
AOF "记账本" 追求安全、不能丢数据
混合模式 "先拍照,再记增量账" 大多数生产环境首选

2.4 数据格式

Redis 提供了 5 种核心数据结构 (String、Hash、List、Set、Sorted Set),以及 3 种高级扩展结构 (Bitmap、HyperLogLog、GEO),从 Redis 5.0 起还新增了 Stream。这些数据结构均基于内存优化设计,各具特色,适用于不同场景。

数据结构 是否有序 是否唯一 典型场景 内部实现
String - - 缓存、计数器 int / embstr / raw
Hash - 字段名唯一 对象存储 ziplist / hashtable
List (插入顺序) 无序 消息队列、timeline quicklist
Set 无序 唯一 标签、关系 intset / hashtable
ZSet (按 score) 唯一 排行榜、延时队列 ziplist / skiplist+hash
Bitmap (bit 位) - 签到、布隆过滤器 String
HyperLogLog - - UV 统计 String(概率算法)
GEO (按距离) 唯一 LBS 服务 ZSet
Stream (ID 递增) 唯一 可靠消息队列 Radix Tree + Listpack

2.4.1 String(字符串)

  • int:存储整数(如 SET counter 100
  • embstr:短字符串(≤44 字节),内存连续,只读优化
  • raw:长字符串(>44 字节),动态分配

特点:

  • 最基础的数据类型,可存字符串、整数、二进制数据(如图片)
  • 支持原子操作(INCRDECR

常用命令

命令 说明 时间复杂度
SET key value 设置值 O(1)
GET key 获取值 O(1)
INCR key 原子自增 O(1)
SETEX key ttl val 带过期时间设置 O(1)

应用场景

  • 缓存(HTML 片段、JSON 对象)
  • 计数器(文章阅读量、用户登录次数)
  • 分布式锁(SET lock nx ex
  • Session 存储

2.4.2 Hash(哈希表)

  • ziplist:小 Hash(字段少、值短),内存紧凑
  • hashtable:大 Hash,标准哈希表

特点

  • 存储对象的多个字段(类似 JSON 对象)
  • 只需一次网络往返即可获取/更新部分字段

常用命令

命令 说明 时间复杂度
HSET user:1001 name "Alice" 设置字段 O(1)
HGET user:1001 name 获取字段 O(1)
HMGET user:1001 name age 批量获取 O(N)
HGETALL user:1001 获取所有字段 O(N)

应用场景

  • 用户资料存储(避免序列化整个对象)
  • 购物车(HSET cart:uid item1 2 item2 1
  • 配置项管理

优势:比 String 存整个 JSON 更节省内存,且支持部分更新。

2.4.3 List(列表)

  • ziplist:小 List(元素少、值短)
  • quicklist(Redis 3.2+):ziplist 的双向链表,兼顾内存与性能

特点

  • 有序、可重复
  • 支持两端高效插入/弹出(O(1))
  • 可按索引访问(但中间操作慢)

常用命令

命令 说明 时间复杂度
LPUSH queue msg 左侧插入 O(1)
RPOP queue 右侧弹出 O(1)
LRANGE queue 0 9 获取范围 O(S+N)
LINDEX queue 5 按索引取 O(N)

应用场景

  • 消息队列 (简单场景,配合 BRPOP 阻塞消费)
  • 最新消息列表(如朋友圈 timeline)
  • 任务队列(生产者-消费者模型)

注意:List 不支持 ACK,消息可能丢失;高可靠场景用 Stream

2.4.4 Set(集合)

  • intset:纯整数集合,内存极省
  • hashtable:通用集合

特点

  • 无序、唯一、不重复
  • 支持集合运算(交、并、差)

常用命令

命令 说明 时间复杂度
SADD tags:news tech sports 添加元素 O(1)
SMEMBERS tags:news 获取所有元素 O(N)
SISMEMBER tags:news tech 判断存在 O(1)
SINTER set1 set2 求交集 O(N*M)

应用场景

  • 标签系统(文章打标签)
  • 好友关系(共同关注、可能认识的人)
  • 抽奖去重(SPOP 随机弹出)

2.4.5 Sorted Set(ZSet,有序集合)

  • ziplist:小 ZSet
  • skiplist(跳跃表) + hashtable:大 ZSet

跳跃表支持 O(log N) 插入/查询,同时用哈希表实现 O(1) 查分

特点

  • 每个元素关联一个 score(分数)
  • 自动按 score 升序排序
  • 元素唯一,score 可重复

常用命令

命令 说明 时间复杂度
ZADD leaderboard 100 "user1" 添加带分元素 O(log N)
ZRANGE leaderboard 0 9 获取 Top 10 O(log N + M)
ZRANK leaderboard user1 获取排名 O(log N)
ZINCRBY leaderboard 10 user1 增加分数 O(log N)

应用场景

  • 排行榜(游戏积分、热销商品)
  • 延时队列(score = 执行时间戳)
  • 带权重的任务调度

2.4.6 高级扩展结构(基于上述基础类型实现)

2.4.6.1. Bitmap(位图) → 基于 String
  • 本质是 bit 级别的 String

  • 命令:SETBIT, GETBIT, BITCOUNT, BITOP

  • 场景:用户签到、活跃度分析、布隆过滤器底层

    用户 123 在第 28 天签到(11月)

    SETBIT sign:123:202511 27 1
    BITCOUNT sign:123:202511 # 本月签到天数

2.4.6.2. HyperLogLog → 基于 String
  • 用于 基数统计(UV),误差 <1%,固定内存 ~12KB

  • 命令:PFADD, PFCOUNT, PFMERGE

  • 场景:统计网站日活、独立 IP 数

    PFADD uv:20251128 user1 user2 user3
    PFCOUNT uv:20251128 # 返回 ≈3

2.4.6.3. GEO(地理空间) → 基于 Sorted Set
  • 存储经纬度,支持距离计算、附近查询

  • 命令:GEOADD, GEORADIUS, GEODIST

  • 场景:附近的人、门店搜索

    GEOADD cities 116.40 39.90 "Beijing"
    GEORADIUS cities 116.41 39.91 10 km

2.4.6.4. Stream(Redis 5.0+)→ 独立数据结构
  • 持久化消息队列,支持消费者组、ACK、回溯

  • 命令:XADD, XREAD, XGROUP, XACK

  • 场景:可靠事件总线、日志管道、订单状态通知

    XADD orders * user_id 1001 amount 99.9
    XREAD GROUP order_group consumer1 STREAMS orders >

2.4.7 总结

  • 存单个值 → String
  • 存对象 → Hash
  • 需要队列/栈 → List (简单)或 Stream(可靠)
  • 去重集合 → Set
  • 排序/排行榜 → ZSet
  • 统计 UV → HyperLogLog
  • 用户行为(签到)→ Bitmap
  • 地理位置 → GEO

优先使用最贴合业务语义的数据结构,避免"用 String 存 JSON"导致无法高效操作字段。

三、高可用

Redis 的高可用架构核心围绕 "数据不丢失、服务不中断" 设计,主要通过 主从复制(数据冗余)、哨兵模式(自动故障转移)、集群模式(横向扩展 + 高可用) 三层架构实现。

3.1基础:主从复制(Master-Slave Replication)

1. 核心定位

主从复制是 Redis 高可用的 基础 ,核心作用是 数据冗余备份读写分离,解决 "单机故障导致数据丢失" 和 "单节点读写压力过载" 问题。

2. 架构图

3、配置

主节点(master.conf)
复制代码
# 监听所有 IP(生产环境建议指定内网 IP)
bind 0.0.0.0
port 6379
daemonize yes
requirepass "your_master_password"  # 可选:设置密码

# 关闭保护模式(若 bind 0.0.0.0)
protected-mode no
从节点(slave.conf)
复制代码
bind 0.0.0.0
port 6380                    # 可与主同机,端口不同
daemonize yes
protected-mode no

# 指定主节点(关键!)
replicaof 192.168.1.10 6379  # 主节点 IP 和端口

# 若主节点有密码
masterauth "your_master_password"

# 从节点只读(默认开启)
replica-read-only yes

启动命令

复制代码
redis-server master.conf
redis-server slave.conf
参数 说明 建议值
replicaof <ip> <port> 指定主节点 必填
masterauth <password> 主节点密码 若主设密码则必填
replica-read-only yes 从节点只读 保持 yes
repl-diskless-sync no 是否无盘复制(大内存建议 yes) yes(>10GB 内存)
repl-backlog-size 128mb 复制积压缓冲区大小 默认 1MB,高写入场景调大
min-replicas-to-write 1 至少 N 个从在线才允许主写 防脑裂(配合 Sentinel)
min-replicas-max-lag 10 从节点延迟 ≤10 秒 配合上一条使用

4. 核心工作原理

(1)复制流程(全量同步 + 增量同步)
  1. 初始化全量同步
    • 从节点启动时,发送 SYNC 命令给主节点;
    • 主节点执行 BGSAVE 生成 RDB 快照,同时缓存期间的写命令到 "复制缓冲区";
    • 主节点将 RDB 文件发送给从节点,从节点加载 RDB 完成数据初始化;
    • 主节点将复制缓冲区的写命令同步给从节点,从节点执行后与主节点数据一致。
  2. 运行时增量同步
    • 全量同步完成后,主节点每执行一次写命令,都会通过 "复制积压缓冲区" 异步同步给从节点;
    • 从节点通过偏移量(offset)记录已同步的命令,若网络中断,重连后可通过偏移量获取未同步的增量命令(避免全量同步)。
(2)关键特性
  • 读写分离:主节点负责写操作(SET、HSET 等),从节点负责读操作(GET、HGET 等),分散单节点压力;
  • 数据冗余:从节点是主节点的完整备份,主节点故障时可手动切换从节点为主节点;
  • 异步同步:主节点发送数据后不等待从节点确认,保证主节点写性能(存在短暂数据不一致窗口);
  • 级联复制:支持 "主→从→从" 的级联架构,减少主节点同步压力(适合从节点较多的场景)。

5. 优缺点

优点 缺点
架构简单,部署成本低 主节点故障后需手动切换(服务中断);
读写分离提升读并发(从节点可横向扩展) 不支持自动故障转移;
数据冗余,降低单机数据丢失风险 主节点是性能瓶颈(所有写请求集中在主节点);
支持级联复制,扩展灵活 存在数据不一致窗口(异步同步)。

5. 适用场景

  • 读多写少的场景(如商品详情页缓存、用户会话查询);
  • 数据备份需求(避免单机磁盘损坏导致数据丢失);
  • 对故障转移要求不高(可接受手动切换的短暂中断)。

3.2、进阶:哨兵模式(Sentinel)

1. 核心定位

哨兵模式是 主从复制的增强版 ,核心作用是 自动故障转移,解决 "主节点故障后手动切换" 的问题,实现 "服务不中断"。

访问流程

2. 架构图

3.配置

最少 3 个 Sentinel 节点(奇数,避免脑裂)

Sentinel 与 Redis 实例分开部署(避免共命运)

跨机器/机房部署 (防单点故障)

|---------|---------------------|------------------|
| 服务器 | redis | 哨兵 |
| 机器 A | Redis Master (6379) | Sentinel (26379) |
| 机器 B | Redis Slave1 (6379) | Sentinel (26379) |
| 机器 C | Redis Slave2 (6379) | Sentinel (26379) |

推荐:

  • 3 台 Redis(1 主 2 从)
  • 3 台独立机器运行 Sentinel(仅哨兵进程)

每个 Sentinel 节点使用相同配置(除 bind IP 外)

复制代码
# ================== 基础配置 ==================
port 26379
daemonize yes
protected-mode no
bind 0.0.0.0

# 日志(便于排查)
logfile "/var/log/redis/sentinel.log"
dir "/tmp"  # 用于保存临时文件和持久化配置

# ================== 监控主节点 ==================
# 格式:sentinel monitor <master-name> <ip> <port> <quorum>
# quorum = 判定主节点客观下线所需的 Sentinel 投票数
sentinel monitor mymaster 192.168.1.10 6379 2

# ================== 故障检测参数 ==================
# 主节点无响应超时(毫秒),触发主观下线(SDOWN)
sentinel down-after-milliseconds mymaster 30000

# 故障转移超时(毫秒)
# - 同一个主节点 failover 间隔
# - 从节点同步新主的超时
sentinel failover-timeout mymaster 180000

# 故障转移期间,并行同步从节点的最大数量
# 设为 1 避免主节点压力过大
sentinel parallel-syncs mymaster 1

# ================== 安全认证 ==================
# 若 Redis 主节点设置了 requirepass
sentinel auth-pass mymaster your_redis_password

# Sentinel 之间通信密码(可选)
# sentinel auth-user mymaster sentinel_user
# sentinel auth-pass mymaster sentinel_password

# ================== 通知脚本(可选) ==================
# 主节点切换时执行
# sentinel notification-script mymaster /path/to/notify.sh
# 故障转移完成时执行
# sentinel client-reconfig-script mymaster /path/to/reconfig.sh
参数 推荐值 说明
quorum N/2 + 1(N=Sentinel总数) 决定 ODOWN 的投票数,不用于选举 Leader
down-after-milliseconds 30000(30秒) 避免网络抖动误判
failover-timeout 180000(3分钟) 足够完成整个切换流程
parallel-syncs 1 防止新主被大量从节点同步压垮

quorum 设置示例

  • 3 个 Sentinel → quorum 2
  • 5 个 Sentinel → quorum 3

启动哨兵

复制代码
# 方式 1:使用 redis-sentinel 命令
redis-sentinel /etc/redis/sentinel.conf

# 方式 2:使用 redis-server --sentinel
redis-server /etc/redis/sentinel.conf --sentinel

验证哨兵状态

复制代码
# 连接任意 Sentinel
redis-cli -p 26379

# 查看监控的主节点
127.0.0.1:26379> SENTINEL MASTER mymaster
# 输出包含:主节点 IP、端口、状态、从节点列表等

# 查看所有 Sentinel 节点
127.0.0.1:26379> SENTINEL SENTINELS mymaster

# 查看从节点
127.0.0.1:26379> SENTINEL REPLICAS mymaster

防脑裂配置(在 Redis 主节点配置中)

复制代码
min-replicas-to-write 1      # 至少1个从在线才允许写
min-replicas-max-lag 10      # 从节点延迟 <=10秒

Java 示例(Jedis)

复制代码
Set<String> sentinels = Set.of(
    "192.168.1.10:26379",
    "192.168.1.11:26379",
    "192.168.1.12:26379"
);

JedisSentinelPool pool = new JedisSentinelPool("mymaster", sentinels, "password");
try (Jedis jedis = pool.getResource()) {
    jedis.set("key", "value");
}

4. 核心工作原理

哨兵集群的核心功能是 "监控、判断、选举、切换",流程如下:

(1)监控阶段
  • 每个哨兵节点通过 心跳检测(默认每 1 秒 PING 一次) 监控主节点和从节点的健康状态;
  • 哨兵节点之间通过 "发布订阅" 机制同步节点状态信息(如主节点是否存活、从节点偏移量等)。
(2)判断阶段(主观下线 + 客观下线)
  1. 主观下线(SDOWN):单个哨兵节点连续多次(默认 3 次)未收到主节点的 PONG 响应,认为主节点 "主观下线";
  2. 客观下线(ODOWN):当超过半数(quorum 配置,默认 1)的哨兵节点都认为主节点主观下线时,触发 "客观下线"(确认主节点确实故障)。
(3)选举阶段(Leader 选举)
  • 客观下线后,哨兵集群通过 Raft 算法 选举出一个 "哨兵领导者"(Leader),由其执行故障转移操作(避免多个哨兵同时切换);
  • 选举规则:获得超过半数哨兵投票的节点成为 Leader(因此哨兵节点数建议为奇数,避免脑裂)。
(4)切换阶段(故障转移)
  1. 哨兵 Leader 从所有从节点中筛选出 "最优从节点"(优先选择偏移量最大、健康状态最好、配置优先级最高的从节点);
  2. 向该从节点发送 SLAVEOF NO ONE 命令,将其提升为新主节点;
  3. 向其他从节点发送 SLAVEOF 新主节点地址 命令,让它们同步新主节点的数据;
  4. 向客户端发送 "主节点地址更新" 通知(客户端需通过哨兵获取主节点地址,而非硬编码);
  5. 原故障主节点恢复后,自动成为新主节点的从节点。

5. 关键特性

  • 自动故障转移:主节点故障后,哨兵集群自动完成切换(通常 10-30 秒内恢复服务);
  • 高可用哨兵集群:哨兵节点本身也支持集群部署(至少 3 个节点),单个哨兵故障不影响整体功能;
  • 客户端路由:客户端通过哨兵集群获取当前主节点地址,无需手动维护主从关系;
  • 监控告警:支持配置告警脚本,当节点故障时触发邮件 / 短信通知。

6. 优缺点

优点 缺点
自动故障转移,服务不中断 架构较主从复制复杂,部署和维护成本高;
哨兵集群高可用,无单点故障 主节点仍是写性能瓶颈;
支持监控告警,便于运维 数据同步仍为异步,存在短暂不一致;
兼容主从复制的读写分离 故障转移期间可能出现短暂的读服务不可用。

7. 适用场景

  • 对服务可用性要求高(不能接受手动切换的中断);
  • 读多写少的场景(可通过扩展从节点提升读并发);
  • 数据量中等(单主节点可承载),无需横向扩展写性能。

3.3 高级:集群模式(Redis Cluster)

1. 核心定位

Redis Cluster 是 Redis 官方提供的 分布式集群方案 ,核心作用是 横向扩展(分片存储)+ 高可用,解决 "单主节点写性能瓶颈" 和 "海量数据存储" 问题。

2. 架构图

cluster集群

3. 配置

最小生产配置(高可用):

  • 6 个节点:3 主 + 3 从(每个主有 1 个从)
  • 至少 3 台机器(防单机故障)
  • 端口规划
    • Redis 实例:7000~7005
    • Cluster Bus 端口 = Redis 端口 + 10000(如 7000 → 17000)

|---------|------------|-----------|
| 服务器 | master | slave |
| 机器 A | 7000 | 7001 |
| 机器 B | 7002 | 7003 |
| 机器 C | 7004 | 7005 |

必须满足

  • 每个主节点至少 1 个从节点
  • 主从不能在同一台机器(否则机器宕机导致双挂)
1)、准备配置文件(redis-cluster.conf)

为每个节点创建独立配置(以 7000.conf 为例):

复制代码
# 基础配置
port 7000
bind 0.0.0.0
daemonize yes
protected-mode no
dir /var/lib/redis/7000
logfile /var/log/redis/7000.log

# 启用集群模式(关键!)
cluster-enabled yes

# 集群配置文件(自动生成,勿手动修改)
cluster-config-file nodes-7000.conf

# 节点超时(毫秒)
cluster-node-timeout 15000

# 是否允许从节点迁移(自动平衡)
cluster-migration-barrier 1

# 安全选项(可选)
requirepass "your_password"
masterauth "your_password"  # 从节点连接主节点密码

为 7001~7005 创建相同配置,仅修改 portdirlogfilecluster-config-file

2)、启动所有节点
复制代码
redis-server /etc/redis/7000.conf
redis-server /etc/redis/7001.conf
# ... 启动 7002~7005
3)、创建集群(使用 redis-cli)

Redis 5.0+ 内置 --cluster 命令,无需 ruby

复制代码
# 创建 3 主 3 从集群(--cluster-replicas 1 表示每个主配 1 从)
redis-cli --cluster create \
  192.168.1.10:7000 192.168.1.10:7001 \
  192.168.1.11:7002 192.168.1.11:7003 \
  192.168.1.12:7004 192.168.1.12:7005 \
  --cluster-replicas 1 \
  -a your_password  # 若设置了密码

输出

复制代码
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 192.168.1.10:7001 to 192.168.1.10:7000
...
[OK] All 16384 slots covered.
4)、查看集群信息
复制代码
# 连接任意节点
redis-cli -p 7000 -a your_password

# 查看集群状态
127.0.0.1:7000> CLUSTER INFO

关键字段:

复制代码
cluster_state:ok          # 必须为 ok
cluster_slots_assigned:16384
cluster_known_nodes:6
cluster_size:3            # 主节点数量

查看节点列表

复制代码
127.0.0.1:7000> CLUSTER NODES

输出示例

复制代码
a1b2c3... 192.168.1.10:7000@17000 master - 0 1710000000 1 connected 0-5460
d4e5f6... 192.168.1.10:7001@17001 slave a1b2c3... 0 1710000000 2 connected
  • master / slave:角色
  • connected 0-5460:负责的 slot 范围
  • @17000:Cluster Bus 端口

Cluster 模式下,客户端必须支持 Cluster 协议(自动重定向)。

Java(Lettuce)

复制代码
RedisURI uri1 = RedisURI.create("redis://:your_password@192.168.1.10:7000");
RedisURI uri2 = RedisURI.create("redis://:your_password@192.168.1.11:7002");
RedisURI uri3 = RedisURI.create("redis://:your_password@192.168.1.12:7004");

RedisClusterClient clusterClient = RedisClusterClient.create(Arrays.asList(uri1, uri2, uri3));
StatefulRedisClusterConnection<String, String> connection = clusterClient.connect();
RedisAdvancedClusterCommands<String, String> commands = connection.sync();

commands.set("key", "value");
String value = commands.get("key");

禁止 使用普通 Redis 客户端(会报 MOVED 错误)!

4. 核心设计原理

(1)数据分片:哈希槽(Hash Slot)
  • Redis Cluster 将所有数据划分为 16384 个哈希槽(0-16383),每个主节点负责一部分槽位(如 3 主节点时,每个节点负责~5461 个槽);
  • 数据路由规则:客户端对键执行 CRC16(key) % 16384 计算,得到槽位后,将请求路由到负责该槽位的主节点;
  • 槽位迁移:支持在线槽位迁移(如扩容时新增主节点,可将原有槽位迁移到新节点),不影响服务可用性。
(2)高可用机制(内置哨兵逻辑)
  • 每个主节点至少配置 1 个从节点(示例为 3 主 3 从),从节点同步主节点的槽位数据;
  • 主节点故障时,集群自动触发故障转移(逻辑与哨兵模式类似):
    1. 从节点检测到主节点故障,发起故障转移请求;
    2. 其他主节点投票确认(超过半数同意)后,将从节点提升为新主节点;
    3. 新主节点接管原主节点的槽位,继续提供服务;
  • 集群总线:节点之间通过专门的集群总线(默认端口 16379+10000)通信,用于传递节点状态、槽位信息、故障检测等(避免占用业务带宽)。
(3)关键特性
  • 写性能横向扩展:写请求分散到多个主节点,集群整体写性能随主节点数线性提升;
  • 海量数据存储:数据分片存储在不同节点,突破单节点内存限制(如 3 主节点可存储 3 倍于单节点的数据);
  • 自动故障转移:内置哨兵逻辑,无需额外部署哨兵集群;
  • 客户端路由优化:支持 "智能客户端"(如 JedisCluster、Lettuce),客户端缓存槽位映射关系,直接路由到目标节点(减少转发开销);
  • 跨节点操作支持
    • 多键操作(如 MGET key1 key2):若 keys 属于同一槽位,直接执行;若属于不同槽位,需客户端分批执行(或使用 HASHSLOT 标签强制路由到同一槽位);
    • 事务 / Lua 脚本:仅支持单槽位操作(保证原子性)。

5. 优缺点

优点 缺点
写性能横向扩展(突破单主瓶颈) 架构复杂,部署和维护成本高;
海量数据存储(分片机制) 跨槽位操作受限(多键事务、Lua 脚本);
内置高可用(自动故障转移) 客户端需支持集群协议(部分老客户端不兼容);
在线扩容 / 缩容(槽位迁移) 数据一致性仍为异步同步(主从之间);
无中心节点(去中心化) 运维门槛高(需关注槽位均衡、节点状态)。

6. 适用场景

  • 写并发高的场景(如秒杀系统、高频写日志);
  • 数据量巨大(单节点内存无法承载);
  • 对服务可用性和扩展性要求都高的核心业务(如电商订单、支付系统)。

3.4 三者对比与选型建议

1. 核心特性对比表

特性 主从复制 哨兵模式 集群模式
核心作用 数据冗余 + 读写分离 自动故障转移(主从增强) 分片存储 + 横向扩展 + 高可用
写性能 单主节点,瓶颈明显 单主节点,瓶颈明显 多主节点,线性扩展
数据容量 单节点内存限制 单节点内存限制 分片存储,无上限(理论)
故障转移 手动切换 自动切换(哨兵集群) 自动切换(内置哨兵逻辑)
部署复杂度 低(1 主 N 从) 中(3 哨兵 + 主从) 高(至少 3 主 3 从)
跨节点操作 支持(主从数据一致) 支持(主从数据一致) 受限(仅单槽位支持事务 / 脚本)
适用并发 读并发中等,写并发低 读并发高,写并发低 读 / 写并发均高

选择建议:

  • 小团队 / 初创公司:核心业务用 "哨兵模式"(平衡可用性和维护成本),非核心业务用 "主从复制";
  • 中大型公司:核心业务用 "集群模式"(3 主 3 从起步,支持横向扩展),搭配 "哨兵模式" 监控集群状态;
  • 运维能力有限:优先选择云厂商托管的 Redis 集群(如阿里云 Redis、AWS ElastiCache),无需关注底层部署和故障转移;
  • 数据一致性要求高:搭配 Redis 事务 / Lua 脚本(单槽位操作),或使用分布式锁(如 RedLock)保证跨节点数据一致性。

Redis 高可用架构的演进逻辑是:主从复制(数据冗余)→ 哨兵模式(自动故障转移)→ 集群模式(横向扩展)。三者并非互斥关系,而是层层递进的增强:

  • 主从复制是基础,解决 "数据不丢失";
  • 哨兵模式是主从的增强,解决 "服务不中断";
  • 集群模式是分布式方案,解决 "写性能瓶颈" 和 "海量数据存储"。

四、总结与建议

Redis 作为一款高性能、内存优先的键值存储系统,凭借其简洁的架构设计、丰富的数据结构支持、灵活的持久化机制和成熟的高可用方案,已成为现代高并发系统中不可或缺的核心组件。

从最初为解决 Memcached 功能单一、MySQL 性能瓶颈而诞生,到如今支撑亿级用户规模的互联网应用,Redis 的演进体现了"在正确场景做正确的事"这一工程哲学:

  • 性能极致优化:基于 C 语言、单线程事件循环、内存操作,使其在微秒级响应场景中无可替代;
  • 功能高度聚焦:不追求通用数据库的复杂事务与强一致性,而是围绕缓存、队列、计数、排行榜等高频场景深度打磨;
  • 高可用层层递进:从主从复制 → 哨兵自动故障转移 → 集群分片扩展,形成一套完整、可落地的容灾与扩展体系;
  • 生态广泛兼容:主流语言均提供成熟客户端,云厂商全面托管,极大降低使用门槛。

然而,Redis 并非"万能药"。它的优势建立在明确边界之上:

  • 不适合存储海量冷数据(内存成本高);
  • 不支持跨 key 强事务(集群模式下仅限单槽);
  • 单线程模型对大 key 或慢命令极其敏感。

因此,在实际架构中,Redis 应定位为"加速层"而非"唯一数据源"。最佳实践是:

以 MySQL/PostgreSQL 等关系型数据库为"真实数据源",以 Redis 为高性能缓存与实时计算引擎,二者协同,各司其职。

未来,随着 Redis Stream、Functions(Redis 7+)、多线程 I/O 等特性的持续演进,其在消息队列、轻量级计算等领域的角色将进一步强化。但无论功能如何扩展,"简单、高效、可靠" 的初心,仍是 Redis 能够长期领跑 NoSQL 领域的根本原因。

相关推荐
河南博为智能科技有限公司23 分钟前
高集成度国产八串口联网服务器:工业级多设备联网解决方案
大数据·运维·服务器·数据库·人工智能·物联网
Wang's Blog37 分钟前
MongoDB小课堂: 深度诊断与优化——响应时间、内存压力及连接数故障全方位解决指南
数据库·mongodb
z***02601 小时前
MySQL--》如何通过选择合适的存储引擎提高查询效率?
数据库·mysql·adb
SoleMotive.1 小时前
在 MySQL 中如何快速的去复制一张表,包括表结构和数据?
数据库
翔云 OCR API1 小时前
承兑汇票识别接口技术解析-开发者接口
开发语言·前端·数据库·人工智能·ocr
w***15311 小时前
ubuntu 安装 Redis
linux·redis·ubuntu
shan~~1 小时前
mysql迁移到翰高数据库
数据库·mysql
p***32351 小时前
一条sql 在MySQL中是如何执行的
数据库·sql·mysql
yeshihouhou2 小时前
redis 单机安装(linux)
数据库·redis·缓存