当前位置: 首页 > news >正文

保存一条数据到 Redis 的全过程:从客户端到内存存储,深入底层细节(附 Spring Boot 实战)

视频看了几百小时还迷糊?关注我,几分钟让你秒懂!

你是否曾好奇:当你在 Java 代码中调用redisTemplate.opsForValue().set("user:1001", "张三")时,Redis 内部到底发生了什么?
一条数据是如何从你的 Spring Boot 应用,穿越网络,最终安全地存入 Redis 内存中的?

本文将带你逐层拆解这个过程,涵盖客户端、网络协议、服务端处理、内存结构、持久化等关键环节,并结合反例与注意事项,让你真正理解 Redis 的写入机制。


一、整体流程概览

graph LR A[Spring Boot 应用] -->|1. 构造命令| B(Lettuce/Jedis 客户端) B -->|2. RESP 协议编码| C[网络传输 TCP] C -->|3. Redis 服务端接收| D[事件循环 Event Loop] D -->|4. 命令解析| E[执行 SET 命令] E -->|5. 内存分配| F[SDS + Redis Object] F -->|6. 可选持久化| G[RDB/AOF]

下面,我们一步步深入。


二、Step 1:客户端构造命令(Spring Boot 层)

✅ 正确写法:

@Autowired private StringRedisTemplate redisTemplate; public void saveUser(Long id, String name) { // key = "user:1001", value = "张三" redisTemplate.opsForValue().set("user:" + id, name, 30, TimeUnit.MINUTES); }

🔍 背后发生了什么?

  • StringRedisTemplate使用Lettuce(默认)或 Jedis 客户端
  • 将 Java 对象转换为 Redis 命令:SET user:1001 张三 EX 1800
  • 自动序列化:String 类型无需额外序列化器

❌ 反例:存储大对象不设 TTL

// 错误:未设置过期时间,导致内存泄漏! redisTemplate.opsForValue().set("user:full:info:1001", hugeJsonString);

⚠️ 后果:key 永久存在,内存持续增长,最终 OOM。


三、Step 2:RESP 协议编码(客户端 → 网络)

Redis 使用RESP(Redis Serialization Protocol)作为通信协议,简单高效。

示例:SET user:1001 张三 EX 1800的 RESP 编码

*5 $3 SET $10 user:1001 $6 张三 $2 EX $4 1800
  • *5:表示有 5 个参数
  • $3:下一个字符串长度为 3(即 "SET")
  • 所有数据以\r\n结尾

优势:文本协议,易解析;二进制安全(支持任意字节)


四、Step 3:网络传输(TCP 连接)

  • 客户端通过TCP 连接将 RESP 数据包发送到 Redis 服务端(默认端口 6379)
  • Redis 使用I/O 多路复用(epoll/kqueue)监听连接,非阻塞接收数据
  • 数据进入 Redis 的输入缓冲区(querybuf)

📌 注意:若网络延迟高或带宽不足,会影响写入性能。建议 Redis 与应用部署在同一内网。


五、Step 4:服务端处理(Event Loop + 命令分发)

Redis 是单线程事件驱动模型

  1. 事件循环检测到 socket 可读
  2. 读取数据到client->querybuf
  3. 解析命令:按 RESP 格式拆分为argv[]数组
    argv[0] = "SET" argv[1] = "user:1001" argv[2] = "张三" argv[3] = "EX" argv[4] = "1800"
  4. 查找命令表,找到setCommand函数指针
  5. 调用setCommand(client)执行

关键点:整个过程在主线程完成,无锁,无上下文切换。


六、Step 5:内存存储(核心!)

这是最核心的一步:如何把 key-value 存入内存?

6.1 创建 Redis 对象(redisObject)

Redis 为每个 value 包装一个redisObject,包含类型、编码、引用计数等:

typedef struct redisObject { unsigned type:4; // OBJ_STRING unsigned encoding:4; // OBJ_ENCODING_EMBSTR 或 RAW int refcount; // 引用计数 void *ptr; // 指向实际数据 } robj;

6.2 选择底层编码(Encoding)

根据 value 大小,自动选择最优结构:

value 特征底层编码说明
是整数(如 "123")int直接用 long 存储
字符串 ≤ 44 字节embstr一次性分配 redisObject + SDS
字符串 > 44 字节rawredisObject 和 SDS 分开分配

💡为什么是 44 字节?
RedisObject (16B) + SDS header (8B) + 字符串 + '\0' ≤ 64B(内存分配器最小单元),避免内存碎片。

6.3 使用 SDS 存储字符串

Redis 不用 C 原生字符串,而是SDS(Simple Dynamic String)

struct sdshdr8 { uint8_t len; // 已用长度 uint8_t alloc; // 总分配长度 unsigned char flags; // 类型标识 char buf[]; // 实际字符数组 };

优势

  • O(1) 获取长度
  • 杜绝缓冲区溢出
  • 二进制安全(可存图片、序列化对象)

6.4 插入全局哈希表

Redis 将 key-value 存入全局字典(dict),本质是哈希表 + 链地址法

dictEntry *entry = dictAddRaw(db->dict, key, NULL); entry->v.val = val; // val 是 redisObject 指针

📌注意:Redis 会在哈希冲突严重时自动 rehash(渐进式,不阻塞)。


七、Step 6:持久化(可选,异步)

如果开启了持久化,Redis 会异步记录写操作

方式 1:RDB(快照)

  • 不立即触发,由配置的save规则决定(如 900 秒 1 次修改)
  • 主进程 fork 子进程,子进程将内存数据写入.rdb文件

方式 2:AOF(追加日志)

  • 将命令追加到aof_buf缓冲区
  • 根据appendfsync策略(always/everysec/no)刷盘
  • 不影响主线程性能(刷盘由后台线程或 OS 负责)

重要:持久化不会阻塞 SET 命令返回!客户端在步骤 5 完成就收到 OK。


八、完整 Spring Boot 实战示例

@Service public class UserService { @Autowired private StringRedisTemplate redisTemplate; // 保存用户信息(带过期时间) public void saveUser(User user) { String key = "user:" + user.getId(); String value = JSON.toJSONString(user); // 转为 JSON // 自动选择 embstr 或 raw 编码 redisTemplate.opsForValue().set(key, value, 30, TimeUnit.MINUTES); // 验证存储编码(开发环境可用) // redis-cli> OBJECT ENCODING user:1001 } }

如何验证底层编码?

# 连接 Redis redis-cli # 查看 key 的编码 127.0.0.1:6379> SET small "hello" OK 127.0.0.1:6379> OBJECT ENCODING small "embstr" 127.0.0.1:6379> SET large "这是一个超过44字节的字符串,用于测试raw编码" OK 127.0.0.1:6379> OBJECT ENCODING large "raw"

九、常见误区与注意事项

误区正确认知
“SET 命令会立刻写磁盘”❌ 持久化是异步的,SET 返回时只保证内存写入
“Redis 用 HashMap 存数据”❌ 用的是自研 dict(哈希表 + 渐进 rehash)
“字符串都用 embstr”❌ >44 字节会转为 raw,内存开销更大
“不设 TTL 没关系”❌ 会导致内存泄漏,务必设置合理过期时间

十、总结:一条数据的旅程

  1. 客户端:构造 SET 命令,编码为 RESP
  2. 网络:TCP 传输到 Redis 服务端
  3. 服务端:事件循环接收 → 解析命令 → 执行 setCommand
  4. 内存
    • 创建 redisObject
    • 根据大小选择 int/embstr/raw 编码
    • 用 SDS 存储字符串
    • 插入全局哈希表
  5. 持久化(异步):记录到 AOF 或等待 RDB 快照

💡核心思想:Redis 的快,源于内存操作 + 单线程无锁 + 精细内存管理


结语

下次当你调用redisTemplate.set()时,不妨想象一下:此刻,Redis 正在用 SDS、redisObject、哈希表为你精心安放这条数据,而这一切,都在微秒级完成!

理解这个过程,你不仅能写出更高效的代码,还能在面试中惊艳面试官:“我知道 Redis 为什么用 embstr……”

视频看了几百小时还迷糊?关注我,几分钟让你秒懂!

http://www.jsqmd.com/news/284691/

相关文章:

  • 2026年企业如何选择?阿里云邮箱优质服务商推荐与全方位对比指南
  • 【收藏级】AI大模型学习路线全解析:抓准缺人风口,实现职业跃迁
  • Redis 过期与淘汰策略深度解析:从原理到 Spring Boot 实战,彻底搞懂内存管理机制!
  • PPIO × 商汤 LazyLLM: 一站式构建 Multi-Agent |实操指南
  • 2026公务车定制厂家推荐:实力品牌与专业定制方案解析
  • 分析诚信的豪雅新乐学配镜机构,北京靠谱的有哪些?
  • 收藏!30+程序员破局35岁危机:从Java后端到大厂大模型岗的实战指南
  • 域名系统支撑无人机网络身份认证及IPv6创新应用研究
  • 工业设计公司服务找哪家,京津冀璞新科技优势盘点
  • 录屏老翻车?那是你没遇到sunwoo录屏大师!
  • 收藏级指南|大模型SFT与RL核心训练调优技巧,小白也能看懂
  • Redis 为什么这么快?深入解析高性能背后的秘密(附 Spring Boot 实战)
  • 讲讲上海新房除甲醛品牌供应商,生态美家哪家性价比高?
  • 婴幼儿喘息怎么办?布咳乐F6高性能罐式雾化器填补市场关键空白
  • 基于供应链数据泄露的硬件钱包钓鱼攻击分析与防御机制研究
  • 文山州马关丘北广南富宁英语雅思培训辅导机构推荐,2026权威出国雅思课程中心学校口碑排行榜
  • 广州公关公司 TOP 级选择!汇志传媒二十年深耕,筑牢品牌声誉防线
  • 必收藏!RAG(检索增强生成)全解析:从原理到流程,小白也能看懂的大模型优化技术
  • 收藏必学!一文看懂大模型三大架构:从Encoder-only到Decoder-only的完全指南
  • 2026年华东阿里云企业邮箱代理服务全解析,推荐高性价比合作伙伴!
  • 2026年市场评价好的AGV货架批发厂家找哪家,仓储货架/精益管料架/货架定制/不锈钢货架,AGV货架生产厂家选哪家
  • 【珍藏指南】AI Agent核心技术解析:从第一性原理到多Agent协作的未来
  • 2026年杨家坪、仁安里特色茶馆排行榜,私密环境茶馆哪个口碑好
  • 收藏!AI抢了程序员一半饭碗?真相的底层逻辑小白必看
  • 买立式/卧式加工中心认准垂直平台,机床商务网适配性拉满
  • 板式换热器厂家哪家好,金多邦凭资质和案例获认可
  • 2026年高中数学竞赛培训机构有哪些 口碑好的机构优选推荐
  • 2026年Databricks交付部署厂商排名
  • 2026年1月亚马逊最新动态:政策密集调整,卖家破局指南
  • 液位计生产厂哪家售后好,杭州美控值得关注的排名揭秘