用过redis哪些数据类型?Redis String 类型的底层实现是什么?

用过Redis哪些数据类型?Redis String类型的底层实现是什么?

作为一名全栈工程师,Redis 是我日常开发中不可或缺的缓存和数据结构服务器。无论你是后端开发者还是前端工程师,理解 Redis 的数据类型及其底层实现,对于优化系统性能、设计高效的数据存储方案都至关重要。## Redis 支持的数据类型概览Redis 以其丰富的数据结构而闻名,以下是常用的数据类型:- String :字符串,最基础的数据类型,可以存储字符串、整数或浮点数。- List :列表,基于双向链表实现,支持从两端插入和删除。- Set :无序集合,基于哈希表实现,支持集合运算(交集、并集、差集)。- Sorted Set :有序集合,通过跳跃表实现,每个成员关联一个分数,可按分数排序。- Hash :哈希表,存储字段-值对的映射,适合存储对象。- Bitmaps :位图,基于String类型实现,用于位操作统计。- HyperLogLogs :基数统计,用极小的内存估算唯一元素数量。- Streams :流,5.0版本引入,支持消息队列模式。在实际项目中,我最常用的是String、Hash和List。比如,用String缓存用户会话,用Hash存储用户信息,用List实现消息队列。## Redis String 类型详解String 是 Redis 中最简单的数据类型,但它的底层实现却非常巧妙。Redis 的 String 并不是直接使用 C 语言的字符串,而是封装了一个叫做 SDS(Simple Dynamic String)的结构体。### SDS 结构体SDS 在 Redis 源码中定义(sds.h),核心结构如下:cstruct sdshdr { int len; // 已使用字节数 int free; // 未使用字节数 char buf[]; // 字节数组,用于存储字符串};- len:记录当前字符串的实际长度,避免每次获取长度时遍历整个字符串(O(1) 复杂度)。- free:记录 buf 数组中未使用的字节数,用于预分配空间,减少内存重分配次数。- buf[]:以 \0 结尾的字符数组,兼容 C 字符串函数。### SDS 的优势1. 常数时间复杂度获取字符串长度 :C 字符串需要 O(n) 遍历,SDS 直接读取 len 字段。2. 杜绝缓冲区溢出 :当修改字符串时,SDS 会先检查空间是否足够,不足则自动扩容。3. 减少内存重分配 :通过空间预分配和惰性空间释放策略,优化内存管理。4. 二进制安全 :C 字符串以 \0 结尾,不能存储二进制数据;SDS 的 len 字段明确记录长度,可以存储任意二进制数据(如图片、序列化对象)。### 空间预分配策略当 SDS 的 API 需要对字符串进行修改时,Redis 会执行以下操作:- 如果修改后长度小于 1MB,程序分配和 len 同样大小的未使用空间(即 free = len)。- 如果修改后长度大于等于 1MB,程序分配 1MB 的未使用空间(即 free = 1MB)。例如,一个长度为 13 的字符串 "Hello World!!!",修改后长度为 15,那么 SDS 会分配 15 + 15 + 1 = 31 字节(len=15, free=15)。这避免了频繁的内存重分配,提升了性能。## 实战代码示例为了更直观地理解 Redis String 的使用和底层行为,我用 Python 和 Redis 客户端进行演示。请确保已安装 redis 库(pip install redis)。### 示例 1:String 的基本操作pythonimport redis# 连接 Redis 服务器(默认端口 6379)r = redis.Redis(host='localhost', port=6379, db=0)# 设置键值对(String 类型)r.set('user:1:name', 'Alice') # 存储字符串r.set('user:1:age', 25) # 存储整数r.set('user:1:salary', '5000.50') # 存储浮点数(作为字符串)# 获取值name = r.get('user:1:name')age = r.get('user:1:age')salary = r.get('user:1:salary')print(f"Name: {name.decode('utf-8')}") # 输出: Name: Aliceprint(f"Age: {age.decode('utf-8')}") # 输出: Age: 25print(f"Salary: {salary.decode('utf-8')}") # 输出: Salary: 5000.50# 数值操作(INCR 原子递增)r.incr('user:1:age', 1) # 年龄增加 1new_age = r.get('user:1:age')print(f"New Age: {new_age.decode('utf-8')}") # 输出: New Age: 26# 查看字符串长度(底层 SDS 的 len 字段)str_len = r.strlen('user:1:name')print(f"Length of name: {str_len}") # 输出: Length of name: 5说明 :- set 命令存储字符串时,Redis 内部使用 SDS 存储。- incr 命令利用 SDS 的数值支持,自动转换为整数并递增。- strlen 直接返回 SDS 的 len 字段,复杂度 O(1)。### 示例 2:SDS 空间预分配效果验证pythonimport redisr = redis.Redis(host='localhost', port=6379, db=0)# 创建一个较长的字符串,模拟 SDS 扩容original = "A" * 100 # 100 个字符r.set('test:long', original)# 获取原始字符串长度print(f"Original length: {r.strlen('test:long')}") # 输出 100# 追加大量数据,触发空间预分配r.append('test:long', "B" * 50) # 追加 50 个字符new_length = r.strlen('test:long')print(f"After append length: {new_length}") # 输出 150# 查看 Redis 内部内存占用(通过 DEBUG OBJECT 命令)debug_info = r.execute_command('DEBUG', 'OBJECT', 'test:long')print(f"Debug info: {debug_info}")# 输出类似: Value at:0x7f1234567890 refcount:1 encoding:embstr serializedlength:151 lru:12345 lru_seconds_idle:10# 注意:encoding 显示为 embstr(短字符串)或 raw(长字符串),这里长度 150 可能仍为 raw说明 :- append 命令会触发 SDS 扩容,根据预分配策略分配额外空间。- DEBUG OBJECT 命令显示内部编码(encoding),短字符串(<44 字节)用 embstr,长字符串用 raw(基于 SDS)。- 实际项目中,预分配策略减少了内存分配次数,提升了大量字符串操作的性能。## 底层编码细节Redis String 的底层实现根据字符串长度和内容采用不同的编码方式:- int :如果字符串可以表示为整数(如 "123"),Redis 直接使用整数存储,节省内存。- embstr :如果字符串长度小于等于 44 字节(Redis 3.2+),使用嵌入式 SDS,将 SDS 结构体和字符串数据分配在同一连续内存块,减少内存碎片。- raw :如果字符串长度超过 44 字节,使用标准的 SDS 结构,数据单独分配。例如,设置 set key "Hello",内部编码为 embstr;设置 set key "A" * 100,内部编码切换为 raw。## 总结Redis 的 String 类型看似简单,但其底层 SDS 实现却体现了经典的空间换时间思想。通过预分配和惰性释放策略,SDS 在频繁的字符串操作中保持了高性能,同时提供了二进制安全和 O(1) 长度获取的能力。作为全栈工程师,理解这些细节有助于我们:- 合理选择数据类型,避免滥用 String 导致内存浪费。- 优化缓存策略,比如对大字符串使用压缩或分段存储。- 排查性能问题,知道何时使用 incrappend 等原子操作。在实际开发中,String 类型适用于小数据量的缓存、计数器、分布式锁等场景。对于复杂对象,建议使用 Hash 或序列化后存储。掌握底层原理,才能写出更高效的 Redis 应用。

相关推荐
这就是佬们吗20 小时前
回溯算法三板斧---掌握「回溯三问」思考模板快速入门回溯
java·数据库·算法
青皮桔20 小时前
Redis AOF 文件损坏修复记录
运维·数据库·redis
happyh h h h p p p p20 小时前
LVS 项目完整知识点总结
linux·服务器·数据库
仙宇觉尘21 小时前
记一次 .NET 某中医药附属医院门诊系统 崩溃分析
数据库·oracle·.net
小码农 - 初21 小时前
MySQL的表操作
数据库·mysql
灵机一物21 小时前
2026智能清洁设备推荐榜:无人值守与效率提升解析
数据库
醉城夜风~21 小时前
HTML表单域学习博客:表单标签、输入框、按钮详解
android·java·缓存
xuankuxiaoyao21 小时前
WQ 算子尝试1
数据库·mysql
筑梦之路21 小时前
harbor使用国产数据库PolarDB PG版本部署——筑梦之路
数据库·信创适配·国产化改造
大鹏说大话1 天前
移动应用打包常见报错汇总
数据库