从单线程到云原生:Redis 二十年演进全景与内在机理深剖

——从 1.0 到 7.2,一窥数据结构、网络模型、持久化、复制、高可用与生态协同的底层脉络

(一)序章:为什么是 Redis
1999 年,Salvatore Sanfilippo 在开发一个实时访客分析系统时,发现传统磁盘型数据库无法在毫秒级响应 100 万并发连接。于是,他写了一个将全量数据放在内存、用 C 语言实现、单线程处理命令、支持简单文本协议的服务——这就是 Redis 的原型。二十年后,Redis 已不仅仅是“缓存”的代名词,而是横跨缓存、消息总线、流处理、机器学习特征存储、实时排行榜、地理位置服务等众多场景的“瑞士军刀”。

本文不贴源码,也不给调参清单,而是回到设计哲学:为什么 Redis 选择单线程?为什么后来又要引入多线程 I/O?为什么跳表而不是 B+ 树?AOF 与 RDB 能否合二为一?主从复制与哨兵、集群、Raft 之间的演进逻辑是什么?带着这些问题,我们穿越版本号,拆解 Redis 的骨骼与血脉。

(二)数据结构:不止是 key-value
2.1 字符串(SDS)
Redis 没有复用 C 字符串,而是自建简单动态字符串 SDS。预分配、惰性释放、二进制安全、O(1) 长度获取,这些看似微小的优化,奠定了所有复合类型的基石。

2.2 字典
哈希表 + 渐进式 rehash。负载因子 1 时开始扩容,0.1 时缩容;rehash 期间采用“分槽迁移”策略,避免一次性拷贝导致延迟抖动。

2.3 跳表
实现有序集合 ZSET 的核心。多层索引、概率平衡、范围查询 O(logN)。跳表比 B+ 树省内存,且实现简单,但范围查询的 CPU cache 局部性稍逊。Redis 在 7.0 引入 listpack + 跳表混合编码,将元素 < 64 字节且个数 < 128 的小 ZSET 压缩为连续内存块,从而减少 30% 内存。

2.4 压缩列表 → quicklist → listpack
早期 list 用 ziplist 保存,连锁更新问题严重;3.2 引入 quicklist(双向链表 + ziplist 节点);5.0 以后用 listpack 取代 ziplist,彻底消除连锁更新。

2.5 HyperLogLog、Bitmap、Geo、BloomFilter、TDigest、CuckooFilter
这些模块化数据结构展示了 Redis “数据结构服务器”的定位:把学术界算法工程化,暴露成几条命令即可落地业务。

(三)网络模型:单线程、I/O 多路复用、最近的多线程
3.1 单线程事件循环
Reactor 模式 + epoll/kqueue。命令执行阶段在单线程完成,天然避免锁。瓶颈不在 CPU,而在系统调用和内存带宽。

3.2 连接风暴与 I/O 多线程
6.0 引入 I/O 多线程:主线程仍负责命令执行,网络 read/write 可由线程池并行化。通过“无锁队列 + 原子操作”传递 client 对象,避免上下文切换开销。实测在 128 并发连接、value 1 KB 场景下,QPS 从 12 万提升到 45 万。

3.3 TLS、Unix Socket、RESP3
TLS 握手开销大,Redis 通过“延迟启动 TLS”优化:先接受普通连接,AUTH 成功后再升级。RESP3 引入属性、推送、大数类型,为客户端提供类型安全与流式通知。

(四)持久化:RDB、AOF 与混合范式
4.1 RDB 快照
fork + copy-on-write。父进程继续服务,子进程遍历哈希表写磁盘。大实例 fork 延迟可达百毫秒,Redis 7 引入 “lazy free” 后台线程异步释放页表,减少 80% 阻塞时间。

4.2 AOF 日志
4.0 以前 AOF 是命令重放;4.0 支持 RDB-AOF 混合:前半段是 RDB,后半段是增量命令。重写时用 “copy-on-write + pipe” 边生成边传输,避免双倍内存。

4.3 fsync 策略
always、everysec、no。云盘场景下 everysec 也可能阻塞毫秒级,Redis 6.2 支持 “aof-fsync-in-thread” 将 fsync 移到后台线程。

4.4 重启恢复流程
先加载 RDB 构建基线,再重放 AOF 增量。Redis 7.2 引入 “AOF manifest” 记录多个 AOF 文件顺序,实现并行加载,提速 40%。

