当前位置:网站首页>[Redis]-[Redis底层数据结构]-字典
[Redis]-[Redis底层数据结构]-字典
2022-06-21 07:48:00 【Pacifica_】
前言
字典,又称为符号表,关联数组或映射,是一种用于保存键值对的数据结构
字典的实现
Redis 的字典使用 哈希表 作为底层实现
哈希表
typedef struct dictht{
dictEntry** table; //哈希表数组
unsigned long size; //哈希表大小
unsigned long sizemask; //哈希表大小掩码,总是等于 size - 1
unsigned long used; //该哈希表已有节点的数量,即所保存键值对的数目
}dictht;
table 属性是一个数组,其中每个元素都是一个指向 dictEntry 结构的指针,每一个 dictEntry 结构保存着一个键值对
哈希表节点
typedef struct dictEntry{
void *key; //键
union{
void *val;
uint64_tu64;
int64_ts64;
}v; //值
struct dictEntry *next; //指向下一个哈希表节点,形成链表
}dictEntry;
可以看出,Redis 中的哈希表使用 链地址法 解决哈希冲突的问题。而且由于没有保存指向链表表尾的指针,为了速度考虑,程序使用的是 头插入法,复杂度为 O(1)
字典
typedef struct dict{
dictType *type; //类型特定函数
void *privdata; //私有数据
dictht ht[2]; //哈希表
int rehashidx; //rehash 索引,当rehash过程不在进行时,值为-1
}dict;
type 属性和 privdata 属性是针对不同类型的键值对,为创建多态字典而设置。
其中,type 属性是一个指向 dictType 结构的指针,每个 dictType 结构保存了一簇用于操作特定类型键值对的函数,Redis 就会为用途不同的字典设置不同的函数
ht 属性是一个包含两个元素的数组,每个项都是一个哈希表,一般使用的是 ht[0] 哈希表,ht[1] 只有在 rehash 时会使用
哈希算法
当要添加一个新的键值对到字典里时:
- 根据键值对的键计算出哈希值,使用字典中的哈希函数进行计算
hash = dict->type->hashFunction(key) - 根据哈希值以及哈希表的大小掩码,进行相与运算,计算出索引值,
index = hash & dict->ht[x].sizemask - 再将键值对放到哈希表数组的指定索引上面
当字典被用作数据库的底层实现,或者哈希键的底层实现时,Redis 使用 MurmurHash2 算法来计算键的哈希值
rehash
为了使哈希表的负载因子维持在一个合理的范围之内,当哈希表保存的键值对太多或者太少时,程序需要对哈希表的大小进行相应的扩展或者收缩,通过执行 rehash 操作来完成:
- 为字典的 ht[1] 哈希表分配空间,哈希表的空间大小取决于要执行的操作以及当前 ht[0] 包含的键值对数量
>>> 如果执行的是 扩展 操作,那么 ht[1] 的大小为 第一个大于等于 ht[0].used * 2 的 2 的 n 次方幂
>>> 如果执行的是 收缩 操作,那么 ht[1] 的大小为 第一个大于等于 ht[0].used 的 2 的 n 次方幂 - 将保存在 ht[0] 中的所有键值对 rehash 到 ht[1] 上面。rehash 即重新计算键的哈希值以及索引值,然后根据新的索引值放置到 ht[1] 哈希表的相应位置上
- 当 ht[0] 包含的所有键值对都迁移到了 ht[1] 后,释放 ht[0],将 ht[1] 设置为 ht[0],并在 ht[1] 新创建一个空白哈希表,为下一次 rehash 做准备
扩展或收缩的时机
当 服务器目前没有在执行 BGSAVE 命令或者 BGREWRITEAOF 命令,并且哈希表的负载因子大于等于 1,或者 服务器正在执行 BGSAVE 命令或者 BGREWRITEAOF 命令,并且哈希表的负载因子大于等于 5,程序就会自动对哈希表执行扩展操作。其中负载因子为 load_factor = ht[0].used / ht[0].size
为何根据 BGSAVE 命令或者 BGREWRITEAOF 命令是否正在执行,所选择的扩展所需负载因子不同?
这是因为在执行 BGSAVE 命令或者 BGREWRITEAOF 命令的过程中,Redis 需要创建当前服务器进程的子进程,而大多数操作系统在创建使用子进程时都会采用写时复制的技术,此时如果对哈希表进行了扩展操作,则会触发子进程对父进程的内存拷贝操作。所以在子进程存在的时候,服务器会提高执行扩展操作所需的负载因子,从而尽可能避免在子进程存在期间进行扩展操作,避免不必要的内存写入操作,最大限度地节约内存
当哈希表的负载因子小于 0.1 时,程序自动开始对哈希表执行收缩操作
渐进式 rehash
rehash 的过程并不是一次性,集中式地完成的,而是分多次,渐进式地完成的
原因是,如果一次性完成,对于 ht[0] 中保存键值对较少的情况下还可以接受,因为一次性 rehash 也不会耗费太多时间;而如果 ht[0] 中保存很多键值对,几千万个,那么要一次性将这些键值对全部 rehash 到 ht[1] 中的话,庞大的计算量以及工作量可能会导致服务器在一段时间内停止服务
渐进式 rehash的步骤为:
- 为 ht[1] 分配空间,让字典同时持有 ht[0] 和 ht[1]
- 将字典中的索引计数器变量 rehashidx 设置为 0,表示 rehash 工作正式开始
- 在 rehash 进行期间,在每次对字典执行添加,删除,查找或者更新操作时,程序除了会执行指定的操作外,还会顺带将 ht[0] 哈希表在 rehashidx 索引上的所有键值对 rehash 到 ht[1] ,当 rehash 完成之后,程序将 rehashidx 的属性的值增一,准备 rehash 下一索引位置上的键值对。这里执行的查找 (删除,更新时也会涉及到查找) 操作会在两个哈希表上进行,先在 ht[0] 中进行查找,如果没找到,就继续到 ht[1] 中进行查找;添加操作一律对 ht[1] 进行,保证 ht[0] 包含的键值对数量只会减少而不会增加,最终必定能变为空表
- 随着字典操作的不断执行,最终在某个时间点上,ht[0] 的所有键值对都会被 rehash 到 ht[1] 中,这时程序就会将 rehashidx 的值设为 -1,表示整个 rehash 操作已经完成
边栏推荐
- mysql的安装路径如何查看
- Getting started with MATLAB
- How to write the statement of executing stored procedure in MySQL
- 图解 Google V8 # 14:字节码(二):解释器是如何解释执行字节码的?
- mysql中执行存储过程的语句怎么写
- . Net 4.5 asynchronous programming pilot (async and await)
- Wanzi detailed data warehouse, data lake, data middle platform and lake warehouse are integrated
- China uncoated intermittent catheter market trend report, technical innovation and market forecast
- 华三IPsec
- mysql数据库拉链表是什么
猜你喜欢

