redis数据结构:
string
String通过 int、SDS(simple dynamic string)作为结构存储,int用来存放整型数据,sds存放字节/字符串和浮点型数据。
typedef char *sds;
sdshdr有五种类型,所以至少需要3位来表示
000:sdshdr5
001:sdshdr8
010:sdshdr16
011:sdshdr32
100:sdshdr64
struct __attribute__ ((__packed__)) sdshdr8 { //8表示字符串最大长度是2^8-1 (长度为255)
uint8_t len;//表示当前sds的长度(单位是字节)
uint8_t alloc;//表示已为sds分配的内存大小(单 位是字节)
unsigned char flags;//用一个字节表示当前sdshdr的类型,001表示他的类型
char buf[];//sds实际存放的位置
}
char buf[]的存储方式是什么样的,首先它是一个二进制,再加上是在内存的一个连续空间,所以中间需要使用标识符来表示结束
List类型
redis3.2之前,List类型的value对象内部以linkedlist或者ziplist来实现, 当list的元素个数和单个元素的长度比较小 的时候,
Redis会采用ziplist(压缩列表)来实现来减少内存占用。否则就会采用linkedlist(双向链表)结构
redis3.2之后,采用的一种叫quicklist的数据结构来存储list,列表的底层都由linkedlist+ziplis实现
结构如图:
typedef struct quicklist {
//指向头部(最左边)quicklist节点的指针
quicklistNode *head;
//指向尾部(最右边)quicklist节点的指针
quicklistNode *tail;
//ziplist中的entry节点计数器
unsigned long count;
//quicklist的quicklistNode节点计数器
unsigned int len;
//保存ziplist的大小,配置文件设定,占16bits
int fill : 16;
//保存压缩程度值,配置文件设定,占16bits,0表示不压缩
unsigned int compress : 16;
}
压缩的list数据结构:ziplist
typedef struct quicklistLZF {
//表示被LZF算法压缩后的ziplist的大小
unsigned int sz;
//保存压缩后的ziplist的数组,柔性数组
char compressed[];
}
hash类型
map提供两种结构来存储,一种是hashtable、另一种是前面讲的ziplist,数据量小的时候用ziplist
什么时候使用ziplist:
- hash对象保存的键和值字符串长度都小于64字节
- hash对象保存的键值对数量小于512
在redis中,哈希表分为三层,分别是,源码地址【dict.h】
dictEntry:也就是map中的一个元素,也可以说的基本单位
管理一个key-value,同时保留同一个桶中相邻元素的指针,用来维护哈希桶的内部链;
typedef struct dictEntry {
void *key;
union {
//因为value有多种类型,所以value用了union来存储
void *val;
uint64_t u64;
int64_t s64;
double d;
} v;
struct dictEntry *next;//下一个节点的地址,用来处理碰撞,所有分配到同一索引的元素通过next指针 链接起来形成链表key和v都可以保存多种类型的数据
}
dictht:dictht表示的,这个每个元素所属的下标
实现一个hash表会使用一个buckets存放dictEntry的地址,一般情况下通过hash(key)%len得到的值就是buckets的 索引,这个值决定了我们要将此dictEntry节点放入buckets的哪个索引里,这个buckets实际上就是我们说的hash 表。dict.h的dictht结构中table存放的就是buckets的地址
typedef struct dictht { dictEntry **table;//buckets的地址 unsigned long size;//buckets的大小,总保持为 2^n unsigned long sizemask;//掩码,用来计算hash值对应的buckets索引 unsigned long used;//当前dictht有多少个dictEntry节点 } dictht;
比如我们要讲一个数据存储到hash表中,那么会先通过murmur计算key对应的hashcode,然后根据hashcode取 模得到bucket的位置,再插入到链表中
dict:在扩容的时候会用到
dictht:实际上就是hash表的核心,但是只有一个dictht还不够,比如rehash、遍历hash等操作,所以redis定义了 一个叫dict的结构以支持字典的各种操作,当dictht需要扩容/缩容时,用来管理dictht的迁移,以下是它的数据结 构,源码在
typedef struct dict {
dictType *type;//dictType里存放的是一堆工具函数的函数指针,
void *privdata;//保存type中的某些函数需要作为参数的数据
dictht ht[2];//两个dictht,ht[0]平时用,ht[1] rehash时用
long rehashidx; //当前rehash到buckets的哪个索引,-1时表示非rehash状态
int iterators; //安全迭代器的计数。
} dict;
set类型
Set在的底层数据结构以intset或者hashtable来存储。
当set中只包含整数型的元素时,采用intset来存储,
否则,采用hashtable存储
zset类型
zset类型的数据结构就比较复杂一点,内部是以ziplist或者skiplist+hashtable来实现,
这里面最核心的一个结构就 是skiplist,也就是跳跃表