Redis总共支持五种数据类型:string,hash,list,set及zset。这里介绍字符串类型的实现

 

首先了解字符串对象的结构


// redis对象内存分配,列出主要相关的属性
redisObject {
    // 对于字符串对象,type = REDIS_STRING
    type
    // 字符串的底层编码,有三种:int、raw、embstr,后文介绍
    encoding
    // 指向实际保存字符串内容的空间
    ptr
    // 还有其他属性
    ......
}


上面的redisObject不实际保存字符串内容,而是通过ptr指向实际保存字符串内容的空间,叫sdshdr(Simple Dynamic String hdr 简单动态字符串)


struct sdshdr {
    // 记录buf数组中已使用的字节数,等同于字符串长度(不包括结尾的\0)
    int len;
    // 记录buf数组中未使用的字节数
    int free;
    // 实际保存字符串的字节数组
    char buf[];
}


这个sdshdr比较厉害,在字符串变长的时候可以预申请额外内存、缩短时不直接释放内存以备未来变长使用,等。

 

字符串编码

字符串对象有三种可能的底层编码:int、raw、embstr。

这三种编码格式是字符串对象底层空间分配的不同,对上层调用没有区别,不同编码的字符串对象在执行命令时效果是一样的

int编码

如果字符串保存的值是整数,范围在long之内,那么encoding会被设置为int,并且直接将证书值保存在ptr里(ptr不再指向一个复杂的sds结构了,直接是整数值)


// 插入long之内的值为整数的字符串
127.0.0.1:6379> set test "123" 
OK
127.0.0.1:6379> object encoding test
"int"
// 插入long的最大值2^63-1
127.0.0.1:6379> set test "9223372036854775807" 
OK
127.0.0.1:6379> object encoding test
"int"
// 插入2^63,就不是int编码了
127.0.0.1:6379> set test "9223372036854775808" 
OK
127.0.0.1:6379> object encoding test
"embstr"


raw编码

如果字符串保存的值长度比较大,那么encoding为raw,值使用sds(简单动态字符串),ptr指向这个sds的空间

比较大:《Redis设计与实现》里说长度大于39字节时,n多文章说长度大于32字节时,我测试返回长度大于44时才行,没查到是配置可选的还是redis版本指定的

总的来说就是对于大字符串,redis采用一套机制来控制字符串变长变短时的内存分配策略,实现是用的sds

embstr编码

如果字符串保存的值不是整数,并且长度不是很大,那么encoding为embstr,值也是sds,ptr指向这个sds的空间

embstr编码是专门用于保存段字符串的一种优化编码方式

  • embstr和raw的实际字符串存储都是用了sds,区别在于:
  • embstr编码的字符串对象,其所有数据保存在一块连续的内存空间中(redisObject和其ptr指向的sds在连续的内存空间),只需要一次内存分配操作
  • raw编码的字符串对象,redisObject和其ptr指向的sds是不连续的,需要两次内存分配

embstr的内存空间连续的优势

  • 其创建时的内存分配、释放时的内存回收次数都只有1次
  • 方便预读取一段空间内的数据做缓存

其他

  • embstr只读,没有修改方法。当对embstr编码的字符串做修改时,会先转为raw、再修改
127.0.0.1:6379> set test "hello"
OK
127.0.0.1:6379> object encoding test
"embstr"
127.0.0.1:6379> append test "world"
(integer) 10
127.0.0.1:6379> object encoding test
"raw"
  • int编码在执行append前,也会先转为raw,再修改
  • int编码执行整数计算时(比如incrby)直接使用整数编码
  • raw和embstr编码,执行整数方法(比如incrby)时,如果其内容为long内的整数,则先转为int再执行,执行完还是int。如果内容不是long内的整数,则向客户端抛出异常

字符串命令详细实现:

 

redis 获取前缀下的所有Key redis获取字符串_redis 获取前缀下的所有Key