Redis是互联网技术领域使用广泛的存储中间件,其全称是“Remote Dictionary service”。Redis以其高性能、丰富的文档、简洁易懂的源码和丰富的客户端支持受到好评,许多大型公司都在使用,比如新浪微博、twitter、阿里巴巴、华为、腾讯等。
1redis的常见数据类型
string(字符串):内部是一个带长度信息的字符数组,也叫“SDS”。Redis的字符串是动态字符串,可以修改,类似于StringBuilder类,单个Redis的键和值大小不能超过512MB。常见的string操作指令就不多说了,大家都明白。
struct SDS<T>{
//数组容量
T capactity;
//数组长度
T len;
//数组标志位
byte flags;
//数组内容
byte[] content;
}
Redis的字符串有两种存储方式,当长度较短时,使用embstr存储,当长度超过44字节时,使用raw存储,如下图所示。
为什么会有这种现象,要先了解redis的对象头结构,所有的redis对象都有下面的对象头结构
struct RedisObject{
int4 type; 4bits
int4 encoding; 4bits
int24 lru; 24bits
int32 refcount; 4bytes;
void *ptr; 8bytes
}
查看之前的SDS结构体,当字符串比较小时,分配一个字符串的最想空间为(16+3)字节,在分配内存时,大小是按2/4/8/16/32/64字节来分配的,为了能完整容纳一个embar对象,最少会分配32字节,再长就是64字节,而SDS中字符串是以NULL结尾的,所以留给字节数组的大小为64-1-19=44字节,再大就会转为raw类型,这就是原因。
list(列表):redis中的list相当于Java中的LinkedList,这意味着既可以用来当作栈,也可以用来当作队列,具有较快的插入和删除性能。实际上,list是一个quickList结构,称为压缩列表,他将所有的元素彼此紧挨着一起存储,分配的是一块连续的内存,有兴趣的可以阅读源码进行学习,我也正在阅读,希望有所收获。list的用途很广泛,可以用来获取最新的数据,比如获取对某条信息的最新评论,存储是只要按照队列来使用能完成功能。
struct quicklistNode{
quicklistNode* prev;
quicklistNode* next;
ziplist* zl;
int32 size;
int16 count;
int2 encoding;
}
struct quicklist{
quicklistNode* head;
quicklistNode* tail;
long count;
int nodes;
int compressDepth;
}
hash(字典):redis中的hash相当于Java中的HashMap,内部存储了很多键值对,我们可以使用对应的命令来修改hash中对应的键值对的值。redis中的hash的值只能是string类型,并且由于redis追求的是高性能,所以采用了渐进式rehash策略,渐进式rehash策略会在rehash的同时,保留新旧两个hash结构,查询是会同时查询新旧两个hash结构,然后在后续的定时任务和使用中,将旧hash中的值慢慢迁入到新hash中,当搬迁完成,旧hash就会被替换删除了。
//渐进式hash
dictEntry *dictAddRaw(dict *d,void *key,dictEntry ** existing){
long index;
dictEntry *entry;
dictht *ht;
//这里进行小步搬迁
if(dictIsRehashing(d)) _dictRehashStep(d);
if((index=_dictKeyIndex(d,key,dictHashKey(d,key),existing))==-1){
return null;
}
//如果字典处于搬迁过程中,需要将新的元素挂到新的数组下面
ht=dictIsRehashing(d)?&d->ht[1]:&d->ht[0];
entry=zmalloc(sizeof(*entry));
entry->next=ht->table[index];
ht->table[index]=entry;
ht->used++;
dictSetKey(d,entry,key);
return entry;
}
func get(key){
let index=hash_func(key)%size;
let entry=table[index];
while(entry!=null){
if(entry.key==target){
return entry.value;
}
}
}
set(集合):redis的集合相当于Java的HashSet,他的内部实现当于一个字典,内部的键值对是无序的,并且每个键值对的值都为null,当删除最后一个元素时,数据结构被自动删除,可以用来存储中奖的信息,具有去重功能。
zset(有序集合):这可以说是redis使用最多的数据结构,可以用来记录热榜,分数排名,记录帖子的点赞用户排名,在实际运用的非常广泛。他在set的基础上维护了一个score,可以是数目,时间,代表了这个value的权重,通过zrange和zrevrange两个命令来顺序遍历和倒序遍历zset,他的内部实现是跳跃列表结构。
struct zslnode{
string value;
double score;
zslnode*[] forwards;
zslnode*[] backword;
}
struct zsl{
//跳跃列表头指针
zslnode* header;
//跳跃列表最高层
int maxLevl;
//所有的键值对
map<String,zslnode*> ht;
}
关于跳跃列表,可参考:https://blog.csdn.net/lz710117239/article/details/78408919