您的位置:首页 > 数据库 > Redis

Redis核心结构字典

2014-09-20 12:52 183 查看
Redis 是支持多key-value数据库,并用 RedisDb 来表示一个key-value数据库. redisServer 中有一个 redisDb *db成员变量, RedisServer 在初始化时,会根据配置文件的 db 数量来创建一个
redisDb 数组. 客户端在连接后,通过 SELECT 指令来选择一个 reidsDb,如果不指定,则缺省是redisDb数组的第1个(即下标是 0 ) redisDb. 一个客户端在选择 redisDb 后,其后续操作都是在此 redisDb 上进行的. 下面是redisDb结构体

1:
2: /*
3:  * 数据库结构
4:  */
5: typedef struct redisDb {
6:     // key space,包括键值对象
7:     dict *dict;                 /* The keyspace for this DB */
8:     // 保存 key 的过期时间
9:     dict *expires;              /* Timeout of keys with a timeout set */
10:     // 正因为某个/某些 key 而被阻塞的客户端
11:     dict *blocking_keys;        /* Keys with clients waiting for data (BLPOP) */
12:     // 某个/某些接收到 PUSH 命令的阻塞 key
13:     dict *ready_keys;           /* Blocked keys that received a PUSH */
14:     // 正在监视某个/某些 key 的所有客户端
15:     dict *watched_keys;         /* WATCHED keys for MULTI/EXEC CAS */
16:     // 数据库的号码
17:     int id;
18: } redisDb;
19:







其中redisDb结构中包含指向dict结构的指针,这个指针指向了字典结构,下面我们具体分析字典结构的用途、组成和一些操作

Redis中dict的主要应用

dict的主要用途有两个:

1.实现数据库的键空间

2.用作hash类型键的一种实现

实现数据库键空间

Redis 是一个键值对数据库,数据库中的键值对就由字典保存:每个数据库都有一个与之相对

应的字典,这个字典被称之为键空间(key space)。

当用户添加一个键值对到数据库时(不论键值对是什么类型),程序就将该键值对添加到键空

间;当用户从数据库中删除一个键值对时,程序就会将这个键值对从键空间中删除;等等

用作 Hash 类型键的其中一种底层实现

Redis 的 Hash 类型键使用以下两种数据结构作为底层实现:

1. 字典;

2. 压缩列表;

dict的实现

Redis用hash表作为字典的底层实现

下面给出dict结构的定义

1:
2: /*
3:  * 字典
4:  *
5:  * 每个字典使用两个哈希表,用于实现渐进式 rehash
6:  */
7: typedef struct dict {
8:
9:     // 特定于类型的处理函数
10:     dictType *type;
11:
12:     // 类型处理函数的私有数据
13:     void *privdata;
14:
15:     // 哈希表(2个)
16:     dictht ht[2];
17:
18:     // 记录 rehash 进度的标志,值为-1 表示 rehash 未进行
19:    int rehashidx;
20:
21:     // 当前正在运作的安全迭代器数量
22:     int iterators;
23:
24: } dict;
25:

其中dict结构中包含一个具有两个hash表的数组,hash表用dictht结构体表示,下面是其具体实现:

1:
2: /*
3:  * 哈希表
4:  */
5: typedef struct dictht {
6:
7:     // 哈希表节点指针数组(俗称桶,bucket)
8:     dictEntry **table;
9:
10:     // 指针数组的大小
11:     unsigned long size;
12:
13:     // 指针数组的长度掩码,用于计算索引值
14:     unsigned long sizemask;
15:
16:     // 哈希表现有的节点数量
17:     unsigned long used;
18:
19:} dictht;
20:

table实际是一个数组,数组的元素是指向dictEntry的指针,而每一个dictEntry保存着一个键值对,还有一个指向另一个dictEntry结构的指针

1:
2: /*
3:  * 哈希表节点
4:  */
5: typedef struct dictEntry {
6:
7:     // 键
8:     void *key;
9:
10:     // 值
11:     union {
12:         void *val;
13:         uint64_t u64;
14:         int64_t s64;
15:     } v;
16:
17:     // 链往后继节点
18:     struct dictEntry *next;
19:
20:} dictEntry;
21:

其中多个dictEnty通过其中的next指针可以构成链表,而redis中的hash表使用链接地址法处理碰撞,当多个不同的键具有相同的哈希值时,采用链接的方法将它们连接成一个链表

下图是整个dict字典结构的图示:





rehash

使用链地址法来解决碰撞问题的哈希表dictht来说,哈希表的性能依赖于它的大小(size属性)和它所保存的节点的数量(used 属性)之间的比率

• 比率在 1:1 时,哈希表的性能最好;

• 如果节点数量比哈希表的大小要大很多的话,那么哈希表就会退化成多个链表,哈希表本身的性能优势就不再存在

为了在键值对不断增加的情况下继续保持高性能,字典需要对hash表进行rehash,对hash表进行扩容

那么什么情况下会进行hash表的扩容呢?

dictAdd 在每次向字典添加新键值对之前,都会对哈希表 ht[0] 进行检查,对于 ht[0] 的size 和 used 属性,如果它们之间的比率 ratio = used / size 满足以下任何一个条件的话,rehash 过程就会被激活:

1. 自然 rehash :ratio >= 1 ,且变量 dict_can_resize 为真。

2. 强 制 rehash : ratio 大 于 变 量 dict_force_resize_ratio

rehash执行过程

字典的 rehash 操作实际上就是执行以下任务:

1. 创建一个比 ht[0]->table 更大的 ht[1]->table(ht[1]的size是ht[0]的两倍) ;

2. 将 ht[0]->table 中的所有键值对迁移到 ht[1]->table

3. 将原有 ht[0] 的数据清空,并将 ht[1] 替换为新的 ht[0]

当然rehash并不是在激活或就立即执行的,而是渐进地、分多次进行的

因为服务器不能一直阻塞等待rehash完成才处理客户到达的命令,所以我们采用渐进的rehash方法:

渐进式 rehash 主要由 _dictRehashStep 和 dictRehashMilliseconds 两个函数进行:

• _dictRehashStep 用于对数据库字典、以及哈希键的字典进行被动 rehash ;

• dictRehashMilliseconds 则由 Redis 服务器常规任务程序(server cron job)执行,用于对数据库字典进行主动 rehash ;

每次执行 _dictRehashStep ,ht[0]->table 哈希表第一个不为空的索引上的所有节点就会全部迁移到 ht[1]->table ,在 rehash 开始进行之后(d->rehashidx 不为 -1),每次执行一次添加、查找、删除操作,_dictRehashStep 都会被执行一次:





dictRehashMilliseconds 可以在指定的毫秒数内,对字典进行 rehash

当 Redis 的服务器常规任务执行时(serverCrom),dictRehashMilliseconds 会被执行,在规定的时间内,尽可能地对数据库字典中那些需要 rehash 的字典进行 rehash ,从而加速数据库字典的 rehash进程(progress)

其他注意事项

在哈希表进行 rehash 时,字典还会采取一些特别的措施,确保 rehash 顺利、正确地进行:

• 因为在 rehash 时,字典会同时使用两个哈希表,所以在这期间的所有查找、删除等操作,除了在 ht[0] 上进行,还需要在 ht[1] 上进行

• 在执行添加操作时,新的节点会直接添加到 ht[1] 而不是 ht[0] ,这样保证 ht[0] 的节点数量在整个 rehash 过程中都只减不增
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: