redis 从0到1完整学习 (六):Hash 表数据结构

文章目录

  • 1. 引言
  • 2. redis 源码下载
  • 3. dict 数据结构
  • 4. 哈希表扩容与 rehash
  • 5. 参考


1. 引言

前情提要:
《redis 从0到1完整学习 (一):安装&初识 redis》
《redis 从0到1完整学习 (二):redis 常用命令》
《redis 从0到1完整学习 (三):redis 数据结构》
《redis 从0到1完整学习 (四):字符串 SDS 数据结构》
《redis 从0到1完整学习 (五):集合 IntSet 数据结构》
本文主要结合源码来介绍 hash 表的数据结构

2. redis 源码下载

Redis 源码可以点击这里下载,方便查看其中定义的一些数据结构。
在这里插入图片描述

3. dict 数据结构

Dict 由三部分组成,分别是:哈希表(DictHashTable)、哈希节点(DictEntry)、字典(Dict),数据结构如下:
在这里插入图片描述
dict、dictht、dictEntry 三者的数据结构关系如下:
在这里插入图片描述

当 Dict 添加键值对时,首先由 key 计算出 hash 值 h,通过 h & sizemask (等同取模计算,提升计算速度) 计算元素对应数组中的索引位置。假设哈希值 h = 5,则 5&7=5,因此键值对存储到数组索引为5的位置。

如下图:第一次插入到下标为5的数组中。
在这里插入图片描述

如果第二次插入的 hash 值计算后的下标也是5,则第二次插入到链表的头部:
在这里插入图片描述

4. 哈希表扩容与 rehash

当哈希表中元素越来越多导致哈希冲突增多时,链表过长后,会查询效率降低,由查询的时间复杂度最开始的 O(1) 向 O(n) 移动。

这部分源码在 Redis 的好几个版本都有所变化,主要是看看扩容的条件:
(1)Redis 6.0
这里是直接判断 ht->used == ht->size

/* Expand the hash table if needed */
static int _dictExpandIfNeeded(dict *ht) {/* If the hash table is empty expand it to the initial size,* if the table is "full" dobule its size. */if (ht->size == 0)return dictExpand(ht, DICT_HT_INITIAL_SIZE);if (ht->used == ht->size)return dictExpand(ht, ht->size*2);return DICT_OK;
}

(2)Redis 6.2
引入哈希表的负载因子:LoadFactor = used/size。在每次新增键值对时都会检查负载因子。

/* Expand the hash table if needed */
static int _dictExpandIfNeeded(dict *d)
{// 已经在 rehash, 则返回if (dictIsRehashing(d)) return DICT_OK;// 如果为空,则初始化 size 为4if (d->ht[0].size == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE);// 如果负载因子 > dict_force_resize_ratio(定义为5),则扩容if (d->ht[0].used >= d->ht[0].size &&(dict_can_resize ||d->ht[0].used/d->ht[0].size > dict_force_resize_ratio) &&dictTypeExpandAllowed(d)){return dictExpand(d, d->ht[0].used + 1);}return DICT_OK;
}

(3)Redis 7.2

/* Expand the hash table if needed */
static int _dictExpandIfNeeded(dict *d)
{/* Incremental rehashing already in progress. Return. */if (dictIsRehashing(d)) return DICT_OK;/* If the hash table is empty expand it to the initial size. */if (DICTHT_SIZE(d->ht_size_exp[0]) == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE);/* If we reached the 1:1 ratio, and we are allowed to resize the hash* table (global setting) or we should avoid it but the ratio between* elements/buckets is over the "safe" threshold, we resize doubling* the number of buckets. */if (!dictTypeExpandAllowed(d))return DICT_OK;if ((dict_can_resize == DICT_RESIZE_ENABLE &&d->ht_used[0] >= DICTHT_SIZE(d->ht_size_exp[0])) ||(dict_can_resize != DICT_RESIZE_FORBID &&d->ht_used[0] / DICTHT_SIZE(d->ht_size_exp[0]) > dict_force_resize_ratio)){return dictExpand(d, d->ht_used[0] + 1);}return DICT_OK;
}

下面以 Redis 6.2 源码介绍下扩容的核心方法_dictExpand

