Redis的String 底层实现

本文将介绍有关 redis 的 string 底层实现的内容

结构源码

redis 的的 String 类型底层主要是依靠 redis 的SDS 结构,同时通过 int, embstr, raw 等不同编码方式进行存储。

首先我们来看 redis 中进行数据存储的 redisObject 源码

c 复制代码
struct redisObject {
    unsigned type:4;      // 数据类型(字符串、哈希等)
    unsigned encoding:4;  // 编码类型(int、embstr、raw等)
    int64_t ptr;          // 实际的数据指针,这里直接存储整数值
};

可以看到其中有三个字段,分别是

  • type,表明数据类型(字符串、哈希等)
  • encoding,表明编码类型,如 int、embstr、raw 等
  • ptr,实际的数据指针

针对于 redis 的 String 类型数据,redis 的 type 为字符串,encoding 将为 int、embstr、raw。而 ptr 又会根据 encoding 有不同的分配指向。

接着我们又来看看 String 依赖的底层结构 SDS,如下图(图片来自于小林 coding

可以看到 SDS 的数据结构主要有以下及部分组成

  • len,记录了字符串长度
  • alloc,分配给字符数组的空间长度
  • flags,用来表示不同类型的 SDS。redis 一共设计了 5 中类型,分别是 sdshdr 5、sdshdr 8、sdshdr 16、sdshdr 32 和 sdshdr 64。这几种不同类型主要差别在于对数据结构中的 len 和 alloc 变量的数据类型不同,比如 sdshdr 16 的 len 和 alloc 是uint16_t,而 sdshdr 64 的 len 和 alloc 是 uint 64_t,类型不同,变量能够表示的数据范围也不同
  • buf[],字节数组,用来保存实际数据

这里我们以 sdshdr 32 的源码定义为例,其中的 __attribute__ ((packed)) redis 使用的准们的编译优化来节省空间,告诉编译器取消编译过程中的优化对齐,按照实际占用字节数进行对齐

c 复制代码
struct __attribute__ ((packed)) sdshdr32 {
    uint32_t len;          // 当前字符串长度
    uint32_t alloc;        // 已分配的内存大小
    unsigned char flags;   // 编码类型
    char buf[];            // 实际字符串数据
};

通过源码,redis 相比于 C 语言的 char 数组实现字符串,有以下好处

  • SDS可以通过 O(1 )的方式获取字符串长度,而不是像 C 语言那样通过遍历获得
  • SDS可以存储二进制数据文件,因为 SDS 以 len 来判断是否为字符串末尾,不再像 C 语言那样利用'\0'来判断
  • SDS 的 API 更为安全,拼接字符串不会造成缓冲区溢出的问题。这是因为 SDS 可以通过 alloc-len 来检查剩余空间是否满足要求

现在 redis 对于 String 实现的结构定义源码已经看了,我们现在可以来关注当 encoding 使用不同的编码方式时,redis 有关 String 的存储形式是什么样子的。

不同编码实现

encoding=int

当 redis 的 String 存储的是整数值,并且这个整数值可以用 long 来表示,那么字符串对象会将整个数值保存在 redisObject 的 ptr 属性之中(将 void* 转换成 long),并将字符串对象的编码设置为 int,如下图(图片来源于小林 coding

encoding=embstr

当 redis 的 string 保存的是一个字符串,且这个字符串长度小于等于 32 字节(这个与 redis 的版本有关),那么字符串对象的 encoding 将为 embstr,如下图(图片来源于小林 coding

当使用这中编码方式时,字符串相关的结构体和数据将存放在连续的内存中,分配的时候,只需要分配一次,减少内存分配和管理开销

encoding=raw

当 redis 的 string 保存的是一个字符串,且这个字符串长度大于 32 字节(这个与 redis 的版本有关),那么字符串对象的 encoding 将为 raw,如下图(图片来源于小林 coding

其中可以看到这种编码方式将通过调用两次内存分配函数来分别分配两块空间来保存 redisObject 和 SDS,将 redis 对象结构体和数据分开存储,以便处理更长的数据。

最后总结一下

  • int 编码:用于存储可以解析为整数的字符串,内存消耗最小,适合数值
  • embstr 编码:用于存储较短的字符串,将结构和数据存储在同一块内存中,一次性分配内存
  • raw 编码:用于存储较长的字符串,调用两次内存分配函数来分别分配两块空间来保存,结构和数据分开存储,适合频繁操作的大字符串

参考资料

相关推荐
北邮刘老师11 分钟前
马斯克的梦想与棋盘:空天地一体的智能体互联网
数据库·人工智能·架构·大模型·智能体·智能体互联网
开开心心_Every13 分钟前
优化C盘存储:自定义软件文档保存路径工具
java·网络·数据库·typescript·word·asp.net·excel
醉舞经阁半卷书121 分钟前
Etcd服务端参数详解
数据库·etcd
gugugu.25 分钟前
Redis持久化机制详解(一):RDB全解析
数据库·redis·缓存
暗之星瞳38 分钟前
mysql表的链接
大数据·数据库·mysql
陌路2041 分钟前
redis持久化篇AOF与RDB详解
数据库·redis·缓存
@老蝴1 小时前
MySQL - 索引
数据库·mysql
tgethe1 小时前
MySQL 进阶攻略
数据库·mysql
亮子AI1 小时前
【node.js MySQL】node.js 如何连接 MySQL?
数据库·mysql·node.js
程序员根根1 小时前
Web 开发必学:Java 数据库操作从 JDBC 到 MyBatis 的进阶之路
数据库·后端