目录

一、从传统查找的痛点到哈希表的优势​

二、哈希表的核心结构:文件柜的构成​

2.1、 dictht 结构体:文件柜本体​

2.2、dictEntry 结构体:带链条的文件夹​

2.2.1、 哈希冲突的解决:抽屉里的链条​

2.3、字典的高层封装:文件柜管理系统​

2.4、dictType:文件处理工具包​

三、哈希计算:如何确定文件位置​

四、动态扩容与收缩:文件柜的大小调整​

4.1、何时需要调整大小?​

4.2、调整大小的规则​

4.3、渐进式 rehash:边营业边搬家​

4.4、写时复制优化:拍照时不整理房间​

五、字典与 SDS 的完美配合​

六、字典设计的核心智慧​

如果说 SDS 是 Redis 的 "智能笔记本",链表是 "活页文件夹",那么字典就是把这些笔记和文件夹有序管理起来的 "智能文件柜系统"。当你在 Redis 中使用哈希类型存储用户信息(如HSET user:1 name "张三" age 30)时,背后正是字典结构在高效运作。​

一、从传统查找的痛点到哈希表的优势​

想象你在图书馆找书的三种方式:​

  • 按书架顺序查找:像数组遍历一样,逐个查看每个书架,效率极低​
  • 按分类目录查找:像链表遍历一样,虽然有分类但仍需逐个翻阅​
  • 按编号定位查找:像哈希表一样,通过书号直接计算出所在书架和位置,一步到位​

Redis 的字典采用哈希表作为底层实现,正是为了实现这种 "一步到位" 的高效查找能力。哈希表就像配备了智能索引系统的文件柜,能在 O (1) 时间复杂度内完成数据的查找、添加和删除操作。​

二、哈希表的核心结构:文件柜的构成​

Redis 的哈希表结构由三个关键部分组成,就像文件柜的不同组件:​

2.1、 dictht 结构体:文件柜本体​

struct dictht {​dictEntry **table; // 抽屉数组(哈希表节点数组)​unsigned long size; // 抽屉总数(哈希表大小)​unsigned long sizemask; // 定位掩码(总是等于size-1)​unsigned long used; // 已使用抽屉数(节点数量)​};​

这个结构体就像一个文件柜:​

  • table是一排抽屉,每个抽屉可以放多个文件夹(通过链表连接)​
  • size记录总共有多少个抽屉​
  • sizemask是定位工具,用来计算文件应该放入哪个抽屉​
  • used记录已经放了多少文件​

2.2、dictEntry 结构体:带链条的文件夹​

struct dictEntry {​void *key; // 文件标签(键)​union { // 文件内容(值)​void *val;​uint64_t u64;​int64_t s64;​} v;​struct dictEntry *next; // 下一个文件夹(解决冲突的链表)​};​

每个节点就像一个带链条的文件夹:​

  • key是文件的标签,在 Redis 中通常用 SDS 存储(比如 "name"、"age")​
  • v是文件内容,可以是字符串指针(void *val)、无符号整数(u64)或有符号整数(s64)​
  • next是连接链条,当多个文件需要放入同一个抽屉时,用链条将它们串起来(哈希冲突解决)​

2.2.1、 哈希冲突的解决:抽屉里的链条​

当两个不同的键计算出相同的抽屉编号时(哈希冲突),就像两个文件要放进同一个抽屉,这时next指针就发挥作用了 —— 它把这些文件串成一个链表,这种方法称为 "链地址法"。​

比如 "张三" 和 "张三丰" 计算出相同的抽屉编号,它们会通过next指针连接在一起,查找时只需遍历这个小链表即可。​

2.3、字典的高层封装:文件柜管理系统​

Redis 的字典结构在哈希表之上增加了管理层,就像文件柜的智能管理系统:​

struct dict {​dictType *type; // 操作工具集​void *privdata; // 工具参数​dictht ht[2]; // 两个哈希表(主表和备用表)​int rehashidx; // 搬家进度(-1表示未进行)​};​

这个管理系统的核心功能:​