/* Expand or create the hash table,* when malloc_failed is non-NULL, it'll avoid panic if malloc fails (in which case it'll be set to 1).* Returns DICT_OK if expand was performed, and DICT_ERR if skipped. */
int _dictExpand(dict *d, unsigned long size, int* malloc_failed)
{if (malloc_failed) *malloc_failed = 0;// 如果当前 size 大于要申请的 size,或者正在 rehash,则报错if (dictIsRehashing(d) || d->ht[0].used > size)return DICT_ERR;dictht n; /* the new hash table */// 初始化第一个大于等于 size 的 2^n 数,这个数赋值为 realsize,但是不会低于4unsigned long realsize = _dictNextPower(size);...// 重置 hash 表的大小和掩码,并且分配新内存n.size = realsize;n.sizemask = realsize-1;if (malloc_failed) {n.table = ztrycalloc(realsize*sizeof(dictEntry*));*malloc_failed = n.table == NULL;if (*malloc_failed)return DICT_ERR;} elsen.table = zcalloc(realsize*sizeof(dictEntry*));n.used = 0;// 第一次初始化,则直接返回if (d->ht[0].table == NULL) {d->ht[0] = n;return DICT_OK;}// 如果不是第一次初始化,说明是扩容,需要 rehash,将 rehashidx 置为0,在后续增删改,会触发 rehashd->ht[1] = n;d->rehashidx = 0;return DICT_OK;
}

上面代码的最后只是说明了要进行 rehash 操作,在 rehash 过程中:

  • 每次增、删、改、查,都会把 dict.ht[0].table[rehashidx] 的值 rehash 到 dict.ht[1] 中,同时 rehashidx++,这样渐进式地 rehash,防止 rehash 阻塞主进程太久,影响效率。
  • 新增操作,直接写入ht[1]
  • 删、改、查会在 dict.ht[0],dict.ht[1] 依次查找。

5. 参考

《redis 从0到1完整学习 (一):安装&初识 redis》
《redis 从0到1完整学习 (二):redis 常用命令》
《redis 从0到1完整学习 (三):redis 数据结构》
《redis 从0到1完整学习 (四):字符串 SDS 数据结构》
《redis 从0到1完整学习 (五):集合 IntSet 数据结构》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/239730.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Linux笔记】用户和权限管理基本命令介绍

🍎个人博客:个人主页 🏆个人专栏:Linux学习 ⛳️ 功不唐捐,玉汝于成 目录 ​编辑 前言: 命令: whoami: passwd: useradd: userdel: chm…

37.常用shell之 nano / vi / vim - 文本编辑器 的用法及衍生用法

nano, vi, 和 vim 是在类 Unix 系统中常用的文本编辑器,每个都有其特定的用法和功能集。 nano nano 是一个简单易用的文本编辑器,适合初学者和那些需要轻量级编辑器的用户。 基本用法: 打开或创建文件:nano filename这会打开 fi…

css 实现满屏升空的气球动画

最终实现效果 demo放在最后了。。。。 问题一 怎么实现满屏气球?简单理解就是多个气球的合并,难道要写多个盒子吗?确实是这样子,但可以有更好的办法,其实就是通过原生操作多个盒子生成,所以只需要实现一个…

智能优化算法应用:基于人工大猩猩部队算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用:基于人工大猩猩部队算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用:基于人工大猩猩部队算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.人工大猩猩部队算法4.实验参…

YZ系列工具之YZ03:高版本Excel的自定义菜单

我给VBA下的定义:VBA是个人小型自动化处理的有效工具。利用好了,可以大大提高自己的工作效率,而且可以提高数据的准确度。我的教程一共九套一部VBA手册,教程分为初级、中级、高级三大部分。是对VBA的系统讲解,从简单的…

关于游戏性能优化的技巧

关于游戏性能优化的技巧 游戏性能优化对象池Jobs、Burst、多线程间隔处理定时更新全局广播缓存组件缓存常用数据2D残影优化2D骨骼转GPU动画定时器优化DrawCall合批处理优化碰撞层优化粒子特效 游戏性能优化 好久没有在CSDN上面写文章了,今天突然看到鬼谷工作室技术…

docker部署mysql主主备份 haproxy代理(swarm)

docker部署mysql主主备份 haproxy代理(swarm) docker部署mysql主主备份 docker部署mysql主主备份(keepalived)跨主机自动切换 docker部署mysql主主备份 haproxy代理(swarm) 1. 环境准备 主机IPnode119…

QT调用外部exe及无终端弹窗的解决方案、并实现进程输出信息获取

博主使用QT调用外部exe程序&#xff0c;外部exe程序有printf输出&#xff0c;起初使用的是C语言中的system()方法&#xff0c;但在笔记本上有概率出现终端窗口一闪而过的情况&#xff0c;后修改了调用方案。 1. QT调用外部exe 使用QT中的QProcess方法 #include <QProcess…

pytest常用命令行参数

