String,List,hash,set,zset那么它们的底层实现是什么,redis系统又对这些数据进行了什么管理,这里就仔细分析一下。以下是redis数据结构的层次图。
1.RedisObject
typedef struct redisObject {
// 类型
unsigned type:4;
// 编码
unsigned encoding:4;
// 对象最后一次被访问的时间
unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */
// 引用计数
int refcount;
// 指向底层数据结构的指针
void *ptr;
} robj;
Redis并没用直接使用5种数据类型,而是将他们封装成一个统一的对象robj。
type值表示对象的属性也就是那5种类型。
encoding值是底层实现类型。
refcount是对象的引用次数,redis里面使用的是引用计数法去管理内存(因为系统已经是写死的,不会出现相互引用的情况),在每次释放资源的时候就会-1并检查是否为0为0就释放。
lru是这个对象最后一次被访问的时间,用于内存管理如果系统开了maxmemory并且服务器的内存回收算法是lru那么内存满后就会回收这些时间长的对象。
2.SDS(简单动态字符串)
struct sdshdr {
unsigned int len; //buf中已经使用的长度
unsigned int free; //buf中未使用的长度
char buf[]; //柔性数组buf
}
这里对于字符串的存储通过一个char数组进行存储,len表示长度,free表示剩余长度
空间预分配:redis数据操作次数多,那么每次对于字符串操作就分配新的内存是不现实的。所以给字符串操作后分配多一点的内存是redis系统中的一个功能。比如将二个长度字符串为5的合并在一起,如果一个sds的buf长度不够,那么就会分配10(合并长度)+10(预分配长度)的长度。
二进制安全: 不同于C语言以0结尾,redis提取字符串是依据长度提取这样就保证安全。
3.embstr
embedded),当长度超过44时,使用raw形式存储
embstr存储形式是这样一种存储形式,它将RedisObject 对象头和 SDS 对象连续存在一起,使用 malloc 方法一次分配。而raw存储形式不一样,它需要两次 malloc,两个对象头在内存地址上一般是不连续的
4.ziplist
特殊编码的连续内存块组成的顺序型(sequential)数据结构
5.Linkedlist
常规的双向链表,redis功能比如列表键、发布订阅、监视器等都使用这一结构
6.字典/ht(数据库里面的key-value存储,过期时间表的存储等等都用到字典)
typedef struct dict{
//类型特定函数
dictType *type;
//私有数据
void *privdata;
//哈希表
dictht ht[2];
//rehash索引
//当rehash不在进行时,值为-1
int rehashidx;
}dict;
redis里面哈希表(dictht)底层是通过数组+链表的形式组成的。而字典里面对于哈希表进行了一次包装,里面包含有两个哈希表,使用2个哈希表是为了rehash的均摊做准备。
rehash:通常的rehash比如java里面是创建一个两倍的entry数组然后把数据重新hash一遍。但是一遍rehash过于耗时,redis采取渐进式的方法去rehash。
假设当前数据在ht[0]中并且数据已经过了阈值那么就会分配一个两倍ht[0]的数组到ht[1]中,在字典中维持一个索引计数器rehashidx,每次对于字典的操作都会让rehashidx加一并对于ht[0]中的对应行ht[0][rehashidx]中的数据重新hash到ht[1]表中。
注意:程序在执行bgsave或者bgrewriteof的时候不能rehash
7.intset
typedef struct intset {
uint32_t encoding; // 编码类型 int16_t、int32_t、int64_t
uint32_t length; // 长度 最大长度:2^32
int8_t contents[]; // 柔性数组
} intset;
整数数据有序的存储在contents数组里面,通过二分查找来查找元素
8.skiplist-东西有点多引用一个讲的好的链接
https:// blog.csdn.net/yellowriv er007/article/details/79021103
9.5种数据格式总结
String类型的ptr指针可以指向一个long整数也可以指向一个sds或者embstr对象
List类型指向一个ziplist对象或者linkedlist对象
hash类型指向一个ziplist对象或者字典对象
Set类型指向一个intset对象或者字典对象
ZSet类型指向一个ziplist对象或者跳跃表+字典的组合结构对象
这里跳跃表加字典的组合结构,就是同时用字典和跳跃表去存储成员和score,优点是能以字典的特性在O(1)的时间查找某个值,以跳跃表的特性O(logN)的时间去查询范围。
以上结构(hash,list,zset)使用ziplist都是在数据量少的情况下,当数据量变多的时候就会将ziplist里面的数据转移到后面的对象中
同理String类型如果给一个整数就用int存,如果变成小的字符串就升级成embstr,如果字符串变大就升级成sds。
Set如果最开始添加了一堆整数就是intset,加入了一些字符串或者元素个数超过512个就升级成字典。