Redis 字符串 SDS 存储大小

2020-04-27  本文已影响0人  Yellowtail

前言

突然对 Redis 里的 字符串存储空间感兴趣了,所以研究一下,分享在这里

更新: 2022-04-12 15:44:48 重新写了一下博客,之前写的貌似是错的 :(

RedisObject

redis 是一个 key-value数据库, 无论是 key, 还是 value, redis 都使用 redisObject 来包裹

结构如下

struct RedisObject {
    int4 type; // 4bits 表示对象的类型,包括5种基本类型
    int4 encoding; // 4bits  表示值对象的内部编码
    int24 lru; // 24bits  记录对象被命令访问的最后一次时间,和内存回收有很大关系
    int32 refcount; // 4bytes = 32bits 记录被引用的次数
    void *ptr; // 8bytes,64-bit system  指向真正内容的指针
}

源码位置是 redisObject

image.png

加起来就是 4 + 4 + 24 + 32 + 64 = 128 bit,也就是 16 字节

SDS

redis 的 key 一定是一个 字符串,所以存储结构是 SDS
我们都知道 Redis 是使用 SDS 数据结构来存储字符串的,原因和好处不是本文关注的重点

数据结构 表述如下( Redis 3.0 版本的结构, 3.0之后变化很大,本文不关注)
3.0 sdshdr

struct sdshdr {
    // 记录 buf 数组中已使用字节的数量
    // 等于 SDS 所保存字符串的长度
    unsigned  int len;

    // 记录 buf 数组中未使用字节的数量
    unsigned  int free;

    // 字节数组,用于保存字符串
    char buf[];
};

3.0 之后的版本,redis 会根据字符串长度的不同,选用不同的结构,如下


image.png

现在着重看一下 3.0 版本,这个版本的字符串结构用泛型来表达就是 RedisObject<Sds>

Key

int 长度为 4字节

初始化的时候,free=0
假设字符串长度为n, 那么 sds 长度为 4+4+n+1 = n+9
1 是因为有个\0

所以对于 redis里的 key-value 来说,key 是 sds,那么长度就是 n+9

再加上外面包裹的 redisObject 的 16字节,所以 key 的长度是 n+25 字节

比如

set a b

那么 a 占了 26字节

Value

value 和 key 的差别在于,value 可以变化,可以 append
初始化的时候,长度和 key 是一样的

value修改 的时候

当第一次追加append的时候,
如果新的大小 new=old+append 小于1MB的时候, 扩容的时候就 翻倍, new=new*2
也就是说,假设原本长度为5, 扩容后变成15,那么扩容后 总长度为30,free变成了15

如果新的大小new 大于1MB(new>1M), 那么总长度是new=new+1Mfree 就是 1MB

当再次追加append的时候,
如果free够用,那么就直接使用
如果不够用,还是按照上面的策略来

缩短的时候

缩短的时候,是一个惰性释放
会把多出来的空间记录到 free里,如果下一次扩容了,说不定能用上
当然了,redis也提供了一些api,可以真正的去释放这些空间

结论

字符串作为 key
长度是 n+25

字符串作为 value
如果没有被改过,那么长度也是 n+25
如果被修改过,那就复杂了

参考

3.0 之后的数据结构参考
简单动态字符串
知乎sds
sds
sds 编码的分界线39

上一篇下一篇

猜你喜欢

热点阅读