当前位置: 首页 > news >正文

【Redis】数据结构之dict

目录

  • dict的基本结构
  • dict的相关操作函数
    • 底层通用的之查找插入key-value对应该放入ht表的哪个槽
    • rehash过程

dict的基本结构

typedef struct dict {dictType *type;void *privdata;dictht ht[2];long rehashidx; /* rehashing not in progress if rehashidx == -1 */unsigned long iterators; /* number of iterators currently running */
} dict;

Redis中dict结构体包含了两个ditcht,这是为了rehash。

typedef struct dictType {uint64_t (*hashFunction)(const void *key);void *(*keyDup)(void *privdata, const void *key);void *(*valDup)(void *privdata, const void *obj);int (*keyCompare)(void *privdata, const void *key1, const void *key2);void (*keyDestructor)(void *privdata, void *key);void (*valDestructor)(void *privdata, void *obj);
} dictType;

提供了dictType,我认为这是用C语言实现的编译时多态,在创建dict时需要将dictType传入,不同的dictType可以提供不同的hashFunction、keyDup、keyCompare函数。

typedef struct dictht {dictEntry **table;unsigned long size;unsigned long sizemask;unsigned long used;
} dictht;

dicht的结构中有dictEntry **table这是一个指针数组,可以理解为是哈希表的array部分。

typedef struct dictEntry {void *key;union {void *val;uint64_t u64;int64_t s64;double d;} v;struct dictEntry *next;
} dictEntry;

这是dict中每个entry的结构,除了k和不同类型的v意外,还有struct dictEntry *next;体现了这是一个链哈希,即如果发生哈希冲突,就通过链表指针来组织起所有位于同一个哈希槽的entry。

dict的相关操作函数

底层通用的之查找插入key-value对应该放入ht表的哪个槽

/* Returns the index of a free slot that can be populated with* an hash entry for the given 'key'.* If the key already exists, -1 is returned. */
static int _dictKeyIndex(dict *ht, const void *key) {unsigned int h;dictEntry *he;/* Expand the hashtable if needed */if (_dictExpandIfNeeded(ht) == DICT_ERR)return -1;/* Compute the key hash value */h = dictHashKey(ht, key) & ht->sizemask;/* Search if this slot does not already contain the given key */he = ht->table[h];while(he) {if (dictCompareHashKeys(ht, key, he->key))return -1;he = he->next;}return h;
}
/* 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 (d->ht[0].size == 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 (d->ht[0].used >= d->ht[0].size &&(dict_can_resize ||d->ht[0].used/d->ht[0].size > dict_force_resize_ratio)){return dictExpand(d, d->ht[0].used*2);}return DICT_OK;
}

dictExpand函数中,有

    _dictInit(&n, ht->type, ht->privdata);n.size = realsize;n.sizemask = realsize-1;n.table = calloc(realsize,sizeof(dictEntry*));

所以,一个key应该插入到ht的哪个槽呢?就是_dictKeyIndex中的这一句

 h = dictHashKey(ht, key) & ht->sizemask;

可以保证h的值在[0,size-1]之间,而这些槽已经被初始化了:

n.table = calloc(realsize,sizeof(dictEntry*));

常规的dictAdd、dictDelete比较简单。

rehash过程

值得一提的是rehash过程。

int dictRehash(dict *d, int n) {int empty_visits = n*10; /* Max number of empty buckets to visit. */if (!dictIsRehashing(d)) return 0;while(n-- && d->ht[0].used != 0) {dictEntry *de, *nextde;/* Note that rehashidx can't overflow as we are sure there are more* elements because ht[0].used != 0 */assert(d->ht[0].size > (unsigned long)d->rehashidx);while(d->ht[0].table[d->rehashidx] == NULL) {d->rehashidx++;if (--empty_visits == 0) return 1;}de = d->ht[0].table[d->rehashidx];/* Move all the keys in this bucket from the old to the new hash HT */while(de) {uint64_t h;nextde = de->next;/* Get the index in the new hash table */h = dictHashKey(d, de->key) & d->ht[1].sizemask;de->next = d->ht[1].table[h];d->ht[1].table[h] = de;d->ht[0].used--;d->ht[1].used++;de = nextde;}d->ht[0].table[d->rehashidx] = NULL;d->rehashidx++;}/* Check if we already rehashed the whole table... */if (d->ht[0].used == 0) {zfree(d->ht[0].table);d->ht[0] = d->ht[1];_dictReset(&d->ht[1]);d->rehashidx = -1;return 0;}/* More to rehash... */return 1;
}

整体来看就是ht[0]的尺寸太小了,为了效率,需要把ht[0]的所有元素都搬运到扩展了尺寸的ht[1]中。
返回值为1说明rehash还没有完成。
返回值为0说明rehash已经完成。并且已经交换了ht[0]和ht[1],之后的命令写入可以往ht[0]里写了。

