redis数据结构,网上资料也很多,redis设计与实现书上讲的也很明白,具体的也可以参考博客专栏,这里只对redis2.8和4.0版本做个简单的对比:
http://blog.csdn.net/column/details/15428.html
(sds,list,dictht,skiplist,intset,ziplist,quicklist)
简单动态字符串(sds),在Redis需要的不止是字符串字面量,而是一个可以被修改的字符串值时,Redis会使用SDS来表示,这里可以先看其定义,在2.8和4.0版本中定义是不一样的,4.0中的版本中有sdshdr5、sdshdr8、sdshdr16、sdshdr32、sdshdr64五种类型,可以在sds.h文件中找到。
redis2.8
struct sdshdr{
int len;// 记录buf数组中已经使用的字节数量
int free;//记录buf中未使用的字节数量
char buf[];//字节数组,用于保存字符串
};
redis4.0
struct __attribute__ ((__packed__)) sdshdr64 {
uint64_t len; //记录buf数组中已经使用的字节数量
uint64_t alloc; //分配的总字节数
unsigned char flags; //用0、1、2、3、4代表类型
char buf[];
};
SDS有以下好处:
1、可以以O(1)获取数组长度。
2、避免缓冲区溢出,因为当SDS需要修改时,会先检查空间是否足够,不够的话会自动扩容,然后修改。
3、减少内存重分配,c中对append或者trim操作会重新分配内存,而sds通过空间预分配和惰性空间释放对其进行了优化。
(空间预分配是在修改SDS之后,对于SDS的长度小于1MB时,程序会分配和当前数组len长度一致的free空间,而当修改的SDS的总长度大于1MB,则会多分配1MB的free空间。惰性空间释放既在对SDS进行截断或者缩短时,不会立即回收多出来的内存,而是将free增加,这样在可以保证避免频繁操作,当日SDS也提供了API可以真正释放内存。)
链表(list )是列表键的底层存储结构之一,定义在adlist.h文件中,链表类型在2.8中用于list的实现,在4.0中list则全部使用quicklist实现。
redis2.8和4.0中一致
typedef struct listNode {
struct listNode *prev;//前置节点
struct listNode *next;//后置节点
void *value;//节点值
} listNode;
typedef struct list {
listNode *head;//表头节点
listNode *tail;//表尾节点
void *(*dup)(void *ptr);//节点值复制函数
void (*free)(void *ptr);//节点值释放函数
int (*match)(void *ptr, void *key);//节点值匹配函数
unsigned long len;//链表所包含节点数量
} list;
字典(dictht)是哈希表的底层实现,一个哈希表里面有两个哈希表节点,一个用于存储,一个用于迁移数据,同样2.8和4.0的结构一致,可在dict.h文件中找到。
typedef struct dictEntry {
void *key;//键
union {//值
void *val;
uint64_t u64;
int64_t s64;
double d;
} v;
struct dictEntry *next;//下一个哈希表节点,链表形式
} dictEntry;
/* This is our hash table structure. Every dictionary has two of this as we
* implement incremental rehashing, for the old to the new table. */
typedef struct dictht {
dictEntry **table;//哈希表数组
unsigned long size;//哈希表大小
unsigned long sizemask;//哈希表大小掩码,用于计算索引值
unsigned long used;//哈希表已有节点数量
} dictht;
跳跃表(skiplist),跳跃表可在redis.h文件中找到,其中zkiplistNode和zskiplist为其定义,2.8和4.0定义一致。
typedef struct zskiplistNode {
robj *obj;//节点成员对象
double score;//节点分值,从小到大排序
struct zskiplistNode *backward;//后退指针,指向当前节点的前一个节点
struct zskiplistLevel {
struct zskiplistNode *forward;//前进指针,用于访问表尾方向的其他节点
unsigned int span;//跨度,记录前进指针所指节点和当前节点距离
} level[];
} zskiplistNode;
typedef struct zskiplist {
struct zskiplistNode *header, *tail;//表头尾节点
unsigned long length;//跳跃表长度,也就是当前包含节点长度
int level;//当前跳跃表层数最大的节点层数
} zskiplist;
typedef struct zset {
dict *dict;
zskiplist *zsl;
} zset;
整数集合(intset)是集合键的底层实现之一,当一个集合只包含整数元素,并且这个集合元素数量不多时,Redis就会使用整数集合作为集合键的底层实现,其中2.8和4.0定义一致。
typedef struct intset {
uint32_t encoding;//编码方式
uint32_t length;//集合包含的元素数量
int8_t contents[];//保存元素的数组
} intset;
压缩列表(ziplist)是列表键和哈希键的实现之一,在2.8中当一个列表键只包含少量列表时会使用ziplist,在4.0中列表键则是quicklist的实现,其中节点结构可在ziplist.c文件中找到,2.8和4.0的定义一致。
typedef struct zlentry {
unsigned int prevrawlensize; //编码 前驱节点的长度所需字节大小
unsigned int prevrawlen; //前驱节点长度
unsigned int lensize; //编码当前节点长度所需字节大小
unsigned int len; //当前节点值长度
unsigned int headersize; //prevrawlensize + lensize.
unsigned char encoding; // Set to ZIP_STR_* or ZIP_INT_* 当前节点编码格式
unsigned char *p; //指向当前节点指针
} zlentry;
(quicklist)是一个有ziplist组成的双向链表,从3.2版本新增的,可以在quicklist.h中找到其定义。
typedef struct quicklistNode {
struct quicklistNode *prev;//前驱节点指针
struct quicklistNode *next;//后驱节点指针
unsigned char *zl;//不设置压缩数据参数时指向ziplist,设置时指向quicklistLZF
unsigned int sz; //压缩列表ziplist总长度
unsigned int count : 16; //ziplist中包的节点数,占16bists
unsigned int encoding : 2; /* RAW==1 or LZF==2 */是否采用LZF压缩算法压缩quicklist节点
unsigned int container : 2; /* NONE==1 or ZIPLIST==2 */表示一个quicklistNode是否采用ziplist,1表示没压缩,2表示压缩了
unsigned int recompress : 1; //标记quicklist节点的ziplist是否被压缩过,占1bit
unsigned int attempted_compress : 1; //测试时使用
unsigned int extra : 10; //额外扩展位
} quicklistNode;
typedef struct quicklist {
quicklistNode *head;//指向头节点指针
quicklistNode *tail;//指向尾节点指针
unsigned long count; //ziplist节点计数器
unsigned int len; //quicklistNode节点计数器
int fill : 16; //ziplist的大小,配置文件设置,16bits
unsigned int compress : 16; //压缩程度值,配置文件设置,16bits,0表示不压缩
} quicklist;
不同类型和编码对象对应关系,以及转换条件
- String:int(使用整数值实现的字符串对象,如果一个字符串对象保存的是整数值,并且可以用long表示),embstr(使用embstr编码的简单动态字符串实现的字符串对象,字符串对象保存的是字符串值,并且字符串值长度小于39字节,),raw(使用简单动态字符串实现的字符串对象,字符串对象保存的是字符串值,并且字符串值长度大于39字节)
- List:ziplist,linkedlist,当列表对象保存的所有字符串长度都小于64字节,且保存的元素数量小于512个时用ziplist。
- Hash:ziplist,hashtable,哈希对象保存的键值对的键和值字符串长度都小于64字节,且保存的键值对数量小于512使用ziplist。
- Set:intset,hashtable,集合对象保存的元素都是整数值,且集合对象保存的元素数量不超过512个时用intset。
- Zset:ziplist,skiplist和Hashtable,有血集合保存的元素小于128个,且有序集合保存的所有元素成员的长度都小于64字节时使用ziplist。注意在因为类似ZRANK和ZRANGE命令用skiplist,ZSCORE则从Hashtable获取。