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 编码:用于存储较长的字符串,调用两次内存分配函数来分别分配两块空间来保存,结构和数据分开存储,适合频繁操作的大字符串

参考资料

相关推荐
Re.不晚25 分钟前
MySQL进阶之战——索引、事务与锁、高可用架构的三重奏
数据库·mysql·架构
老邓计算机毕设35 分钟前
SSM智慧社区信息化服务平台4v5hv(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
数据库·ssm 框架·智慧社区、·信息化平台
麦聪聊数据1 小时前
为何通用堡垒机无法在数据库运维中实现精准风控?
数据库·sql·安全·低代码·架构
2301_790300961 小时前
Python数据库操作:SQLAlchemy ORM指南
jvm·数据库·python
m0_736919101 小时前
用Pandas处理时间序列数据(Time Series)
jvm·数据库·python
亓才孓1 小时前
[JDBC]PreparedStatement替代Statement
java·数据库
m0_466525292 小时前
绿盟科技风云卫AI安全能力平台成果重磅发布
大数据·数据库·人工智能·安全
爱学习的阿磊2 小时前
使用Fabric自动化你的部署流程
jvm·数据库·python
摇滚侠2 小时前
阿里云安装的 Redis 在什么位置,如何找到 Redis 的安装位置
redis·阿里云·云计算
枷锁—sha2 小时前
【SRC】SQL注入快速判定与应对策略(一)
网络·数据库·sql·安全·网络安全·系统安全