Redis:从两大维度和三大主线建立知识体系

Redis 的知识点可以放进同一个框架中理解:

  • 应用维度回答 Redis 用来解决什么问题。
  • 系统维度回答 Redis 内部如何处理请求、管理内存和保存数据。
  • 高性能、高可靠、高可扩展三条主线把底层机制串联起来。

本文以 Redis Open Source 6.0 及之后版本的通用机制为主;涉及 Redis 7.0 起的多文件 AOF 等版本差异时单独说明。

一、两大维度

1. 应用维度

应用维度关注 Redis 在业务系统中的角色,主要包括三类应用。

缓存应用

缓存把读取频繁、生成成本高的数据放入更快的访问路径。设计缓存时需要明确:

  1. 缓存对象:适合缓存读取频繁、计算或查询成本高,并且允许短暂陈旧的数据。
  2. 过期时间:使用 TTL 控制生命周期,并增加随机抖动,避免大量 Key 同时失效。
  3. 更新策略:常见做法是先更新数据库,再删除缓存;删除失败时需要重试或消息补偿。
  4. 淘汰策略 :数据内存超过 maxmemory 后,根据访问模式选择近似 LRU、近似 LFU、随机淘汰或只淘汰带过期时间的 Key;noeviction 不淘汰 Key,而是拒绝会继续增加内存的写命令。
  5. 后端保护:缓存失效后,数据库侧仍需要限流、熔断、请求合并和降级措施。

缓存应用中的核心问题包括缓存一致性、缓存污染、缓存雪崩、缓存击穿和缓存穿透。

数据结构应用

Redis 不只是字符串缓存。不同数据结构对应不同的计算模型。

数据结构 主要能力 典型场景 注意点
String 计数、位操作、对象序列化 计数器、令牌、简单缓存 避免存放过大的序列化对象
Hash 字段级读写 对象属性、购物车 关注字段数量和底层编码转换
List 有序元素、阻塞读写 简单队列、任务列表 可靠消费能力有限
Stream 消息追加、消费组、确认机制 消息流、事件处理 需要管理消费者和待确认消息
Set 去重和集合运算 标签、共同关注、抽奖 大集合运算可能阻塞主线程
Sorted Set 按分值排序 排行榜、延迟队列 控制成员规模和范围查询结果量
Bitmap(基于 String) 位级记录与统计 签到、状态标记 适合连续、紧凑的整数 ID
HyperLogLog 近似基数统计 UV、去重计数 标准误差约为 0.81%,不能返回具体成员
GEO 地理位置索引 附近的人、门店距离 底层基于 Sorted Set
集群应用

当数据容量、访问吞吐或可用性要求超出单实例能力时,需要组合复制、哨兵和分片机制。

  • 主从复制提供数据副本和读扩展,但复制是异步的,从库不是强一致副本。
  • 哨兵机制负责监控、下线判断、领导者选举和主从故障切换。
  • Redis Cluster把数据分配到多个节点,以水平扩展容量和吞吐。

2. 系统维度

系统维度沿一次请求的处理链路拆解 Redis。

网络层

Redis 使用非阻塞套接字和 I/O 多路复用处理大量连接。Linux 上通常使用 epoll,内核只返回已经就绪的文件描述符,事件循环不需要逐个轮询连接。

Redis 6 开始支持 I/O 线程并行处理部分网络读写,但命令执行仍以串行方式为主。网络收发可以并行,不代表耗时命令可以并行执行。

处理层

Redis 通过事件循环接收请求并执行命令。串行执行带来两个特点:

  • 常规命令在核心执行路径中不会与其他命令交错,因此其数据修改具有原子执行语义;这不等于支持事务回滚,也不代表结果已经持久化或复制完成。
  • 不需要为共享数据结构引入大量线程锁和上下文切换。

相应的代价是主执行路径对慢操作非常敏感。一次耗时几十毫秒的命令会让后续请求排队。

线上需要重点关注:

  • 命令的时间复杂度。
  • 命令实际处理的元素数量。
  • 返回结果大小。
  • Lua 脚本执行时间。
  • BigKey 的读取、删除和迁移成本。

