一个简单的字符串,为什么 Redis 要设计的如此特别 (2)

一个简单的字符串,为什么 Redis 要设计的如此特别

同样的,假如要缩小字符串的长度,那么也需要重新申请释放内存。否则,字符串一直占据着未使用的空间,会造成内存泄露

C 语言避免缓存区溢出和内存泄露完全依赖于人为,很难把控,但是使用 sds 就不会出现这两个问题,因为当我们操作 sds时,其内部会自动执行空间分配策略,从而避免了上述两种情况的出现。

空间预分配

空间预分配指的是当我们通过 api 对 sds 进行扩展空间的时候,假如未使用空间不够用,那么程序不仅会为 sds 分配必须要的空间,还会额外分配未使用空间,未使用空间分配大小主要有两种情况:

1、假如扩大长度之后的 len 属性小于等于 1MB (即 1024 * 1024),那么就会同时分配和 len 属性一样大小的未使用空间(此时 buf 数组已使用空间 = 未使用空间)。

2、假如扩大长度之后的 len 属性大于 1MB,那么就会分配 1MB 未使用空间大小。

执行空间预分配策略的好处是提前分配了未使用空间备用后,就不需要每次增大字符串都需要分配空间,减少了内存重分配的次数。

惰性空间释放

惰性空间释放指的是当我们需要通过 api 减小 sds 长度的时候,程序并不会立即释放未使用的空间,而只是更新 free 属性的值,这样空间就可以留给下一次使用。而为了防止出现内存溢出的情况,sds 单独提供给了 api 让我们在有需要的时候去真正的释放内存。

sds 和 C 语言字符串区别

下面表格中列举了 Redis 中的 sds 和 C 语言中实现的字符串的区别:

C 字符串 SDS
只能保存文本类不含空字符串 \0 数据   可以保存文本或者二进制数据,允许包含空字符串 \0  
获取字符串长度的复杂度为 O(n)   获取字符串长度的复杂度为 O(1)  
操作字符串可能会造成缓冲区溢出   不会出现缓冲区溢出情况  
修改字符串长度 N 次,必然需要 N次内存重分配   修改字符串长度 N 次,最多需要 N 次内存重分配  
可以使用 C 字符串相关的所有函数   可以使用 C 字符串相关的部分函数  
sds 是如何被存储的

在 Redis 中所有的数据类型都是将对应的数据结构再进行了再一次包装,创建了一个字典对象来存储的,sds也不例外。每次创建一个 key-value 键值对,Redis 都会创建两个对象,一个是键对象,一个是值对象。而且需要注意的是在 Redis 中,值对象并不是直接存储,而是被包装成 redisObject 对象,并同时将键对象和值对象通过 dictEntry 对象进行封装,如下就是一个 dictEntry 对象:

typedef struct dictEntry { void *key;//指向key,即sds union { void *val;//指向value uint64_t u64; int64_t s64; double d; } v; struct dictEntry *next;//指向下一个key-value键值对(哈希值相同的键值对会形成一个链表,从而解决哈希冲突问题) } dictEntry;

redisObject 对象的定义为:

typedef struct redisObject { unsigned type:4;//对象类型(4位=0.5字节) unsigned encoding:4;//编码(4位=0.5字节) unsigned lru:LRU_BITS;//记录对象最后一次被应用程序访问的时间(24位=3字节) int refcount;//引用计数。等于0时表示可以被垃圾回收(32位=4字节) void *ptr;//指向底层实际的数据存储结构,如:sds等(8字节) } robj;

当我们在 Redis 客户端中执行命令 set name lonely_wolf ,就会得到下图所示的一个结构(省略了部分属性):

一个简单的字符串,为什么 Redis 要设计的如此特别

看到这个图想必大家会有疑问,这里面的 type 和 encoding 到底是什么呢?其实这两个属性非常关键,Redis 就是通过这两个属性来识别当前的 value 到底属于哪一种基本数据类型,以及当前数据类型的底层采用了何种数据结构进行存储。

type 属性

type 属性表示对象类型,其对应了 Redis 当中的 5 种基本数据类型:

类型属性 描述 type 命令返回值
REDIS_STRING   字符串对象   string  
REDIS_LIST   列表对象   list  
REDIS_HASH   哈希对象   hash  
REDIS_SET   集合对象   set  
REDIS_ZSET   有序集合对象   zset  

可以看到,这就是对应了我们 5 种常用的基本数据类型。

encoding 属性

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wssxdx.html