(五)主从复制:全量、部分、PSYNC2
5.1 SYNC 与 PSYNC
2.8 以前全量复制;PSYNC 引入 backlog 环形缓冲区,支持断线续传。backlog 大小 = 平均写速率 * 容忍宕机时间。

5.2 PSYNC2
4.0 解决级联复制场景下 “主从切换后新主仍能与旧主部分重同步” 的问题。通过 replid + offset 双标识,实现“多主历史”追踪。

5.3 无盘复制
5.0 支持 “diskless replication”,子进程直接把 RDB 通过 socket 流式发送到 replica,省去磁盘临时文件。

(六)高可用:哨兵、集群、Raft、联邦
6.1 Sentinel
哨兵本质是分布式仲裁系统:监控、通知、自动故障转移、配置提供者。raft-style leader 选举,但不需要持久化日志。

6.2 Cluster
16384 槽位、CRC16 算法映射、客户端重定向、ASK/MOVED 语义。节点间采用 Gossip + 心跳 + 故障报告机制。扩容/缩容通过 “槽迁移” 实现:迁移过程中 key 临时存在于源、目标两节点,客户端通过 ASKING 命令解决双重应答。

6.3 RedisRaft
Redis 官方实验项目,将 Raft 日志嵌入 Redis 内部命令流,实现强一致。牺牲性能换 CP,适用于金融场景。

6.4 联邦模式
在云厂商实践中,常见“代理层分片”:twemproxy、codis、predixy。优势是对客户端零侵入;劣势是代理层成为瓶颈,且无法支持多 key 原子事务。

(七)内存管理:jemalloc、内存碎片、逐出策略
7.1 jemalloc vs tcmalloc
jemalloc 的 arena 机制减少多线程竞争,但 4.x 版本存在 huge page 浪费;Redis 6 引入 “activedefrag” 后台线程,周期性搬迁对象,降低碎片率。

7.2 逐出算法
LRU、LFU、TTL、random。LFU 用 Morris counter 近似 8 位计数器,支持衰减因子。

7.3 内存压缩
listpack、hash-ziplist、set-intset、HyperLogLog-dense/sparse 编码,根据元素特征自适应切换。

(八)事务与脚本:从 MULTI/EXEC 到 Lua 再到 Functions
8.1 MULTI/EXEC 与 WATCH
乐观锁机制,WATCH 监控 key 版本号,EXEC 时若版本变化则回滚。

8.2 Lua
5.1 解释器嵌入,脚本以原子方式执行。EVALSHA 缓存字节码,减少带宽。

8.3 Functions
Redis 7 推出 “Functions”:脚本持久化到 AOF/RDB,重启可恢复;支持集群模式自动广播。解决 Lua 脚本在集群扩容后丢失的问题。

(九)发布订阅与 Stream:消息总线的两种范式
9.1 Pub/Sub
无持久化、无 ACK、无回溯,纯推模式。适用于高吞吐低可靠场景,例如弹幕、实时股价推送。

9.2 Stream
Kafka-like 日志抽象:消息 ID(毫秒时间戳+序号)、消费组、pending list、ack、claim。支持按 ID 重放,实现 CQRS 与事件溯源。

(十)模块系统:从 RedisModule 到 RedisML
模块 API 允许在子线程注册新命令、新数据结构、新事件。RedisTimeSeries、RedisSearch、RedisJSON、RedisBloom、RedisAI 等模块让 Redis 变身时序数据库、搜索引擎、向量数据库。

(十一)云原生与可观测性
11.1 Kubernetes Operator
通过 CRD 定义 RedisCluster、RedisFailover,利用 sidecar 实现自动故障转移、滚动升级、密码轮换。

11.2 监控指标
• 内存:used_memory、mem_fragmentation_ratio
• 延迟:latency_percentiles_usec、slowlog
• 复制:master_repl_offset、master_link_down_time_seconds
• 集群:cluster_state、cluster_slots_fail

11.3 追踪
Redis 7 支持 “keyspace hits/misses” 细粒度指标,结合 eBPF 可定位 hotkey。

(十二)尾声:下一跳
Redis 8 Roadmap:
• Threaded Lua:脚本跑在独立线程池,避免长脚本阻塞主循环。
• Diskless Cluster:Gossip 消息也走 RDMA,实现全内存复制。
• SQL-like 查询:基于 RediSQL 模块,支持类关系型语法。
• Tiered Storage:SSD 作为二级缓存,LRU 算法跨 DRAM/SSD 两层。

