一 、SDS字符串
redis使用SDS作为底层字符串数据结构,实际上也就是字符串 SDS定义:
struct sdshdr{
int len;// 表示字符串长度
int free;// 表示可用内存空间还有多少字节
char buf[];// 实际存储字符
}
但是和C语言中的字符串有一些区别:
①获取字符串的事件复杂度为O(1)
②不会造成缓冲区溢出,C语言在合并字符串时不一定会预留足够的内存空间,可能造成数据覆盖问题,但是SDS不会,SDS需要修改的时候,首先会检查free大小是否足够,如果不足够会进行空间预分配:如果长度小于1Mb,那么合并以后的剩余空间会等于此时的len,也就是说合并以后free=len,如果长度大于1Mb,那么合并之后的free永远是1Mb。在减少字符串的时候,剩余空间也不会立马被收回,而是在需要的时候,调用相应的API去释放这些空闲空间。
③二进制安全 :C语言字符串以 进行结尾,那么有些数据就会有限制,但是SDS不会对其中的数据进行任何限制,过滤等。SDS API都是以处理二进制的方式去处理字符。redis不是用这个数组保存字符,而是保存一系列二进制数据。
④但是实际上SDS字符串的结尾仍然是 。这是为了兼容一些C字符串函数
二、字典
//字典底层代码
typedef struct dictEntry {
void* key; // 健
union {
void* val;
uint64_t u64;
int64_t s64;
}v; //值
struct dictEntry* next; //指向下个节点
}dictEntry;
typedef struct dictht {
dictEntry** table;
unsigned long size; //哈希表大小
unsigned long sizemask; //哈希表掩码 总是等于size-1
unsigned long used; //目前已经有的节点数量
}dictht;
typedef struct dictType {
//各种函数 比如计算哈希值,复制数据,对比数据,销毁数据等
};
typedef struct dict {
dictType* type; //类型特定函数
void* privdata; //私有数据
dictht ht[2]; //哈希表
int rehashidx; //索引
};
哈希算法:使用哈希函数计算key的哈希值,然后与sizemask进行与运算,得到的结果就是索引值。
redis的哈希表使用链地址法解决冲突。但是由于链表没有尾结点,所以在插入数据的时候,采用头插法。
负载因子:哈希表已经存在的节点数量/哈希表大小
服务器在执行BGSAVE或者BGREWRITEAOF时,负载因子大于5进行扩展
服务器不在执行BGSAVE或者BGREWRITEAOF时,负载因子大于1进行扩展
负载因子小于0.1时,进行收缩。
渐进式rehash:哈希表中有的数据ht大小为2,一般来说ht[0]用来存放数据,但是当需要扩展的时候,ht[1]就有了用处,使用ht[1]对ht[0]进行复制,但是有时候键值对太多,一次性进行复制会对服务器的性能产生很大影响,所以采用渐进式rehash,每次复制一个链表,此时rehashidx下标就表示目前复制到了哪里。rehashidx=0时,表示rehash开始,每次复制完一个链表以后值加一,当值为-1的时候,表示工作已经完成。在复制完一个链表后,ht[0]对应的链表就会销毁。在此期间,进行查找,先找ht[0],如果没有再去ht[1]找。新增数据的话会直接加在ht[1]中。删除呢?更新呢?



