很多用过redis的同事都知道redis的5种基本数据结构。String、List、Hash、Set、zset。少部分同事会知道Redis中的Bitmap、Geo、HyperLogLog以及布隆过滤器。Redis这些数据类型的数据结构是怎么设计的呢?人人都说redis对内存优化到了极致,具体是怎么优化的呢?
首先小编先聊一聊Redis的第一种基础数据类型String。Java中的String是一个不可变定长字符串。Redis里面String是一个简单动态字符串构成,简称SDS,是可以追加的。
SDS组成结构:
struct __attribute__ ((__packed__)) sdshdr8 { uint8_t len; //数组长度 uint8_t alloc; //数组容量 unsigned char flags; //特殊标记位 char buf[]; //数据};
通过上面SDS的组成结构可以看出,Redis的String数据类型采用的是预分配空间的方式来减少内存的频繁扩展。
当字符串中存在冗余空间的时候,可以通过append操作在原字符串的末尾进行追加。
当字符串的长度超出已分配的容量的时候,需要把旧字符串拷贝到新的字符串中。
在Redis中,SDS存在两种存储结构。分别是:embstr和raw。存储的字符特别短的时候使用的是embstr。当存储的字符长度超过44个字节的时候,使用的raw。要了解存储形式,需要知道RedisObject。所有的Redis存储对象都会存在redisObject对象头的存在。
struct RedisObject { int4 type; // 类型 int4 encoding; // 存储格式 int24 lru; // 记录LRU信息 int32 refcount; // 引用次数 void *ptr; // 指针 } robj;
不同的对象存在不同的type,相同的对象也存在不同的encoding。SDS就是如此。
embstr 由对象头、指针、sds连接而成。由malloc函数一次分配而成。raw是通过malloc函数二次分配而成,存在两个对象头,两个对象头的地址不连续。
相比较于C语言。SDS动态字符串提供了空间预分配和惰性空间释放的方式
当SDS要进行缩短的时候,内存并不会回收缩短后的那部分空间,而是修改表头的free字段,留给下次使用。
我是凯腾凯,互联网浪潮下一枚苟且偷生的程序员,有兴趣的同学可关注微信公众号:凯腾凯。定期分享一些技术总结文字。