大家好,我是CodeStats。
一个在底层技术上“考古”了四年的硬核爱好者,也是WWAIC(全周项目 AI 编程)范式的提出者和实践者。我曾手写过一个完整的 Java Web 框架(从 IoC 容器到嵌入式 Tomcat,代码全开源),也喜欢用通俗的语言拆解 CPU、JVM、操作系统的运行本质。本文为《Redis 系统知识完全拆解》系列的「上篇」,涵盖 Redis 的架构演进、通信协议、数据类型命令及 Pipeline/Lua 脚本底层原理。中篇将深入持久化与内存管理,下篇将聚焦实战场景与缓存三大经典问题。
📑 上篇目录
提问一:Redis 的单机、主从、哨兵、集群模式原理区别详解
提问二:Redis 服务器采用什么通信协议,是如何利用单线程模型的
提问三:Redis 常用的五种数据类型和操作命令有哪些
提问四:Redis 使用 Pipeline 和 Lua 脚本实现原理,哪个是原子性,Lua 脚本的原子性为什么不支持回滚
一、Redis 的单机、主从、哨兵、集群模式原理区别详解
Redis 的高可用能力是逐步迭代完善的,整体遵循单机 → 主从复制 → 哨兵 Sentinel → Redis Cluster的演进路径。
1. 单机模式(Standalone)
最基础的部署方式,一个 Redis 实例独立运行。痛点在于:单点故障(宕机即停服)、内存上限(受限于单机物理内存)、读写性能瓶颈(无法利用多核)。
2. 主从复制(Master-Slave)
一主多从数据副本,主节点写,从节点读,解决读压力与数据备份问题。
全量复制:从节点首次连接,主节点生成 RDB 快照发送。
增量复制:断线重连后,基于复制积压缓冲区(repl_backlog_buffer)同步断连期间的增量数据,依赖偏移量(offset)判断一致性。
3. 哨兵模式(Sentinel)
在主从复制上增加了自动故障切换能力。哨兵集群通过主观下线与客观下线判断节点状态,利用Raft 算法选举 Leader 执行故障转移。
4. 分片集群(Redis Cluster)
官方原生分布式方案,解决海量数据与高并发写入瓶颈。
哈希槽(Hash Slot):共
16384个槽位,Key 通过 CRC16 算法取模映射到不同主节点。Gossip 协议:节点间去中心化通信,交换集群状态。
自带高可用:每个主节点可挂载从节点。
总结:单机是起点,主从解决读压力,哨兵解决自动切换,Cluster 解决水平扩展。生产环境视数据量与一致性要求选型。
二、Redis 服务器采用什么通信协议,是如何利用单线程模型的
1. 通信协议:RESP(Redis Serialization Protocol)
RESP 是 Redis 客户端与服务端之间的通信协议,设计极为轻量:
直观的文本协议:实现简单,解析性能极好。
5 种最小单元类型:如简单字符串(
+OK\r\n)、错误(-ERR\r\n)、整数(:100\r\n)、批量字符串($5\r\nhello\r\n)、数组(*2\r\n$3\r\nget\r\n$3\r\nkey\r\n)。前缀长度传输:不需要扫描负载查找特殊字符,解析高效。
2. 单线程模型:Reactor + I/O 多路复用
Redis 的网络模型本质是单 Reactor 单线程模型。
I/O 多路复用:基于 Linux 的
epoll(或 BSD 的kqueue),一个线程能同时监听成千上万个 socket 连接。文件事件处理器:将连接、读写、命令执行等事件派发给不同处理器。
6.0 演进:引入多线程 I/O处理网络数据的读写,但核心命令的执行依旧是单线程。
为什么早年死守单线程执行命令?
避免多线程上下文切换与锁竞争。
对 CPU 缓存极致友好,零锁设计保证数据结构操作绝对线程安全。
三、Redis 常用的五种数据类型和操作命令有哪些
| 类型 | 底层数据结构 | 核心命令(示例) |
|---|---|---|
| String(字符串) | SDS(简单动态字符串) | SET key value、GET key、INCR key、SETNX key value |
| Hash(哈希) | 压缩列表 / 哈希表 | HSET key field value、HGET key field、HINCRBY key field 1 |
| List(列表) | 压缩列表 / 双向链表 | LPUSH key value、RPUSH key value、LRANGE key 0 -1、LPOP key |
| Set(集合) | 整数集合 / 哈希表 | SADD key member、SMEMBERS key、SINTER key1 key2(交集) |
| Sorted Set(有序集合) | 压缩列表 / 跳表 + 哈希表 | ZADD key score member、ZRANGE key 0 -1 WITHSCORES、ZINCRBY key 1 member |
通用命令:
DEL key、EXISTS key、EXPIRE key seconds、TTL key、KEYS pattern(生产慎用,可用SCAN替代)。
四、Redis 使用 Pipeline 和 Lua 脚本实现原理,哪个是原子性,Lua 脚本的原子性为什么不支持回滚
1. Pipeline(管道)—— 批量运输车
原理:将多个命令在客户端打包,一次性发送给 Redis,Redis 按序执行后一次性返回所有结果。
目的:减少网络往返时间(RTT),提升吞吐量。
原子性:❌非原子性。Redis 服务端仍然是逐个执行命令,执行过程中其他客户端的命令完全可以穿插进来。
2. Lua 脚本 —— 服务端原子计算器
原理:将业务逻辑封装成 Lua 脚本,整个脚本作为一个整体发送给 Redis 服务端执行。
原子性:✅绝对的原子性。脚本执行期间,Redis 主线程被完全占用,不会执行任何其他命令,直到脚本结束。
3. 为什么 Lua 脚本不支持回滚?
这是 Redis 设计哲学的核心体现:追求极致的性能和简洁。
脚本执行过程中,如果第 3 步报错,前 2 步的写入已经生效(比如
DECR扣了库存)。Redis 官方认为,错误的回滚机制极其复杂且损耗性能(需要记录 Undo Log),与 Redis 轻量级、高速缓存/存储的定位相悖。
最佳实践:开发者应在脚本开头做好完备的逻辑校验(
if判断),确保前置条件满足后再执行写操作,从业务层杜绝半途而废的情况。
总结:无脑攒命令用Pipeline;需要读-计算-写(如扣库存)且要求原子性,必须用Lua 脚本。
💎 上篇总结与预告
本篇我们拆解了 Redis 的四大架构演进、RESP 协议内幕、五大数据类型命令,以及 Pipeline 与 Lua 的底层博弈。下一篇(中篇),我们将深入最底层的持久化机制(RDB 与 AOF 的源码级对比)、过期策略的三种权衡,以及内存淘汰策略的八种选型。敬请期待!