Redis 的演进史,是一部在 “性能、功能、一致性” 三角张力中反复权衡的历史。每一次看似激进的重构,背后都有业务场景的真实痛苦。理解这些权衡,远比背参数、背命令更有生命力。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/89792.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/89792.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

得了甲亢军队文职体检能过吗

根据军队文职体检现行标准&#xff0c;甲亢患者能否通过体检需分情况判定&#xff0c;核心取决于病情控制状态、治疗结果及稳定时长。结合《军队选拔军官和文职人员体检通用标准》及补充规定&#xff0c;具体分析如下&#xff1a;⚕️ 一、可直接通过体检的情况临床治愈满1年且…

【编程语言】C、C++、C#深度对比:三种语言的演进历程与应用场景

一、语言概述与历史背景 &#xff08;一&#xff09;C语言&#xff1a;系统编程的基石诞生背景 1972年由Dennis Ritchie在贝尔实验室开发为了重写UNIX操作系统而创造从B语言演化而来&#xff0c;增加了数据类型设计目标&#xff1a;简洁、高效、可移植设计哲学 “相信程序员”&…

《计算机网络》实验报告五 DNS协议分析与测量

目 录 1、实验目的 2、实验环境 3、实验内容 3.1 查看和配置本机的DNS系统 3.2 DNS信息测量 3.3 DNS协议分析 4、实验结果与分析 4.1 查看和配置本机的DNS系统 4.2 DNS信息测量 4.3 DNS协议分析 5、实验小结 5.1 问题与解决办法&#xff1a; 5.2 心得体会&#x…

Python工厂方法模式详解:从理论到实战

一、工厂方法模式核心概念 工厂方法模式&#xff08;Factory Method Pattern&#xff09;是一种创建型设计模式&#xff0c;属于经典23种设计模式之一。其核心思想是&#xff1a;定义一个创建对象的接口&#xff0c;但将具体对象的实例化过程延迟到子类中实现。这种模式通过引入…

python爬虫获取PDF

【前提&#xff1a;菜鸟学习的记录过程&#xff0c;如果有不足之处&#xff0c;还请各位大佬大神们指教&#xff08;感谢&#xff09;】 1.方法一&#xff1a;网站找到目标数据【单篇PDF】 https://bidding.sinopec.com/tpfront/xxgg/004005/ 按F12&#xff0c;----检查------…

IFN影视官网入口 - 4K影视在线看网站|网页|打不开|下载

IFN影视是一个专注于影视内容的网站&#xff0c;提供电影、电视剧、综艺等各类影视资源的在线观看服务。该网站以用户需求为导向&#xff0c;致力于为用户提供高清、流畅的观影体验&#xff0c;并不断更新内容以满足不同用户的观看习惯和偏好。IFN影视的特色在于其内容丰富、分…

《计算机网络》实验报告四 TCP协议分析

目 录 1、实验目的 2、实验环境 3、实验内容 3.1 利用wget下载新疆大学主页 3.2 使用wireshark分析TCP报文结构 3.3 使用wireshark分析建立连接的三次握手 3.4 使用wireshark分析释放连接的四次挥手 4、实验结果与分析 4.1 利用wget下载新疆大学主页 4.2 使用wiresh…

知识 IP 的突围:从 “靠感觉” 到 “系统 + AI” 的变现跃迁

越来越多的知识付费从业者陷入 “努力无成果” 的困局&#xff1a;做了内容、上了课程&#xff0c;却没人看、没人买。核心问题不在于能力不足&#xff0c;而在于仍在用 “靠感觉” 的原始方式打造 IP。在流量内卷、节奏加快的当下&#xff0c;“内容情怀” 已撑不起一门生意&a…

4.Java创建对象有几种方式?

1.使用 new 关键字&#xff08;最常用&#xff09;通过调用类的构造函数直接实例化对象Person person new Person(); // 调用无参构造 Person person new Person("Alice", 25); // 调用有参构造2.反射机制&#xff08;动态创建&#xff09;利用Java反射 API 在运行…

【好题】洛谷 P1600 [NOIP 2016 提高组] 天天爱跑步(倍增LCA+桶)

