本来想直接写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是通过链地址法来解决哈希冲突,至于为啥不再用红黑树,我也不知道。

Redis整体结构图.jpg

2.redisServer

redisServer在server.h头文件中,这个结构体一共有400多个成员变量,离谱的很,我只找一下个别的变量。

  1. struct redisServer {
  2. ...
  3. redisDb *db;
  4. ...
  5. };

3.redisDb

这个字段还不算多的离谱。

  1. typedef struct redisDb {
  2. dict *dict; /* 存储数据的字典 */
  3. dict *expires; /* 存储有过期时间的 key */
  4. dict *blocking_keys; /* 收到推送的被阻止的密钥 */
  5. dict *ready_keys; /* 客户端等待数据的密钥(BLPOP) */
  6. dict *watched_keys; /* cas的方式监视事务的键 */
  7. int id; /* 数据库id */
  8. long long avg_ttl; /* 用于统计的平均ttl */
  9. unsigned long expires_cursor; /* Cursor of the active expire cycle. */
  10. list *defrag_later; /* 一个接一个,逐渐整理的key列表。 */
  11. } redisDb;

4.dict

dict就是redis中的字典。这里有一点需要注意,Redis带有过期时间的key其实是属于分开存储的,key-value会放到*dict字段中,而key-过期时间戳会放在*expires字段中,字段dict & expires 都是dict类型的。

  1. typedef struct dict {
  2. dictType *type; /* 字典类型 */
  3. void *privdata; /* 私有数据 */
  4. dictht ht[2]; /* 一个字典有两个哈希表 */
  5. long rehashidx; /* rehash 索引 */
  6. unsigned long iterators; /* 当前正在使用的迭代器数量 */
  7. } dict;

5.dicth

dicth类型就是字典里面的hash表,用来存放数据节点,也就是所谓的外层哈希。

  1. typedef struct dictht {
  2. dictEntry **table; /* 哈希表数组 */
  3. unsigned long size; /* 哈希表大小 */
  4. unsigned long sizemask; /* 掩码大小,用于计算索引值。总是等于 size-1 */
  5. unsigned long used; /* 已有节点数 */
  6. } dictht;

6.dictEntry

在Redis中所有数据都以 key-value 的形式存储在节点 dictEntry 中,其中 key 和 value 都是一个 redisObject 结构体对象,只不过 key 总是一个字符串类型的对象,value 则可能是任意一种数据类型的对象。 redisObject 结构体定义在 server.h 。

  1. typedef struct dictEntry {
  2. void *key; /* key 关键字定义 */
  3. union {
  4. void *val; /* value 定义 */
  5. uint64_t u64;
  6. int64_t s64;
  7. double d;
  8. } v;
  9. struct dictEntry *next; /* 指向下一个键值对节点 */
  10. } dictEntry;

7.redisObject

一定要注意一点:key和value都是redisObject类型,只不过key的type永远等于OBJ_STRING,也就是我们说的字符串类型,同时*ptr指针也是指向存储真实key数据的字符串。

  1. typedef struct redisObject {
  2. unsigned type:4;/* 对象的类型,包括:OBJ_STRING、OBJ_LIST、OBJ_HASH、OBJ_SET、OBJ_ZSET */
  3. unsigned encoding:4;/* 具体的数据结构 */
  4. unsigned lru:LRU_BITS; /* 24 位,对象最后一次被命令程序访问的时间,与内存回收有关 */
  5. int refcount;/* 引用计数。当 refcount 为 0 的时候,表示该对象已经不被任何对象引用,则可以进行垃圾回收了*/
  6. void *ptr;/* 指向对象实际的数据结构 */
  7. } robj;

至此,我们就从外到内大概过了一些redis的数据结构,相信此时大家已经对Redis的数据结构有了一个宏观上的认知,接下来的几篇,我们来分析Redis可以存储的不同的数据类型。

Redis整体结构图.jpg