遍历线上数据时应优先使用 SCAN、HSCAN、SSCAN 和 ZSCAN。COUNT 只是每轮工作量和返回量的提示,不是严格上限;完整遍历期间若数据发生变化,还可能返回重复元素,因此调用方需要按业务要求去重。

内存层

Redis 对象包含类型、编码、引用计数以及 LRU/LFU 相关字段等元数据。同一种逻辑类型可以根据数据规模、元素大小和版本配置使用不同的底层编码,以平衡内存占用和操作效率。

数据规模超过阈值后,底层编码可能发生转换,进而改变内存占用和性能特征。

内存管理包括三个重要机制:

  1. 过期删除:惰性删除在访问 Key 时检查过期状态;定期删除周期性抽样清理过期 Key。
  2. 内存淘汰 :数据内存超过 maxmemory 且命令试图继续增加内存时,Redis 根据 maxmemory-policy 淘汰 Key,或者在 noeviction 下返回错误。复制和 AOF 等部分缓冲区不计入淘汰判断,因此配置时要为它们和系统开销预留内存。
  3. 碎片管理 :频繁分配和释放可能导致常驻内存高于实际数据内存。mem_fragmentation_ratio 同时受到分配器碎片和进程其他开销影响,需要结合 allocator_frag_ratio、allocator_rss_ratio、used_memory 与 RSS 判断,不能只凭一个比值下结论。
存储层

Redis 的主要持久化方式是 RDB 和 AOF。

RDB保存某一时刻的数据快照:

  • 文件紧凑。
  • 加载和恢复速度快。
  • 两次快照之间的数据可能丢失。
  • 生成快照通常依赖 fork 和写时复制。
  • 内存规模大、写入频繁时,需要关注 fork 延迟和复制页开销。

AOF通过可重放的操作记录恢复数据。传统 AOF 的增量部分记录会改变数据集的命令;Redis 7.0 起采用多文件 AOF,由一个基础文件、一个或多个增量文件以及清单文件组成,基础文件可以使用 RDB 或 AOF 格式:

  • 数据丢失窗口由刷盘策略决定。
  • appendfsync always 可靠性更高,但写入开销大。
  • appendfsync everysec 通常在性能和持久性之间取得平衡,异常宕机时通常可能丢失约 1 秒的数据。
  • AOF 重写根据当前内存数据生成能够重建现状的紧凑基础文件,并不是读取旧 AOF 后简单合并历史命令。
  • 重写期间会增加 CPU、内存和磁盘压力。

启用 aof-use-rdb-preamble 后,AOF 基础部分可以使用 RDB 格式,后续变化保存在增量 AOF 中,从而兼顾恢复速度和较小的数据丢失窗口。

二、三大主线

1. 高性能主线

Redis 的低延迟来自多种设计共同作用:

  • 数据主要存放在内存中。
  • 数据结构和底层编码针对常见操作做了优化。
  • 网络层使用非阻塞 I/O 和多路复用。
  • 核心命令执行路径避免了大量线程同步。
  • 单条命令通常短小且执行时间可预测。
性能风险

慢命令

大范围聚合、全量遍历、复杂 Lua 脚本和超大结果集都会占用主执行路径。

BigKey

BigKey 可能表现为单个 Value 很大,也可能表现为集合成员非常多。它会同时放大:

  • 网络传输成本。
  • 序列化和反序列化成本。
  • 删除和过期成本。
  • 主从复制流量。
  • RDB 和 AOF 成本。
  • 集群槽迁移时间。

持久化抖动

fork、写时复制、AOF 刷盘和 AOF 重写可能引起延迟尖峰。

系统环境

内存换页、透明大页、CPU 抢占、磁盘拥塞和网络丢包都会影响 Redis 延迟。

常用诊断入口
  • SLOWLOG:查看慢命令。
  • LATENCY DOCTOR:分析常见延迟来源。
  • INFO commandstats:查看各命令调用次数和耗时。
  • INFO memory:检查内存、碎片和淘汰情况。
  • CLIENT LIST:检查连接和客户端缓冲区。
  • redis-cli --bigkeys:通过 SCAN 遍历并按类型比较逻辑长度,适合发现成员多或字符串长的 Key,但它不等同于精确的内存占用排名;需要估算内存时可结合 MEMORY USAGE 或 redis-cli --memkeys。

