Redis中哈希结构(Dict)是一种以键值对(key-value pairs)方式存储数据的数据结构,可以看做是内存中的字典或映射。它采用一个哈希表(hash table)来实现键值对的快速查找,具有增删改查的高效能力。本文将详细讲解Redis中哈希结构(Dict)的实现过程。
一、哈希表(hash table)
哈希表是由哈希函数(hash function)将键(key)映射到存储桶(bucket)上,每个桶上对应着一个链表或红黑树(由linkedlist和ziplist构成),用来存储相应的值(value)。
对于哈希表中的每个键值对,Redis都将其存储在一个类似于字典节点(dictEntry)的数据结构中,其中包含了key、value和指向所在桶的指针等元素。不过,为了提升性能,Redis在存储dictEntry的时候,并没有将其保存为一个单独的内存块,而是将dictEntry的key和value等元素分别存储在一个ziplist中。所以在Redis中,哈希表实际上是由多个ziplist组成的。
下面是哈希表(hash table)的示意图,其中Bucket1、Bucket2等为各个桶,每个桶中的字母表示相应的键(key),数字表示相应的值(value)。
Bucket1: A -> 1 -> B -> 2 -> D -> 4
Bucket2: C -> 3 -> E -> 5
Bucket3: F -> 6
二、哈希结构(Dict)的实现
在Redis中,哈希结构(Dict)是由哈希表结合一些其他技术(如rehashing、渐进式rehashing、键值对的幂等性等)实现的。下面将详细介绍Redis中哈希结构的实现过程。
1. 创建哈希结构
当我们向Redis中添加一条新的哈希结构时,Redis会为其分配一个dict结构体来描述整个哈希结构。dict结构体的定义如下:
typedef struct dict {
dictType *type; // 类型特定函数
void *privdata; // 私有数据
dictht ht[2]; // 两个哈希表,ht[0]为主哈希表,ht[1]为rehash时使用的哈希表
long rehashidx; // rehash的数组的下标
unsigned long iterators; // 正在迭代的数量
} dict;
其中,type
为一个指向函数的指针,它定义了一些哈希表的操作函数,默认为字典类型(dictType)的操作函数;privdata
指向一些私有数据,可用于保存用户自定义的数据;ht
是一个数组,其中ht[0]
为主哈希表,ht[1]
为rehash时使用的哈希表;rehashidx
表示当前rehash的进度;iterators
记录正运行的迭代器数量,防止在迭代时执行rehash等操作。
2. 添加键值对
向哈希结构中添加键值对时,Redis会先计算键的哈希值,然后通过哈希函数将键映射到相应的桶上。接下来,Redis会遍历该桶上的链表或红黑树,查找是否已存在相同的key。如果存在,Redis会根据业务需要更新或覆盖已有的value。如果不存在,则会创建一个新的dictEntry,将key、value等信息存储在ziplist中,并将其插入到该桶的链表或红黑树中。
如果向哈希表中添加的键值对数量已经超过了其负载因子(load factor,即键值对数量与桶数量的比值)的阈值,那么Redis会自动执行rehash操作,将所有键值对移动到一个桶数为原有的2倍的新哈希表中。在新哈希表插入元素之前,Redis会先将新哈希表与旧哈希表的指针互换,这样新的哈希表成为了主哈希表,旧的哈希表成为了rehash时使用的哈希表。
3. 删除键值对
从哈希结构中删除键值对时,Redis也会先计算键的哈希值,并通过哈希函数将键映射到相应的桶上。接下来,Redis会遍历该桶上的链表或红黑树,查找对应的dictEntry,并将其从链表或红黑树中移除。如果在删除元素之后,某个桶上的键值对为0,那么Redis会自动地进行缩容操作,将桶数减半,并将所有元素移动到新的哈希表中。
4. 查找键值对
在哈希结构中查找键值对时,Redis首先需要计算键的哈希值,并将其映射到相应的桶上。接下来,Redis会遍历该桶上的链表或红黑树,查找与指定键匹配的dictEntry。如果找到了匹配的dictEntry,则返回相应的value;否则返回空值。
5. 示例说明
下面是通过Redis命令行客户端进行添加、删除、查询操作的一些示例:
# 添加键为name,值为redis的键值对
$ hmset myhash name redis
# 查询键为name的值
$ hget myhash name
"redis"
# 删除键为name的键值对
$ hdel myhash name
# 查询键为name的值,此时已被删除,返回空值
$ hget myhash name
(nil)
另一个示例说明,如果我们向一个包含100万个键值对的哈希表中添加一个新的键值对,那么Redis默认的负载因子为0.75,所以当键值对数量达到750,000个时,Redis会自动执行rehash操作。这样一来,我们在哈希表中查询、添加、删除元素时,性能会得到极大的提升。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Redis中哈希结构(Dict)的实现 - Python技术站