最新大厂二面面试题,看似简单,怎样才能拿高分呢?

101 阅读4分钟

你好,我是风一样的树懒,一个工作十多年的后端专家,曾就职京东、阿里等多家互联网头部企业。公众号“吴计可师”,已经更新了近百篇高质量的面试相关文章,喜欢的朋友欢迎关注点赞

上一文《大厂二面面试题居然跟简单的String相关,你能答上来么?》 是讲了使用应用上的区别,这样的回答只能说是及格分了,那么怎样才能高分呢?

下面我们来看看Redis 的 String 类型底层设计。

Redis 的 String 类型底层设计基于 简单动态字符串(Simple Dynamic String, SDS),这是一种专门为高效操作和内存管理优化的数据结构。相较于传统 C 字符串,SDS 在性能、安全性和功能性上有显著提升。以下是其核心实现原理和设计细节:


一、SDS 结构定义

Redis 3.2 之后的版本根据字符串长度优化了 SDS 类型,共有 5 种结构体,以最小化内存占用:

// Redis 5.0+ 的 SDS 结构示例(sdshdr8)
struct __attribute__ ((__packed__)) sdshdr8 {
    uint8_t len;        // 已用长度(最大 2^8-1)
    uint8_t alloc;      // 总分配容量(排除头和空终止符)
    unsigned char flags;// 类型标识(低3位表示类型,高5位未用)
    char buf[];         // 实际数据存储(兼容C字符串)
};

SDS 类型分类

类型长度范围适用场景
sdshdr50~31 字节极短字符串(已弃用)
sdshdr80~255 字节短字符串(如缓存键)
sdshdr160~65535 字节中等长度字符串
sdshdr320~4294967295 字节长字符串(如JSON数据)
sdshdr64超大字符串特殊场景(较少使用)

二、SDS 的核心优化设计

1. O(1) 时间复杂度获取长度

  • 传统 C 字符串:需遍历直到 \0,时间复杂度 O(n)。
  • SDS:直接读取 len 字段,时间复杂度 O(1)。

2. 杜绝缓冲区溢出

  • C 字符串问题strcat 可能覆盖相邻内存。
  • SDS 安全机制:修改前检查 alloc,不足时自动扩容。

3. 二进制安全

  • C 字符串限制:依赖 \0 结尾,无法存储含 \0 的数据(如图片、ProtoBuf)。
  • SDS 突破:通过 len 记录真实长度,可存储任意二进制数据。

4. 内存预分配策略

  • 扩容规则

    • 新长度 < 1MB:分配 2 * len 的空间。
    • 新长度 ≥ 1MB:额外分配 1MB。
    • 示例:从 10KB 扩容到 12KB,实际分配 24KB(alloc = 24Kfree = 12K)。
  • 惰性空间释放

    • 缩短字符串时不立即回收内存,更新 len 并保留 free
    • 显式释放 API sdsRemoveFreeSpace 可供调用。

三、编码优化策略

Redis 根据 String 的值类型自动选择最优编码,最大化性能和内存效率:

1. int 编码

  • 触发条件:字符串表示 64 位有符号整数
  • 内存布局:直接存储在指针位置(无需 SDS 结构)。
    void *ptr = (void*)(long)12345; // 直接存储整数
    
  • 优势:节省内存(无需结构体)且支持原子操作(如 INCR)。

2. embstr 编码

  • 触发条件:长度 ≤ 44 字节的字符串。
  • 内存布局:SDS 结构体与数据连续存储在单块内存。
    [ sdshdr8 | data ... | \0 ]
    
  • 优势
    • 内存局部性好(减少 CPU 缓存未命中)。
    • 减少内存分配次数(一次分配完成)。

3. raw 编码

  • 触发条件:长度 > 44 字节的字符串。
  • 内存布局:SDS 结构体与数据分两次分配。
    [ sdshdrX ] --> [ data ... | \0 ]
    
  • 优势:灵活处理大字符串,避免连续内存压力。

四、操作原子性与高性能

1. 原子性操作

  • 单命令原子性:如 APPENDINCRGETSET
  • 线程安全:Redis 单线程模型确保无竞态条件。

2. 零拷贝优化

  • 批量操作:如 SETRANGE 直接修改内存,无需创建新字符串。
  • 网络传输:通过 writev 系统调用合并发送 SDS 数据。

五、实战场景与编码选择

场景推荐编码理由
分布式计数器(库存)int节省内存,支持原子增减
短文本缓存(Token)embstr内存连续,快速访问
大JSON数据存储raw避免内存碎片,灵活扩容
二进制数据(图片)raw二进制安全,支持任意数据格式

六、SDS 的演进与性能对比

  • Redis 3.2 前:单一 SDS 结构,存在内存浪费。
  • Redis 3.2+:多类型 SDS,内存节省 30%~50%
    // sdshdr8(1字节 len + 1字节 alloc + 1字节 flags)
    // 存储 "hello"(5字节)总占用:3头字节 + 6字节(含\0)= 9字节
    

最后

现在基于底层Redis 的 String 类型通过 SDS 数据结构 和 智能编码策略,来回答Java的String与Redis的String的区别

相信基于这样的对比,肯定会收到面试官更多的肯定吧?

今天文章就分享到这儿,喜欢的朋友可以关注我的公众号,回复“进群”,可进免费技术交流群。博主不定时回复大家的问题。 公众号:吴计可师

qrcode_for_gh_79f35896a87f_258.jpg