int dictRehashMilliseconds(dict *d, int ms) {long long start = timeInMilliseconds();int rehashes = 0;while(dictRehash(d,100)) {rehashes += 100;if (timeInMilliseconds()-start > ms) break;}return rehashes;
}
int incrementallyRehash(int dbid) {/* Keys dictionary */if (dictIsRehashing(server.db[dbid].dict)) {dictRehashMilliseconds(server.db[dbid].dict,1);return 1; /* already used our millisecond for this loop... */}/* Expires */if (dictIsRehashing(server.db[dbid].expires)) {dictRehashMilliseconds(server.db[dbid].expires,1);return 1; /* already used our millisecond for this loop... */}return 0;
}

实际上的rehash是在databasesCron函数里做的,incrementallyRehash指定了每次进行rehash的dict和时长(1 ms)。而dicthash()又设置了每次最多进行n个槽和n*10个空槽的遍历。

相关文章:

【Redis】数据结构之dict

目录 dict的基本结构dict的相关操作函数底层通用的之查找插入key-value对应该放入ht表的哪个槽rehash过程 dict的基本结构 typedef struct dict {dictType *type;void *privdata;dictht ht[2];long rehashidx; /* rehashing not in progress if rehashidx -1 */unsigned long…...

curl命令服务器上执行http请求

1. 现在本地使用postman生成curl命令 注意: 将ip改成127.0.0.1,端口是实际服务运行的端口 curl --location --request POST http://127.0.0.1:63040/content/course/list?pageNo1&pageSize2 \ --header Content-Type: application/json \ --data-raw {"courseName&q…...

图论03-【无权无向】-图的深度优先遍历-路径问题/检测环/二分图

文章目录 1. 代码仓库2. 单源路径2.1 思路2.2 主要代码 3. 所有点对路径3.1 思路3.2 主要代码 4. 路径问题的优化-提前结束递归4.1 思路4.2 主要代码 5. 检测环5.1 思路5.2 主要代码 5. 二分图5.1 思路5.2 主要代码5.2.1 遍历每个联通分量5.2.2 递归判断相邻两点的颜色是否一致…...

算法题java

一、四向链表&#xff0c;输入n生成一个多维4向链表 Datastatic class ListNode<T>{private T val;ListNode<T> up,down,left,right;public ListNode(T val){this.val val;}}public static void main(String[] args){ListNode<Integer> node getResult(8);…...

MySQL数据的基础语法

MySQL 是一种强大的关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;它使用 SQL&#xff08;Structured Query Language&#xff09;来管理和操作数据。以下是 MySQL 数据库的基础 SQL 语法&#xff0c;包括创建数据库、创建表、插入、查询、更新和删除数据等基…...

阿里面试(持续更新)

一面&#xff1a; 1 HashMap 实现原理&#xff0c;ConcurrentHashMap 实现原理 HashMap和ConcurrentHashMap都是存储键值对的数据结构&#xff0c;不同的是HashMap是线程不安全的&#xff0c;ConcurrentHashMap是线程安全的&#xff0c;HashMap在高并发情况下会出现数据不一致…...

龙芯3A3000源码编译安装deepin-ide

安装环境 系统为统信专业版1050 CPU为龙芯3A3000 安装步骤 1.安装所有依赖库 sudo apt-get install git debhelper cmake qt5-qmake qtbase5-dev qttools5-dev qttools5-dev-tools lxqt-build-tools libssl-dev llvm llvm-dev libclang-dev libutf8proc-dev libmicrohttpd-d…...

学成在线第二天-查询课程、查询课程分类、新增课程接口实现以及跨域的处理思路和全局异常处理的使用以及面试题

目录 一、接口的实现 二、跨域的处理思路 三、全局异常处理 四、面试题 五、总结 一、接口的实现 1. 查询课程接口 思路&#xff1a; 典型的分页查询 按需查询 模糊查询的查询 controller&#xff1a; ApiOperation(value "课程列表", notes "课程…...

【OpenCV概念】 11— 对象检测

一、说明 这都是关于物体识别的。物体识别是指通过计算机视觉技术&#xff0c;自动识别图像或视频中的物体及其属性和特征&#xff0c;是人工智能领域的一个分支。物体识别可应用于多个领域&#xff0c;包括工业自动化、智能家居、医疗、安防等。请随时阅读这篇文章&#xff1a…...

TensorRT学习笔记--常用卷积、激活、池化和FC层算子API

目录 1--Tensor算子API 1-1--卷积算子 1-2--激活算子 1-3--池化算子 1-4--FC层算子 2--代码实例 3--编译运行 1--Tensor算子API TensorRT提供了卷积层、激活函数和池化层三种最常用算子的API&#xff1a; // 创建一个空的网络 nvinfer1::INetworkDefinition* network …...

【Edabit 算法 ★☆☆☆☆☆】 Less Than 100?

【Edabit 算法 ★☆☆☆☆☆】 Less Than 100? language_fundamentals math validation Instructions Given two numbers, return true if the sum of both numbers is less than 100. Otherwise return false. Examples lessThan100(22, 15) // true // 22 15 37lessTha…...

C++中的智能指针:更安全、更便利的内存管理

在C++编程中,动态内存管理一直是一个重要且具有挑战性的任务。传统的C++中,程序员需要手动分配和释放内存,这往往会导致内存泄漏和悬挂指针等严重问题。为了解决这些问题,C++11引入了智能指针(Smart Pointers)这一概念,它们是一种高级的内存管理工具,可以自动管理内存的…...

google登录k8s dashboard ui显示“您的连接不是私密连接”问题解决梳理

1.问题描述 OS Version:CentOS Linux release 7.9.2009 (Core) K8S Version:Kubernetes v1.20.4 k8s dashboard ui安装完毕后&#xff0c;通过google浏览器登录返现https网页&#xff0c;发现非官方的https网页无法打开 网址&#xff1a;https://192.168.10.236:31001 2.原…...

MIPS指令集摘要

目录 MIPS指令R I J三种格式 MIPS五种寻址方式 立即数寻址 寄存器寻址 基址寻址 PC相对寻址 伪直接寻址 WinMIPS64汇编指令 助记 从内存中加载数据 lb lbu lh lhu lw lwu ld l.d lui 存储数据到内存 sb sh sw sd s.d 算术运算 daddi daddui dadd…...

数据可视化素材分享 | 数十图表、无数模板

很多人在后台求分享报表、源代码&#xff0c;其实何必这么麻烦&#xff0c;在奥威BI数据可视化平台上点击即可获得大量的可视化素材&#xff0c;如数十种可视化图表&#xff0c;适用于不同分析场景&#xff1b;又如大量不同主题的BI数据可视化报表模板&#xff0c;套用后替换数…...

Hadoop3教程(三十二):(生产调优篇)NameNode故障恢复与集群的安全模式

文章目录 &#xff08;159&#xff09;NameNode故障处理&#xff08;160&#xff09;集群安全模式&磁盘修复集群安全模式磁盘修复等待安全模式 参考文献 &#xff08;159&#xff09;NameNode故障处理 如果NameNode进程挂了并且存储的数据也丢失了&#xff0c;如何恢复Nam…...

uniapp下载附件保存到手机(文件、图片)ios兼容

downloadFile(file)&#xff0c;其中file为下载的文件地址uni.downloadFile图片使用uni.saveImageToPhotosAlbum【安卓、ios都合适】文件使用uni.openDocument【安卓图片也可以用这个&#xff0c;ios会失败】 // 下载文件 export function downloadFile(file) {let acceptArr …...

【Edabit 算法 ★☆☆☆☆☆】 Basketball Points

【Edabit 算法 ★☆☆☆☆☆】 Basketball Points language_fundamentals math numbers Instructions You are counting points for a basketball game, given the amount of 2-pointers scored and 3-pointers scored, find the final points for the team and return that …...

Web攻防04_MySQL注入_盲注

文章目录 MYSQL-SQL操作-增删改查盲注概念盲注分类盲注语句参考&更多盲注语句/函数 注入条件-数据回显&错误处理PHP开发项目-注入相关条件&#xff1a;基于延时&#xff1a;基于布尔&#xff1a;基于报错&#xff1a; CMS案例-插入报错&删除延时-PHP&MYSQL1、x…...

Flask自定义装饰和g的使用

1. 在commons.py文件中新增一个装饰器类: 注&#xff1a;一定要加入wraps进行装饰否则&#xff0c;装饰器在给多个函数进行装饰时会报错 from functools import wraps from flask import session, current_app, g# 定义登陆装饰器&#xff0c;封装用户的登陆数据 def user_log…...

基于服务器使用 apt 安装、配置 Nginx

&#x1f9fe; 一、查看可安装的 Nginx 版本 首先&#xff0c;你可以运行以下命令查看可用版本&#xff1a; apt-cache madison nginx-core输出示例&#xff1a; nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业

6月9日&#xff0c;国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解&#xff0c;“超级…...

如何为服务器生成TLS证书

TLS&#xff08;Transport Layer Security&#xff09;证书是确保网络通信安全的重要手段&#xff0c;它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书&#xff0c;可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。

1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj&#xff0c;再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题

【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要&#xff1a; 近期&#xff0c;在使用较新版本的OpenSSH客户端连接老旧SSH服务器时&#xff0c;会遇到 "no matching key exchange method found"​, "n…...

Linux 中如何提取压缩文件 ?

Linux 是一种流行的开源操作系统&#xff0c;它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间&#xff0c;使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的&#xff0c;要在 …...

深入理解Optional:处理空指针异常

1. 使用Optional处理可能为空的集合 在Java开发中&#xff0c;集合判空是一个常见但容易出错的场景。传统方式虽然可行&#xff0c;但存在一些潜在问题&#xff1a; // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...

comfyui 工作流中 图生视频 如何增加视频的长度到5秒

comfyUI 工作流怎么可以生成更长的视频。除了硬件显存要求之外还有别的方法吗&#xff1f; 在ComfyUI中实现图生视频并延长到5秒&#xff0c;需要结合多个扩展和技巧。以下是完整解决方案&#xff1a; 核心工作流配置&#xff08;24fps下5秒120帧&#xff09; #mermaid-svg-yP…...