Redis系列-13.Redis经典五大类型源码及底层实现(一)(上):https://developer.aliyun.com/article/1414742
五大经典结构解析
各个类型的数据结构的编码映射和定义
Debug Object key
开启后
Value at: 内存地址
refcount: 引用次数
encoding: 物理编码类型
serializedlength: 序列化后的长度(注意这里的长度是序列化后的长度,保存为rdb文件时使用了该算法,不是真正存贮在内存的大小),会对字串做一些可能的压缩以便底层优化
lru:记录最近使用时间戳
lru_seconds_idle:空闲时间
String数据类型介绍
3大物理编码方式
RedisObjectt内部对应的3大物理编码
int
保存long型(长整型)的64位(8个字节)的有符号整数
补充:只有整数才会使用int,如果是浮点数,Redis内部其实先将浮点数转化为字符串值,然后再保存。
embstr
代表embstr格式的SDS 简单动态字符串,保存长度小于44字节的字符串,顾名思义,表示嵌入式的String
raw
保存长度大于44字节的字符串
3大物理编码案例
假如现在展现一个字符串:Redis
Redis没有直接复用C语言的字符串,而是新建了属于自己的结构-----SDS
在Redis数据库里,包含字符串值的键值对都是由SDS实现的(Redis中所有的键都是由字符串对象实现的即底层是由SDS实现,Redis中所有的值对象中包含的字符串对象底层也是由SDS实现)。
SDS简单动态字符串
sds.h源码分析
Redis中字符串的实现,SDS有多种结构(sds.h):
sdshdr5、(2^5=32byte)
sdshdr8、(2 ^ 8=256byte)
sdshdr16、(2 ^ 16=65536byte=64KB)
sdshdr32、 (2 ^ 32byte=4GB)
sdshdr64,2的64次方byte=17179869184G用于存储不同的长度的字符串。
len 表示 SDS 的长度,使我们在获取字符串长度的时候可以在 O(1)情况下拿到,而不是像 C 那样需要遍历一遍字符串。
alloc 可以用来计算 free 就是字符串已经分配的未使用的空间,有了这个值就可以引入预分配空间的算法了,而不用去考虑内存分配的问题。
buf 表示字符串数组,真存数据的。
Redis为什么要重新设计一个SDS数据结构呢?
C语言没有Java里面的String类型,只能是靠自己的char[]来实现,字符串在 C 语言中的存储方式,想要获取 「Redis」的长度,需要从头开始遍历,直到遇到 ‘\0’ 为止。所以,Redis 没有直接使用 C 语言传统的字符串标识,而是自己构建了一种名为简单动态字符串 SDS(simple dynamic string)的抽象类型,并将 SDS 作为 Redis 的默认字符串。
C语言 | SDS | |
字符串长度处理 | 需要从头开始遍历,直到遇到 ‘\0’ 为止,时间复杂度O(N) | 记录当前字符串的长度,直接读取即可,时间复杂度 O(1) |
内存重新分配 |
分配内存空间超过后,会导致数组下标越级或者内存分配溢出 | 空间预分配 SDS 修改后,len 长度小于 1M,那么将会额外分配与 len 相同长度的未使用空间。如果修改后长度大于 1M,那么将分配1M的使用空间。惰性空间释放 有空间分配对应的就有空间释放。SDS 缩短时并不会回收多余的内存空间,而是使用 free 字段将多出来的空间记录下来。如果后续有变更操作,直接使用 free 中记录的空间,减少了内存的分配。 |
二进制安全 |
二进制数据并不是规则的字符串格式,可能会包含一些特殊的字符,比如 ‘\0’ 等。前面提到过,C中字符串遇到 ‘\0’ 会结束,那 ‘\0’ 之后的数据就读取不上了 | 根据 len 长度来判断字符串结束的,二进制安全的问题就解决了 |
源码分析
set k1 v1 底层发生了什么?调用关系
三大物理编码方式
int编码
set k1 123
命令示例: set k1 123
当字符串键值的内容可以用一个64位有符号整形来表示时,Redis会将键值转化为long型来进行存储,此时即对应 OBJ_ENCODING_INT 编码类型。内部的内存结构表示如下:
Redis 启动时会预先建立 10000 个分别存储 0~9999 的 redisObject 变量作为共享对象,这就意味着如果 set字符串的键值在 0~10000 之间的话,则可以
直接指向共享对象 而不需要再建立新对象,此时键值不占空间!
set k1 123
set k2 123
接下来就很类似Integer,如果是128以内的话,那么常量池中就存在
redis源代码:server.h,笔记下面还有
redis6源代码:object.c笔记下面还有
redis7源代码:object.c笔记下面还有
EMBSTR编码格式
set k1 abc
redis源代码:object.c
对于长度小于 44的字符串,Redis 对键值采用OBJ_ENCODING_EMBSTR 方式,EMBSTR 顾名思义即:embedded string,表示嵌入式的String。从内存结构上来讲 即字符串 sds结构体与其对应的 redisObject 对象分配在同一块连续的内存空间,字符串sds嵌入在redisObject对象之中一样。
进一步createEmbeddedStringObject方法
redis源代码:object.c
从图中也可以看出,内存相当紧凑,没有重新分配空间
RAW编码格式
set k1 大于44长度的一个字符串,随便写
当字符串的键值为长度大于44的超长字符串时,Redis 则会将键值的内部编码方式改为OBJ_ENCODING_RAW格式,这与OBJ_ENCODING_EMBSTR编码方式的不同之处在于,此时动态字符串sds的内存与其依赖的redisObject的内存不再连续了
明明没有超过阈值,为什么变成raw了?
判断不出来,就取最大Raw
转变逻辑图
Redis系列-13.Redis经典五大类型源码及底层实现(一)(下):https://developer.aliyun.com/article/1414746