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

Redis高频面试题——String对象

Redis 中的 String 是最常用的数据类型之一,它在内部有多种编码方式,用于提升性能和节省内存。下面我们详细介绍一下 👇


🎯 Redis String 对象的编码类型

Redis 中的 String 对象底层支持 三种编码方式(Encoding)3.2版本之前是39,3.2版本之后才是44

编码方式说明使用场景
int整数可以表示为 long long 的整数
embstr短字符串(长度 ≤ 44 字节)小字符串、高效分配
raw普通字符串(长度 > 44 字节)大字符串

🔍 逐个介绍

1. int 编码

  • 存储内容是一个整数,比如:SET age 25
  • Redis 会将该 String 对象编码为 int
  • 优点:节省内存,操作效率高(无需分配字符串结构)
✅ 使用条件:
  • 值能被解析为整数(long long 范围内)

2. embstr 编码(Embedded String)

  • 用于存储长度小于等于 44 字节的字符串
  • Redis 会将 redisObject 和实际字符串 SDS 分配在一起(一次 malloc),提高分配效率和缓存局部性
优点:
  • 创建快,释放快,内存连续
  • 常用于小字符串,如:SET name "Bob"

3. raw 编码

  • 超过 44 字节的长字符串使用 raw 编码
  • redisObject 和字符串内容是分开分配的(两次 malloc)
  • 灵活但性能略低于 embstr

🛠 如何查看实际编码?

你可以使用 OBJECT ENCODING key 命令查看 Redis 内部编码:

SET mykey 42
OBJECT ENCODING mykey   # 返回 intSET mykey "hello"
OBJECT ENCODING mykey   # 返回 embstrSET mykey "a very very long string over 44 bytes........"
OBJECT ENCODING mykey   # 返回 raw

💡 小结

编码方式特点内存结构触发条件
int高效存储整数非字符串字符串能转为整数
embstr快速创建、释放的小字符串连续内存字符串长度 ≤ 44 字节
raw普通字符串,大字符串分散内存字符串长度 > 44 字节

如果你对 SDS(Simple Dynamic String)机制也感兴趣,我可以继续讲讲它是如何扩容、预分配、避免内存碎片的 😉
当然可以!在 Redis 的 raw 编码中,字符串是通过一种特殊的结构 —— SDS(Simple Dynamic String) 来管理的。SDS 的底层结构是 sdshdr,它比 C 的 char* 更加安全、灵活。下面我们详细看看它的结构和特点👇


🔍 什么是 sdshdr

sds 是 Redis 自定义的一种动态字符串类型,它的底层是一个结构体叫 sdshdr。这个结构不仅存储了字符串的内容,还保存了字符串的长度和分配空间等元信息。


💡 为什么不用 char*

C 语言的 char* 有这些问题:

  • 需要用 strlen() 才能知道字符串长度,时间复杂度 O(n)
  • 容易出现 缓冲区溢出
  • 扩容和拼接效率低、内存管理混乱。

而 SDS 就解决了这些问题。


🧱 sdshdr 的常见结构

Redis 中的 SDS 实现其实是一个结构体族(根据字符串长度使用不同的结构),我们以常用的 sdshdr8 举例说明:

struct __attribute__ ((__packed__)) sdshdr8 {uint8_t len;      // 当前字符串长度(不含 \0)uint8_t alloc;    // 已分配的空间长度(不含 header 和 \0)unsigned char flags;  // 低 3 位表示是哪种类型的 sdshdrchar buf[];       // 字符串内容(以 \0 结尾)
};

字段含义解释:

字段含义
len当前字符串实际长度(不包含\0
alloc分配的缓冲区大小(也不含\0
flags记录类型,比如 sdshdr5/8/16/32/64
buf[]存储实际字符串,最后是\0结尾

🧠 优点分析

✅ 获取长度是 O(1)

size_t sdslen(const sds s);

不需要遍历字符串,效率远超 strlen()

✅ 自动扩容

如果你执行 sds cat 或追加,SDS 会自动判断是否需要扩容。

✅ 减少内存碎片

预分配机制:比如你追加 “abc” 后,可能会分配更多的内存空间,下次再追加就不用 realloc 了


📦 多种 sdshdr 类型

为了节省内存,Redis 使用了不同类型的 sdshdr

类型最大长度结构体大小
sdshdr531 字节最小,仅 flags
sdshdr8255 字节常用小字符串
sdshdr1665,535 字节中等字符串
sdshdr324GB长字符串
sdshdr64超大字符串(稀有)特殊场景

Redis 会根据字符串的实际长度自动选择最优的 sdshdr 类型。


🧪 一个例子(伪代码):

sds s = sdsnew("hello redis");
printf("长度: %d\n", sdslen(s));
printf("容量: %d\n", sdsavail(s));

输出示例:

长度: 11
容量: 33

说明 alloc 分配了 44 字节,但当前只用了 11。


🧵 小结

特性优点
O(1) 获取长度高效替代 strlen()
自动扩容更好支持拼接和增长
内存安全避免 C 的 buffer overflow
多结构支持节省内存,适应不同长度的字符串
与 C 兼容buf[] 末尾是 \0,可以与 C 字符串兼容

面试中关于 Redis 的 String 类型的提问非常高频,通常围绕数据结构、编码、性能、应用场景等方面。下面我给你整理了一些面试中高频问题 + 答案解析,帮你系统性准备 👇


🎯 高频面试题 + 答案(关于 Redis 的 String)


1️⃣ Redis 的 String 是什么?底层是怎么实现的?

答:
Redis 的 String 是最基本的数据类型,最大可以存储 512MB 的二进制安全数据。
底层使用的是 SDS(Simple Dynamic String),不是 C 的 char*,它具备以下优点:

  • 记录字符串长度(O(1) 获取)
  • 自动扩容、预分配空间
  • 安全:防止缓冲区溢出
  • 多种类型(sdshdr5、sdshdr8、16、32、64)节省内存

2️⃣ String 的常见应用场景有哪些?

答:

  • 缓存对象(JSON、序列化后的对象)
  • 计数器(用 INCR/DECR 实现)
  • 分布式锁(SET key value NX EX 10
  • 限流器(结合 Lua 脚本 + INCR)
  • 存储配置信息、Token、验证码等

3️⃣ Redis SETNX 和 SET NX 有什么区别?

答:

  • SETNX key value:只在 key 不存在时设置,只能设置值
  • SET key value NX:功能一样,但 SET 命令还支持额外参数,比如 EX 设定过期时间,更推荐使用。

👉 面试建议答:推荐使用 SET key value NX EX 10 原子性地实现分布式锁。


4️⃣ String 的编码方式有哪些?什么时候会用?

答:

Redis 会根据 value 的内容和大小自动选择编码方式:

编码类型说明
int8 字节整数,节省内存
embstr小于等于 44 字节的字符串,连续内存块,创建快
raw长字符串,SDS 实现,支持扩容

触发条件:

  • 字符串是整数 ➜ int
  • 字符串 ≤ 44 字节 ➜ embstr
  • 字符串 > 44 字节 ➜ raw

5️⃣ embstr 和 raw 区别?

特性embstrraw
内存分配一次性分配对象和数据分别分配
适用数据短字符串(≤ 44 字节)长字符串(> 44 字节)
性能创建、释放更快支持动态扩容

6️⃣ 你了解 SDS 的预分配机制吗?

答:
是的。SDS 在字符串增长时会自动扩容,避免频繁的内存分配。

扩容策略如下:

  • newlen < 1MB:多分配相同大小的空间
  • newlen >= 1MB:多分配 1MB

这样可以提升性能,减少 realloc 次数。


7️⃣ String 类型能存二进制数据吗?

答:
能,Redis 的 String 是二进制安全的,可以存储任意内容(图片、音频、序列化对象等),不限制字符编码。


8️⃣ 如何实现一个全局唯一 ID 生成器?

答:

利用 Redis 的 INCR 原子性即可:

INCR user:id

还可以结合业务前缀与时间戳生成订单号等,如:

SET order:20250425 1000
INCR order:20250425

9️⃣ String 类型最大能存多少数据?

答:
最大为 512MB,超出 Redis 会报错。


1️⃣0️⃣ 如何实现分布式锁?

答:
核心命令是:

SET lock:job123 "UUID" NX EX 10

加锁成功返回 OK,失败代表锁已被其他客户端占用。

释放锁时使用 Lua 脚本确保删除的是自己加的锁,避免误删:

if redis.call("get", KEYS[1]) == ARGV[1] thenreturn redis.call("del", KEYS[1])
elsereturn 0
end

11.浮点型在String是用什么表示?
分析:基础知识考查,只有三种编码模式INT只针对于整型,所以浮点型必然是字符串存储。
回答:要将一个浮点数放入字符串对象里面,需要先将这个浮点数转换成字符串值,然后再保存转换所得的字符串值,比如浮点数3.14,对应就变成了"3.14"这个字符串了。所以浮点数在字符串对象里面是用字符串值表示的。
12.Redis字符串底层是String对象,String对象有三种编码方式:INT型、EMBSTR型、RAW型。如果是存在一个整型,可以用long表示的整数就以INT编码存储;如果存字符串,当字符串长度小宇等于一个阈值,使用EMBSTR编码;字符串大于阈值,则用RAW编码。在我用的5.0.5版本中阈值是44。
13.SDS有什么用?
分析:Redis是用C语言写的,SDS可以说是对C字符串的封装,一般对比普通C字符串。可以从计算长度、扩容、缩容、二进制存储这几个场景来描述。
回答:
(1)SDS包含已使用容量字段,O(1)时间快速返回有字符串长度,相比之下,C原生字符串需要O(n)。
(2)有预留空间,在扩容时如果预留空间足够,就不用再重新分配内存,节约性能,缩容时也可以将减少的空间先保留下来,后续可以再使用。
(3)不再以’\0’作为字符串结束判断标注呢,二进制安全,可以很方便地存储一些二进制数据。
如果你需要我总结成思维导图或 PDF 方便复习,请点赞加关注私信我!

相关文章:

  • [ESP-IDF]:esp32-camera 使用指南 ESP32S3-OV2640 用例测试
  • 优化非线性复杂系统的参数
  • Linux基础IO(十一)之动态库(基础IO的最后一篇啦!)
  • CentOS系统防火墙服务介绍
  • 发放优惠券
  • 初窥Java内存模型(JMM)
  • 精益数据分析(24/126):聚焦第一关键指标,驱动创业成功
  • 边界凸台建模与实例
  • PGSql查看表结构以及注释信息
  • NAT穿透
  • 通过API接口在自己的独立站系统上架商品信息。(实战案例)
  • 【Java学习笔记】冒泡排序
  • NEGATIVE LABEL GUIDED OOD DETECTION WITH PRETRAINED VISION-LANGUAGE MODELS
  • WHAT - 前端开发书单推荐
  • 【vue】【element-plus】 el-date-picker使用cell-class-name进行标记,type=year不生效解决方法
  • DeepSeek回答过于笼统,提示词如何优化
  • RK3562/3588 系列之0—NPU基础概念
  • 高防IP+CDN组合:电商大促的“双保险”防护方案
  • 常见网络安全攻击类型深度剖析(二):SQL注入攻击——原理、漏洞利用演示与代码加固方法
  • linux系统问题杂谈
  • 广汽集团一季度净亏损7.3亿元,同比转亏,总销量下滑9%
  • 共话城市自然之美,“微观黄浦”自媒体网络大V沙龙首场活动举行
  • 东北财大“一把手”调整:方红星任校党委书记,汪旭晖任校长
  • 印巴在克什米尔实控线附近小规模交火,巴防长发出“全面战争”警告
  • 青海西宁市公安局原党委委员、副局长王小华被“双开”
  • 影子调查丨掉落的喷淋头:太原一7天酒店加盟店消防设施造假迷局