redis源码-字符串
结构
redis 中的字符串叫SDS (simple dynamic string),结构如下:
struct SDS<T>{
T capacity; // 数组容量
T len; // 实际长度
bytes flags; // 特殊标志位 无需关注
byte[] content; // 数组内容 实际存放的值
}
capacity 和len 有点类似java中的ArrayList。一般来说redis就预分配空间,即capacity > len,为了支持append的操作。
T:泛型,可以根据数组的长度用byte、short、int、long 等表示,可见redis 将内存优化到了极致。
redis 规定字符串的长度不会超过512M,当新建string时,capacity = len,因为设计人员认为,一般不会用append的操作。当len>capacity 时,redis就会扩容。当redis key小于1M时,扩容是翻倍进行的,大于1M时,redis为了节省内存,每次扩容增加1M.
存储方式
redis 的字符串有两种存储方式。在长度特别短时,使用embstr,当长度超过44字节时,使用raw的形式存储。
## 查看存储方式:
> set len45 111111111122222222223333333333444444444455555
OK
> set len44 11111111112222222222333333333344444444445555
OK
> debug object len45
Value at:0x7f13cce45960 refcount:1 encoding:raw serializedlength:29 lru:5761491 lru_seconds_idle:52
> debug object len44
Value at:0x7fa70090eb80 refcount:1 encoding:embstr serializedlength:27 lru:5761519 lru_seconds_idle:102
那么问题来了,为什么是44字节呢?
对象头
所有redis对象都有一个对象头
struct RedisObject{
int4 type; // 4bits 类型
int4 encoding // 4bits 存储类型
int24 lru; // 24bits 记录LRU信息 即 最后一次被访问的时间戳
int32 reconf; //4bytes 引用计数
void *ptr //8bytes 指向对象内容具体的位置
}
从对象头的结构可以看出,每个对象头占用16个字节(0.5+0.5+3+4+8)。而一个字符串SDS至少占3个字节(字符串长度小的时候capacity、len、flag占用一个字节),因此一个字符串至少占用19个字节(16+3 此时字符串没有任何长度)。
内存分配器分配的内存大小单位都是2/4/8/16/32/64字节。若想让内存分配器只分配一次(2/4/8/16/32/64 都是只分配一次),字符串的最大值是64(上面说过字符串最小为19个字节,因此字符串小的时候可以只分配一次额度),因此redis将只需要内存分配一次的字符串定义一种存储方式—embstr,需要分配多次的字符串定义另外一种存储方式—raw。因此,embstr和raw的分界线就是64字节。上面说过字符串最小为19个字节,由于字符串需要用null结尾,因此embstr存储形式的字符串的最大长度为64-19-1=44。