诊断时要区分服务端执行时间、请求排队时间和网络往返时间。客户端观察到的延迟不等于命令本身的执行时间。

2. 高可靠主线

可靠性不是绝不丢数据,而是明确故障模型、可接受的数据丢失窗口和服务恢复时间。

数据持久性
  • 仅使用 RDB:恢复快,但数据丢失窗口通常较大。
  • 仅使用 AOF:数据更完整,但文件体积和恢复成本更高。
  • 使用 RDB 与 AOF,并让 AOF 基础文件采用 RDB 格式:兼顾备份能力、恢复速度与较小的数据丢失窗口,但最终保证仍取决于刷盘策略和故障类型。
主从复制

初次同步通常经历全量复制:

  1. 主库生成 RDB 快照;启用无盘复制时,子进程可以直接把 RDB 数据流发送给从库,而不先落本地磁盘。
  2. 从库接收并加载 RDB 数据集;加载大数据集时,从库仍可能出现阻塞窗口。
  3. 主库把快照期间累积的写命令发送给从库。
  4. 主从进入持续的增量复制状态。

连接短暂中断时,可以根据复制 ID、复制偏移量和复制积压缓冲区进行部分重同步,避免再次传输完整快照。

复制是异步的,因此存在两个边界:

  • 从库可能落后于主库,读取从库会得到陈旧数据。
  • 主库故障时,尚未复制到从库的数据可能丢失。

WAIT 可以等待指定数量的从库确认已接收当前连接此前的写入,从而降低故障时丢失写入的概率;确认接收不等于已持久化。它不会把 Redis 变成强一致系统,故障切换时仍不能保证已确认写入绝不丢失。

哨兵与故障切换

哨兵执行以下任务:

  1. 监控主库、从库和其他哨兵。
  2. 判断实例是否主观下线。
  3. 通过多个哨兵形成客观下线判断。
  4. 由多数哨兵授权并选举一个领导者执行故障转移;配置的 quorum 用于客观下线判断,不等同于执行故障转移所需的多数票。
  5. 选择合适的从库升级为新主库。
  6. 让其他从库改为复制新主库。

故障转移期间仍可能发生:

  • 短暂不可写。
  • 客户端继续连接旧主库。
  • 网络分区时旧主库可能在一段时间内仍可写,形成短暂双主;可用 min-replicas-to-write 和 min-replicas-max-lag 缩小风险窗口,但不能获得严格强一致。
  • 已确认写入仍可能丢失,实际范围取决于复制延迟、网络分区持续时间、持久化配置和客户端连接位置,不能笼统视为固定的"少量"。

客户端需要支持通过哨兵发现新主库、重新连接和有限重试。对超时后结果未知的写请求,盲目重试可能造成重复执行,业务操作还需要具备幂等性或请求去重能力。

3. 高可扩展主线

Redis Cluster 使用数据分片突破单机容量和吞吐上限。

哈希槽

Redis Cluster 使用 16384 个哈希槽。Key 对应的槽位为:

text 复制代码
slot = CRC16(effective_key) mod 16384

一般情况下 effective_key 就是完整 Key;使用有效 Hash Tag 时,它是第一对合法花括号中的非空内容。槽位被分配给不同主节点,集群扩缩容的本质是重新分配槽位,并迁移槽位中的 Key。

路由与重定向
  • MOVED:当前节点不是该槽位的负责节点,客户端应转向返回的节点,并刷新或修正本地槽位映射。
  • ASK :槽位正在迁移,客户端只把当前请求临时转向目标节点,并在发送实际命令前先发送 ASKING;客户端不应据此永久更新槽位映射。
  • Hash Tag :当 Key 包含符合规则的 {...} 且括号内非空时,只对其中的内容计算槽位,可以让相关 Key 落在同一槽中。
扩展边界
  • 跨槽多 Key 命令受到限制,数据建模时需要提前设计 Key 的共置关系。
  • 扩容不能自动消除热 Key,单个热点仍可能压垮一个分片。
  • BigKey 会拖慢槽迁移并放大网络和阻塞风险。
  • 集群节点使用 Gossip 传播状态,节点过多会增加集群总线流量和状态收敛成本。

热点数据可以通过本地缓存、Key 拆分、请求合并、限流或业务维度分片进行治理。

