本来想直接写Redis的数据类型,但是写着写着发现这样并没有一个很宏观的概念,所以临时抽象出来一篇文章,先从整体来看一下Redis的数据结构。
1.整体概览
首先从层级的角度看一下Redis的数据结构,RedisServer->RedisDB->dict->dicth->dictEntry。RedisServer不用想了,一台redis指定只有一个,而RedisDB默认一个server下面有16个,当然我们也可以在redis.conf配置文件直接修改;每个RedisDB内部包含2个dict的数据结构,dict内部包含dicth数组,数组有两个,主要是扩容的时候会用到。dicth内部包含dictEntry数组,dictEntry其实就是类似Java的hashmap的Entry节点,里面有key和value,redis是通过链地址法来解决哈希冲突,至于为啥不再用红黑树,我也不知道。
2.redisServer
redisServer在server.h头文件中,这个结构体一共有400多个成员变量,离谱的很,我只找一下个别的变量。
struct redisServer {
...
redisDb *db;
...
};
3.redisDb
这个字段还不算多的离谱。
typedef struct redisDb {
dict *dict; /* 存储数据的字典 */
dict *expires; /* 存储有过期时间的 key */
dict *blocking_keys; /* 收到推送的被阻止的密钥 */
dict *ready_keys; /* 客户端等待数据的密钥(BLPOP) */
dict *watched_keys; /* cas的方式监视事务的键 */
int id; /* 数据库id */
long long avg_ttl; /* 用于统计的平均ttl */
unsigned long expires_cursor; /* Cursor of the active expire cycle. */
list *defrag_later; /* 一个接一个,逐渐整理的key列表。 */
} redisDb;
4.dict
dict就是redis中的字典。这里有一点需要注意,Redis带有过期时间的key其实是属于分开存储的,key-value会放到*dict字段中,而key-过期时间戳会放在*expires字段中,字段dict & expires 都是dict类型的。
typedef struct dict {
dictType *type; /* 字典类型 */
void *privdata; /* 私有数据 */
dictht ht[2]; /* 一个字典有两个哈希表 */
long rehashidx; /* rehash 索引 */
unsigned long iterators; /* 当前正在使用的迭代器数量 */
} dict;
5.dicth
dicth类型就是字典里面的hash表,用来存放数据节点,也就是所谓的外层哈希。
typedef struct dictht {
dictEntry **table; /* 哈希表数组 */
unsigned long size; /* 哈希表大小 */
unsigned long sizemask; /* 掩码大小,用于计算索引值。总是等于 size-1 */
unsigned long used; /* 已有节点数 */
} dictht;
6.dictEntry
在Redis中所有数据都以 key-value 的形式存储在节点 dictEntry 中,其中 key 和 value 都是一个 redisObject 结构体对象,只不过 key 总是一个字符串类型的对象,value 则可能是任意一种数据类型的对象。 redisObject 结构体定义在 server.h 。
typedef struct dictEntry {
void *key; /* key 关键字定义 */
union {
void *val; /* value 定义 */
uint64_t u64;
int64_t s64;
double d;
} v;
struct dictEntry *next; /* 指向下一个键值对节点 */
} dictEntry;
7.redisObject
一定要注意一点:key和value都是redisObject类型,只不过key的type永远等于OBJ_STRING,也就是我们说的字符串类型,同时*ptr指针也是指向存储真实key数据的字符串。
typedef struct redisObject {
unsigned type:4;/* 对象的类型,包括:OBJ_STRING、OBJ_LIST、OBJ_HASH、OBJ_SET、OBJ_ZSET */
unsigned encoding:4;/* 具体的数据结构 */
unsigned lru:LRU_BITS; /* 24 位,对象最后一次被命令程序访问的时间,与内存回收有关 */
int refcount;/* 引用计数。当 refcount 为 0 的时候,表示该对象已经不被任何对象引用,则可以进行垃圾回收了*/
void *ptr;/* 指向对象实际的数据结构 */
} robj;
至此,我们就从外到内大概过了一些redis的数据结构,相信此时大家已经对Redis的数据结构有了一个宏观上的认知,接下来的几篇,我们来分析Redis可以存储的不同的数据类型。