  • 维护两个哈希表ht[0](日常使用)和ht[1](搬家时使用)​
  • 通过rehashidx记录数据迁移进度​
  • 提供type中定义的工具函数处理不同类型的数据​

2.4、dictType:文件处理工具包​

struct dictType {​unsigned int (*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); // 销毁标签的工具​};​

这些函数就像文件柜的配套工具:​

  • hashFunction:根据文件名计算抽屉编号(哈希函数)​
  • keyDup:复制文件标签(比如复制 SDS 字符串)​
  • keyCompare:比较两个文件标签是否相同​
  • 其他工具负责数据的复制和销毁​

三、哈希计算:如何确定文件位置​

把文件放入文件柜需要两步:计算哈希值(文件名编码)和计算索引(抽屉编号):​

  1. 计算哈希值:用hashFunction对键(key)进行计算,比如对 SDS 字符串 "name" 计算得到一个大整数​
  1. 计算索引:用哈希值与sizemask(size-1)做与运算,得到抽屉编号:​

​索引 = 哈希值 & sizemask​

例如:当size=8(sizemask=7),哈希值 = 10(二进制 1010)时:​

1010 & 0111 = 0010(二进制)→ 索引=2​

这种计算方式要求size必须是 2 的幂次方,这样sizemask的二进制表示全是 1,能保证索引均匀分布在0到size-1之间。​

四、动态扩容与收缩:文件柜的大小调整​

随着文件增减,文件柜需要动态调整大小以保持效率,这个过程称为 "rehash"(重新散列):​

4.1、何时需要调整大小?​

  • 扩展条件:当文件太多导致拥挤时​
  • 无备份任务时,负载因子≥1(used/size ≥1)​
  • 有备份任务时,负载因子≥5(避免频繁复制内存)​
  • 收缩条件:当文件太少导致空间浪费时​
  • 负载因子≤0.1(used/size ≤0.1)​

负载因子就像 "拥挤度":比如 8 个抽屉放了 10 个文件,拥挤度 = 10/8=1.25。​

4.2、调整大小的规则​

  • 扩展:ht[1]的大小是第一个大于等于ht[0].used*2的 2 的幂次方​
    • 例:used=10 → 10*2=20 → 下一个 2 的幂次方是 32​
  • 收缩:ht[1]的大小是第一个大于等于ht[0].used的 2 的幂次方​
    • 例:used=10 → 下一个 2 的幂次方是 16​

4.3、渐进式 rehash:边营业边搬家​

如果文件柜需要从 8 个抽屉扩到 32 个抽屉,不可能一次性搬完所有文件 —— 这会导致图书馆暂时停业。Redis 采用 "渐进式 rehash" 策略,就像边营业边搬家:​

  1. 准备阶段:为ht[1]分配新空间,rehashidx=0(开始搬家)​
  1. 渐进迁移:每次有读写操作时,顺带迁移ht[0]中rehashidx索引处的所有文件到ht[1],然后rehashidx加 1​
  1. 双表并行:迁移期间,查找操作会同时检查ht[0]和ht[1];新增操作直接放入ht[1],保证ht[0]只减不增​
  1. 完成迁移:当ht[0]所有文件都迁移到ht[1]后,ht[1]成为新的ht[0],rehashidx=-1(搬家完成)​

这种策略把巨大的迁移工作分散到每次操作中,避免了服务中断。​

4.4、写时复制优化:拍照时不整理房间​

当 Redis 执行 BGSAVE(备份)或 BGREWRITEAOF(日志重写)时,会创建子进程。为了减少内存复制开销,Redis 采用 "写时复制" 技术:​

  • 子进程创建时共享父进程内存,只有当数据修改时才复制相关内存页​
  • 因此在备份期间,Redis 提高扩展阈值(负载因子≥5 才扩展),减少内存写入操作​
  • 这就像拍照时不整理房间,等照片拍完再整理,避免重复劳动​

五、字典与 SDS 的完美配合​

SDS 在字典中扮演重要角色:​

  • 键(key)的存储:字典的键几乎都是用 SDS 存储的,比如HSET user:1 name "张三"中的 "name"​

​key → SDS结构:len=4, free=4, buf=['n','a','m','e','\0']​