三、从问题反推底层机制

问题 首先检查 关键机制 常见治理方向
阻塞、延迟抖动 慢日志、延迟监控、CPU、fork、磁盘 事件循环、慢命令、持久化 拆分命令、异步删除、错峰持久化、治理 BigKey
数据丢失 AOF 刷盘策略、复制延迟、故障时间线 RDB、AOF、异步复制 缩小刷盘窗口、监控复制偏移、用 WAIT 或最小副本写入降低风险,但不把它们视为强一致保证
主从不一致 复制延迟、网络、从库负载 异步复制、部分重同步 关键读走主库、容忍陈旧读、避免从库过载
热 Key、秒杀热点 Key 访问频率、节点流量和 CPU 分片、串行命令执行 本地缓存、Key 拆分、请求合并、限流
缓存污染 命中率、Key 生命周期、访问频率 TTL、LRU、LFU 使用 LFU、缩短低价值数据 TTL、分离缓存池
缓存雪崩 失效时间分布、后端负载 过期删除、缓存重建 TTL 抖动、多级缓存、预热、限流和熔断
缓存击穿 单个热点 Key 是否失效 热点访问、缓存重建 互斥重建、逻辑过期、较长 TTL 配合主动刷新;避免无边界地让热点数据永不过期
缓存穿透 不存在 Key 的请求比例 缓存命中模型 缓存空值、参数校验、在应用侧或通过 RedisBloom 使用布隆过滤器
内存碎片 used_memory、RSS、分配器与 RSS 碎片指标 内存分配器、对象生命周期 评估并启用主动碎片整理、优化对象规模;必要时再采用受控的主从切换或滚动重启

诊断顺序

  1. 确认现象和监控指标。
  2. 判断问题位于客户端、网络、命令执行、内存、持久化、复制还是分片环节。
  3. 检查数据结构、Key 大小、元素数量和访问频率。
  4. 检查 RDB、AOF、复制、哨兵或集群配置。
  5. 回到业务访问模式,确认一致性、容量和延迟目标是否合理。

四、知识闭环

评估任何 Redis 方案时,可以依次回答以下问题:

  1. 应用角色:Redis 是缓存、状态存储、消息通道,还是分布式数据结构?
  2. 数据特征:Key 数量、Value 大小、读写比例、热点分布和生命周期是什么?
  3. 性能路径:命令复杂度、网络往返、序列化和持久化是否可能造成阻塞?
  4. 可靠性边界:允许丢多少数据、允许中断多久、是否接受陈旧读?
  5. 扩展方式:使用单机、主从、哨兵还是 Redis Cluster?
  6. 集群约束:跨槽操作、BigKey 和热 Key 如何处理?
  7. 可观测性:是否持续监控延迟、命中率、内存、复制、持久化和节点状态?

Redis 知识体系的重点不是记住更多命令,而是能够根据业务需求判断机制取舍,并从线上现象快速定位到请求链路中的具体环节。

相关推荐
禾小西2 小时前
Redis 数据结构:快速的 Redis 有哪些慢操作?
数据结构·数据库·redis
数据库小学妹2 小时前
数据共享交换平台选型:交换方式对比与避坑指南
数据库·信创·数据同步·数据交换平台·政务数据共享·数据共享交换平台·数据库底座
loong_XL4 小时前
决策模型做内容安全检测:从踩坑到上线
数据库·安全·jev·决策模型
这个DBA有点耶4 小时前
数据库双轨并行实战:全量并行策略、增量延迟控制、双向回切与一致性校验
数据库·架构·dba
adinnet20264 小时前
保单、赔付与渠道问数:保险经营数据如何实现按需查询
大数据·数据库·人工智能
云贝贝贝5 小时前
PostgreSQL 分区表:从设计到运维,大表不再卡
运维·数据库·postgresql
oradh5 小时前
Oracle固定执行计划的方法---SQL Plan Baseline
数据库·sql·oracle
数据库小学妹5 小时前
数据库高可用演练怎么做?稳态定义、注入点与中止条件
数据库·rto·高可用架构·运维体系·故障演练·容灾切换
zyseo85 小时前
谷歌SEO 站内搜索优化实战:把站内搜索词变成关键词金矿
java·服务器·数据库