Redis源码学习之字符串对象

前文中提到,Redis的字符串对象的底层数据结构有三种,分别是整数编码、EMBSTR编码和SDS编码。在不同使用场景下进行相互切换,起到节约内存的作用。

1.整数编码

针对将某个key对应的值设置为整数类型的情况,有以下两种情况

  1. Redis服务器没有配置LRU相关的淘汰策略&&设置的整数值在共享整数对象范围内,则使用整数共享对象。
  2. 如果不能使用整数共享对象,则将原对象编码为整数编码类型,并释放原对象使用sds空间。

举个例子,我们使用如下命令:

set a 11111

将键a的值设置为11111(设置为11111是因为共享对象的范围是[0,10000]),我们通过GDB设置断点,打印相关变量:

从图中可以看到,字符串对象的encoding字段从RAW变成了INT,对应的底层数据结构指针也发生了相应的变化。使用【object encoding】命令查看编码类型。

2.EMBSTR编码

EMBSTR编码是专门用于存储短字符串而实现的,当字符串的长度不大于39个字节的时候,就会将该字符串对象编码为EMBSTR类型。EMBSTR编码使用一块连续的内存空间,可以更好的利用到cache line。此外,对比RAW编码,在内存分配和内存释放的次数上都降低了一倍。

其中,创建EMBSTR编码的字符串代码如下:

代码中可以看到,实际上EMBSTR的底层数据结构也是复用了sdshdr结构体,但是在创建的时候只调用了一次zmalloc,而RAW编码则会调用两次,因此,EMBSTR是将redisObject和sdshdr存放到一块连续的内存空间中去了。值得一提的是,Redis并没有为EMBSTR实现修改的方法,所以任何对EMBSTR编码的字符串的修改(例如append),都会使其从EMBSTR转为RAW编码,再进行修改操作。

Redis在处理客户端请求命令时,会针对命令字符串的长度选择使用EMBSTR还是RAW,在后面说到“服务器与客户端交互”时还会具体介绍,代码如下(networking.c:processMultibulkBuffer):

3.SDS编码

SDS编码类型的字符串是Redis基于自身实现的简单动态字符串,它的结构如下图所示:

从图中可以看出,SDS通过len字段记录字符串实际长度,通过free字段记录字符串剩余长度,此外由于Redis是C语言编写,所以继承了C语言字符串以“\0”作为终止符的规则,额外分配一个字节空间给\0(为了兼容C字符串API),因此图中这个字符串(hello)总共占用的内存空间为5+5+1=11个字节。那么,为什么Redis要设计SDS而不直接使用C语言的字符串呢?主要有以下几点:

  1. 避免C字符串的缓冲溢出问题,SDS提供的各种API会根据SDS字符串的剩余空间做扩容策略,而C字符串是不会检查的
  2. 通过len字段可以很方便的获取SDS字符串长度,而C字符串需要遍历查找\0字符
  3. 减少字符串内存空间分配次数,由于free字段的存在,可以在创建字符串的时候预先分配一定的free空间,这样针对一些append操作就不需要再分配一次了。当SDS字符串长度在1M字节以下时,分配的free长度和len长度是一样的;当大于1M字节时,至多分配free长度为1M字节,所以这里也提醒我们当在Redis中存放一个长度M级别的字符串时,对它的修改操作会有额外的内存开销。
  4. 二进制安全,因为长度len字段的存在,SDS字符串API都是二进制安全的。

4.思维导图

欢迎关注我的公众号

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Petrichor的专栏

python: json库 & .json文件读写

[1] python读写json文件 [2] 使用 python 读写中文json

23820
来自专栏金朝麟的专栏

Module.exports和exports的区别

学习Seajs时,看到了exports.doSomething和module.exports,想对这两者的区别一探究竟。官方解释因为SeaJs和Nodejs都是...

20800
来自专栏技术专栏

慕课网Flask构建可扩展的RESTful API-4. 理解WTForms并灵活改造她

之前的代码,修改完成之后,已经修复了之前的缺陷,但是这样爆出了两个问题: 1.代码太啰嗦了,每个试图函数里,都需要这么写 2.ClientTypeError...

13510
来自专栏熊二哥

快速入门系列--深入理解C#

C#语言在近些年得到了长足的方法,代码风格越来越简洁美观,例如常用的泛型及其约束、可空类型、隐式类型、匿名类型和委托等,通过下面的表格可以对这部分相对简单的特性...

21150
来自专栏大内老A

ModelBinder——ASP.NET MVC Model绑定的核心

Model的绑定体现在从当前请求提取相应的数据绑定到目标Action方法的参数。通过前面的介绍我们知道Action方法的参数通过ParameterDescrip...

25390
来自专栏一枝花算不算浪漫

[C#反射]C#中的反射解析及使用.

444140
来自专栏Java帮帮-微信公众号-技术文章全总结

Java面试系列14

一、ArrayList和Vector的区别,HashMap和Hashtable的区别 就ArrayList与Vector主要从二方面来说:    1.同步性:...

32260
来自专栏GreenLeaves

C# checked和unchecked运算符

1、作用 checked和unchecked运算符用于CLR(公共语言运行时)强制对它们所作用的代码块,进行(不进行)代码溢出检测 2、示例说明 有代码如下: ...

23280
来自专栏GuZhenYin

C#7.0之ref locals and returns (局部变量和引用返回,之前欠大家的,现在补上)

没看过原文的请移步:[干货来袭]C#7.0新特性(VS2017可用) 废话不多说,直接进入正题. 首先我们知道 ref关键字是将值传递变为引用传递 那么我们先来...

20990
来自专栏光变

Redis持久化文件RDB的格式解析

Redis的RDB文件是对内存存储的一种表示。这个二进制文件足以完全恢复Redis当时的运行状态。 RDB文件格式针对快速读写进行了优化。LZF压缩被用于减小文...

17910

扫码关注云+社区

领取腾讯云代金券