redis之数据结构

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实现
结构如图:redis之数据结构

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:

  1. hash对象保存的键和值字符串长度都小于64字节
  2. 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,也就是跳跃表

上一篇:chapter5 关联式容器:hashtable相关


下一篇:桶排序