前言没做出来&#xff0c;看了很多篇题解后AC了&#xff0c;感觉大部分题解讲得不清楚。题目思路结果有两种求法模拟跑步过程&#xff0c;统计每个节点能观察到的人数考虑每条路径会对哪些节点作出贡献&#xff08;当前路径的玩家能被观察到&#xff09;尝试第一种求法必须遍历…

valkey之网络管理架构深度解析

一、连接类型实现体系 valkey通过ConnectionType结构体构建了灵活的网络连接抽象&#xff0c;支持多种连接类型的统一管理。每种连接类型都通过填充该结构体的函数指针来实现特定功能&#xff0c;形成了面向接口的设计模式。1.1 socket连接 Socket连接提供了最基础的TCP/IP通信…

【解码文本世界的“隐形分界线”:Windows与Linux回车换行之谜】

在计算机的文本世界里&#xff0c;回车&#xff08;Carriage Return&#xff0c;CR&#xff09;和换行&#xff08;Line Feed&#xff0c;LF&#xff09;是两个看似简单却意义非凡的字符。它们如同文本中的“隐形分界线”&#xff0c;默默地划分着段落与行&#xff0c;影响着文…

【Project】ELK 7.17.16 日志分析系统部署

ELK 日志分析系统集群部署 本文档基于 Rocky Linux 9.4 系统&#xff0c;部署 ELK 7.17.16&#xff08;长期支持版&#xff09;集群 案例准备 1. 节点规划IP主机名部署组件角色说明192.168.100.150kafka01Elasticsearch、Kibana主节点&#xff08;master&#xff09; 可视化192…

分布式定时任务系列13:死循环是任务触发的银弹?

传送门 分布式定时任务系列1&#xff1a;XXL-job安装 分布式定时任务系列2&#xff1a;XXL-job使用 分布式定时任务系列3&#xff1a;任务执行引擎设计 分布式定时任务系列4&#xff1a;任务执行引擎设计续 分布式定时任务系列5&#xff1a;XXL-job中blockingQueue的应用 …

Flutter基础(前端教程①③-单例)

现实类比&#xff1a;公司打印机假设你们公司有一台共享打印机&#xff1a;非单例&#xff08;重复创建&#xff09;&#xff1a;每个员工都自己买一台打印机放在工位上结果&#xff1a;浪费钱&#xff0c;占空间&#xff0c;难维护单例&#xff08;唯一实例&#xff09;&#…

力扣刷题 -- 965.单值二叉树

题目示例&#xff1a; 思路分析代码实现 bool isUnivalTree(struct TreeNode* root) {if(rootNULL){return true;}if(root->left && root->val ! root->left->val){return false;}if(root->right && root->val ! root->right->val){re…

uni-api交互反馈组件(showToast)的用法

欢迎来到我的UniApp技术专栏&#xff01;&#x1f389; 在这里&#xff0c;我将与大家分享关于UniApp开发的实用技巧、最佳实践和项目经验。 专栏特色&#xff1a; &#x1f4f1; 跨平台开发一站式解决方案 &#x1f680; 从入门到精通的完整学习路径 &#x1f4a1; 实战项目经…

借助它,在Web3投资赛道抢占先机

随着互联网技术的飞速发展&#xff0c;Web3的概念逐渐成为科技圈和投资界的热门话题。Web3代表着下一代互联网的发展方向&#xff0c;它强调去中心化、用户主权和数据隐私保护。在这一新兴领域&#xff0c;如何借助Web3技术抢占投资先机&#xff0c;成为许多投资者关注的焦点。…

验证大语言模型不会算数但可以编写算数的程序

摘要&#xff1a;本文通过几个实例测试了大语言模型在数学计算、排序、统计等方面的能力。结果显示&#xff0c;对于简单字符统计、排序等任务&#xff0c;大模型能正确生成实现代码&#xff0c;但当数据区分度降低时容易出错。在计算学生分数排名任务中&#xff0c;大模型生成…

概率论与数理统计(八)

参数估计 通过取样本&#xff0c;并用样本构造函数&#xff0c;达成估计分布函数参数的目的 矩估计法 本质&#xff1a;用样本的各阶矩代替总体的各阶矩&#xff0c;即取&#xff1a; E(X)X‾1n∑iXiE(X2)1n∑iXi2E(X)\overline{X}\dfrac{1}{n}\sum_i X_i\\ E(X^2)\dfrac{1}…