mysql中执行存储过程的语句怎么写

2021-06-16 STM32F103 exti interrupt identification using firmware library

动态规划解决打家劫舍问题

古风排版 (20 分)(测试点4)

JVM memory model concepts

mysql分页查询如何优化

微信公众号对接 : 一键推送文章信息至公众号

Firefox users are down, Mozilla foundation is at a crossroads

Rdkit | molecular similarity based on molecular fingerprint

dried food! Neuron competitive initialization strategy based on information bottleneck theory
随机推荐
Market trend report, technical innovation and market forecast of scaffold free 3D cell culture plate in China
Type de contrôle qml: Drawer
. Net 4.5 asynchronous programming pilot (async and await)
How to optimize MySQL paging query
2021-06-16 STM32F103 exti interrupt identification using firmware library
Use js to switch between clickable and non clickable page buttons
RDKit | 合成可行性分数--SA SCORE
mysql数据库拉链表是什么
2021 - 06 - 16 stm32f103 exti interruption identification using firmware Library
CUDA or FPGA for special purpose 3D graphics computations? [closed]
Dynamic addition of prompt information for successful operation
Solve the problem that Jenkins cannot save the configuration after upgrading
学习太极创客 — ESP8226 (九)JSON 数据通讯 三
Software download method
升级Jenkins步骤和遇到的问题
32单片机——pwm波输出
One year experience interview byte Tiktok e-commerce, share the following experience!
动态规划解决打家劫舍问题
Rdkit | compound library based on murcko skeleton clustering
应用程序卡死,如何快速退出?