  • 字符串值的存储:当值是字符串时,也用 SDS 存储,比如 "张三"​

v.val → SDS结构:len=6, free=6, buf=['张','三','\0']​

  • 数值的优化存储:当值是整数时,直接用u64或s64存储,避免 SDS 的内存开销​

这种组合让字典既能高效存储字符串,又能优化数值类型的存储。​

六、字典设计的核心智慧​

特性​

生活比喻​

技术价值​

哈希表结构​

智能文件柜​

O (1) 时间复杂度的基本操作​

链地址法​

抽屉里的链条​

优雅解决哈希冲突​

双哈希表​

新旧文件柜​

支持动态扩容 / 收缩​

渐进式 rehash​

边营业边搬家​

避免操作阻塞​

类型特定函数​

专用工具包​

支持多类型数据处理​

写时复制适配​

拍照时不整理​

优化备份时的性能​

Redis 字典通过这些设计,完美平衡了性能、灵活性和内存效率。它不仅是哈希类型的底层实现,还被用于 Redis 数据库的核心(键空间)、集群节点通信等关键场景,是 Redis 高效运作的基石之一。​

理解了字典结构,你就掌握了 Redis 数据管理的核心机制 —— 从简单的键值对到复杂的哈希对象,都依赖这个精妙的 "智能文件柜系统" 来实现高效管理。

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

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

相关文章

FAST API部署和使用

第一部分:FastAPI 的使用(开发环境) 1. 安装 首先,你需要安装 FastAPI 和一个 ASGI 服务器,最常用的是 Uvicorn。 pip install "fastapi[standard]"这个命令会安装 FastAPI 以及所有推荐的依赖,包…

【JavaWeb】之HTML(对HTML细节的一些总结)

大家天天开心! 文章目录 前言一、HTML的简介二、HTML运行方式三、html 的标签/元素-说明四、表单注意事项总结 前言 首先我们在把Java基础学习完之后,我们就要进行网站方面的开发了,我们要了解网页的组成,而网页的组成有HTML,CSS,…

互联网医院品牌IP的用户体验和生态构建

一、患者体验与信任构建互联网医院品牌IP的价值核心在于获得患者的深度信任,而卓越的用户体验是实现这一目标的关键路径。在医疗服务同质化严重的当下,患者体验已成为医疗机构差异化竞争的重要维度。研究表明,良好的用户体验能够提高用户满意…

【Node.js教程】Express框架入门:从搭建到动态渲染商品列表

前言 Visual Studio Code(简称VSCode)是微软开发的一款免费开源跨平台代码编辑器,凭借其免费、开源、跨平台的特性,以及丰富的插件生态和美观的界面,成为前端开发者的首选工具。 本文将带你从零开始学习Express框架,包括搭建项目、配置路由、使用中间件以及实现动态渲染…

众擎机器人开源代码解读

一,综述 EngineAI ROS 包: 高层开发模式:用户可通过发布身体速度指令,直接调用 EngineAI 机器人的行走控制器。底层开发模式:用户可通过发布关节指令,自主开发专属的控制器。 ROS2 package:全…

Windows系统安装Git详细教程

文章目录步骤 1:下载 Git 安装包步骤 2:运行安装程序步骤 3:选择安装路径步骤 4:选择组件步骤 5:选择默认编辑器步骤 6:选择路径环境变量步骤 7:选择 HTTPS 协议的传输方式步骤 8:配…

leetcode 3446. 按对角线进行矩阵排序 中等

给你一个大小为 n x n 的整数方阵 grid。返回一个经过如下调整的矩阵:左下角三角形(包括中间对角线)的对角线按 非递增顺序 排序。右上角三角形 的对角线按 非递减顺序 排序。示例 1:输入: grid [[1,7,3],[9,8,2],[4,…

携程旅行 web 验证码 分析

声明 本文章中所有内容仅供学习交流使用,不用于其他任何目的,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关! 逆向分析 部分python代码 result cp…

JavaEE 进阶第一期:开启前端入门之旅(上)

专栏:JavaEE 进阶跃迁营 个人主页:手握风云 一、HTML基础 1.1. 什么是HTML HTML(Hyper Text Markup Language),超文本标记语言。 超文本:比文本要强大,通过链接和交互式方式来组织和呈现信息的文本形式。不仅仅有文本…

4.5 PBR

1.PBR简介 2.高光工作流 3.金属工作流1.PBR简介 PBR(Physically Based Rendering, 基于物理的渲染)的工作流分为金属工作流和高光工作流2.高光工作流 高光工作流是一种传统的工作流, 现在用的相对较少, 但是在某些特定情况下能提供更精细的控制a.核心思想它不区分金属和非金属,…

09.《路由基础知识解析和实践》

09.路由基础 文章目录09.路由基础核心概念路由关键组成部分三层转发原理介绍(通信流程)路由类型及配置直连路由(direct)实验示例**静态路由(Static)****实验示例****动态路由****RIP(routing information protocol---路…

websocket建立连接过程

1. 客户端发送一个GET的http请求,请求头要包含connection: upgradehost:localhost:8000。表明地址upgrade: websocket。指明升级的协议sec-websocket-key 。 安全验证密钥sec-websocket-version。 协议版本sec-websocket-accept 。对传过来的key进行加密…

Simulink库文件-一种低通滤波模块搭建方法

在汽车电控系统应用层开发中,经常会用到低通滤波模块,其主要作用是去除输入信号中的高频干扰,防止由于输入信号的干扰引起后续执行系统的非预期频繁波动。本文介绍简要介绍低通滤波的定义及作用,并介绍一种低通滤波模块simulink搭…

【C++游记】AVL树

枫の个人主页 你不能改变过去,但你可以改变未来 算法/C/数据结构/C Hello,这里是小枫。C语言与数据结构和算法初阶两个板块都更新完毕,我们继续来学习C的内容呀。C是接近底层有比较经典的语言,因此学习起来注定枯燥无味&#xf…

音视频学习(六十二):H264中的SEI

什么是SEI? 在 H.264 视频编码标准中,补充增强信息(Supplemental Enhancement Information,SEI) 是一种特殊的 NAL(网络抽象层)单元。它不像序列参数集(SPS)或图像参数集&#xff0…

docker run 后报错/bin/bash: /bin/bash: cannot execute binary file总结

以下方法来源于AI&#xff0c;个人仅验证了第三条便成功执行 1. 镜像与宿主机架构不匹配 比如&#xff1a; 你是 x86_64 的机器&#xff0c;但镜像是 ARM64 的&#xff08;或反之&#xff09;。在 PC 上拉了树莓派用的镜像。查看镜像架构 docker inspect <image_name> | …

【Redisson 加锁源码解析】

Redisson 源码解析 —— 分布式锁实现过程 在分布式系统中&#xff0c;分布式锁 是非常常见的需求&#xff0c;用来保证多个节点之间的互斥操作。Redisson 是 Redis 的一个 Java 客户端&#xff0c;它提供了对分布式锁的良好封装。本文将从源码角度剖析 Redisson 的分布式锁实现…

uni-app支持单多选、搜索、查询、限制能否点击组件

<template><view class="multi-select-container" :class="{ single-select: !multiple, no-search: !searchable }"><!-- 当组件被禁用时,直接显示选中的内容 --><view class="disabled-display" v-if="disabled &a…

TFT屏幕:STM32硬件SPI+DMA+队列自动传输

看了网上的很多的SPIDMA的代码&#xff0c;感觉都有一些缺陷&#xff0c;就是基本都是需要有手动等待DMA完成的这个操作&#xff0c;我感觉这种等待操作在很大程度上浪费了时间&#xff0c;那么我加入的“队列”就是一种将等待时间利用起来的方法。原本的SPIDMA的操作逻辑如下图…

AI操作系统语言模型设计 之1 基于意识的Face-Gate-Window的共轭路径的思维-认知-情感嵌套模型

摘要&#xff08;AI生成&#xff09;本文提出了一种创新的AI操作系统语言模型设计框架&#xff0c;将人类意识活动的分层结构映射到人工智能系统中。该模型包含三个嵌套层次&#xff1a;理性思维层&#xff08;Face层&#xff09;&#xff1a;采用双面胶隐喻&#xff08;A/B面&…