文章目录 一、前置说明二、操作步骤1. 命令行中执行:pytest2. 命令行中执行:pytest - v3. 命令行中执行:pytest -s4. 命令行中执行:pytest -k test_addition5. 命令行中执行:pytest -k test_pytest_command_params.py6. 命令行中执行:pytest -v -s -k test_pytest_comman…

WSL移动ubuntu到其他盘的几个问题以及安装,使用过程中遇到bug记录

这里写目录标题 无法正常修改Ubuntu系统的默认用户解决方案1&#xff1a;解决方案2&#xff1a; 出现 id xxx no such userGUI不能正常显示 无法正常修改Ubuntu系统的默认用户 ubuntu移动到其他盘可以参考WSL Ubuntu子系统迁移到非系统盘 下面问题是我安装时遇到的&#xff0c…

with torch.no_grad()在Pytorch中的应用

with torch.no_grad()在Pytorch中的应用 参考&#xff1a; https://blog.csdn.net/qq_24761287/article/details/129773333 https://blog.csdn.net/sazass/article/details/116668755 在学习Pytorch时&#xff0c;老遇到 with torch.no_grad()&#xff0c;搞不清其作用&#…

基本ACL配置

ACL&#xff08;Access Control List&#xff09;是一种网络安全技术&#xff0c;用于控制网络设备上的数据流入流出。ACL可以根据预设的规则限制哪些流量允许通过&#xff0c;哪些流量必须被阻止。 下面是基本的ACL配置示例&#xff1a; 先定义ACL规则&#xff1a; access-…

caffe模型的python前向测试

当我们训练好一个网络模型后必不可少的就是对模型跑前向&#xff0c;看模型的实际性能如何。python绝对是最简单的环境&#xff0c;所以本文写一个python版本的前向测试。 import os import cv2 import sys import caffe import glob import argparse from PIL import Image im…

StringBuilder和StringBuffer区别是什么?

想象一下&#xff0c;你在写信&#xff0c;但是你需要不断地添加新的内容或者修改一些词句。在编程中&#xff0c;当你需要这样操作字符串时&#xff0c;就可以用StringBuffer或StringBuilder。 StringBuffer StringBuffer就像是一个多人协作写作的工具。如果你和你的朋友们一…

Linux内核模块文件组成介绍

Linux驱动开发主要的工作就是编写模块&#xff0c;一个典型的Linux内核模块文件.ko 主要由以下几个部分组成。 模块加载函数(必须) 当通过insmod或modprobe命令加载内核模块时&#xff0c;模块的加载函数会自动被内核执行&#xff0c;完成本模块的相关初始化工作。 Linux内核模…

Spark Machine Learning进行数据挖掘的简单应用(兴趣预测问题)

数据挖掘的过程 数据挖掘任务主要分为以下六个步骤&#xff1a; 1.数据预处理2.特征转换3.特征选择4.训练模型5.模型预测6.评估预测结果 数据准备 这里准备了20条关于不同地区、不同性别、不同身高、体重…的人的兴趣数据集&#xff08;命名为hobby.csv)&#xff1a; id,h…

MyBatis关联查询(二、一对多查询)

MyBatis关联查询&#xff08;二、一对多查询&#xff09; 需求&#xff1a;查询所有用户信息及用户关联的账户信息。 分析&#xff1a;用户信息和他的账户信息为一对多关系&#xff0c;并且查询过程中如果用户没有账户信息&#xff0c;此时也要将用户信息查询出来&#xff0c…

竞赛保研 基于GRU的 电影评论情感分析 - python 深度学习 情感分类

文章目录 1 前言1.1 项目介绍 2 情感分类介绍3 数据集4 实现4.1 数据预处理4.2 构建网络4.3 训练模型4.4 模型评估4.5 模型预测 5 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于GRU的 电影评论情感分析 该项目较为新颖&#xff0c;适合作为竞…

msyql 24day 数据库主从 主从复制 读写分离 master slave 有数据如何增加

目录 环境介绍读写分离纵向扩展横向扩展 数据库主从准备环境主库环境(master)从库配置(slave)状态分析重新配置问题分析 报错解决从库验证 有数据的情况下 去做主从清理环境环境准备数据库中的锁的机制主库配置从库配置最后给主库解锁常见错误 环境介绍 将一个数据库的数据 复…

服务器raid中磁盘损坏或下线造成阵列降级更换新硬盘重建方法

可能引起磁盘阵列硬盘下线或故障的情况&#xff1a; 硬件故障&#xff1a; 硬盘物理损坏&#xff1a;包括但不限于坏道、电路板故障、磁头损坏、盘片划伤、电机故障等。连接问题&#xff1a;如接口损坏、数据线或电源线故障、SATA/SAS控制器问题等。热插拔错误&#xff1a;在不…