【Redis原理】数据结构(上)

news2024/12/24 3:43:45

文章目录

  • 动态字符串(SDS)
    • 概念
    • `SDS`特点
    • `SDS`的优势
  • IntSet
    • 概念
    • IntSet的特点
      • 升序
      • 统一的编码格式
      • `IntSet`自动升级
  • Dict
    • 概念
    • Dict特点
      • Dict的伸缩
        • Dict的扩容
        • `Dict`收缩
      • `Dict`的`rehash`
        • 渐进式哈希
    • 总结
      • `Dict`的结构
      • `Dict`的伸缩

动态字符串(SDS)

概念

Redis是使用C语言实现的,C语言字符串底层是字符数组,结束使用/0来表示,但Redis并没有直接使用C语言的字符串,因为C语言字符串存在多个问题:

  • 获取字符串长度需要运算
  • 非二进制安全
  • 不可修改(保存在常量池中,是不能修改的)
    所以Redis创建了一种新的字符串结构,称之为简单动态字符串
    Redis是使用C语言实现的,其中SDS是一个结构体

Redis源码:
在这里插入图片描述
几个重点:

  • uint8_t:无符号8比特位整型,所以buf字符数组中最多保存2^8-1=254个字符(此处是sdshdr8类型的SDS)
  • Unsigned char flags:指代SDS五种类型SDS_TYPE_5 ,SDS_TYPE_8,SDS_TYPE_16,SDS_TYPE_32,SDS_TYPE_64

所以,一个SDS字符串的结构如下:
在这里插入图片描述

SDS特点

SDS之所以叫做动态字符串,是因为它具备动态扩容的能力
在这里插入图片描述

SDS的优势

  1. 获取字符串长度的时间复杂度为O(1)
  2. 支持动态扩容
  3. 减少内存分配次数(因为做了内存的预分配)
  4. 二进制安全(不以\0为结束标志,而是len属性来维护一个字符串)

IntSet

概念

IntSetRedisset集合的一种实现方式,基于整数数组来实现,并具备长度可变,有序等特征
在这里插入图片描述

  • uint32_t length:contents[]数组中的元素个数;
  • int8_t contents[]:存放整数数据
  • encoding包含三种模式,表示存储整数的大小;
    在这里插入图片描述

IntSet的特点

升序

为了方便查找,Redis会将intset中所有的整数按照升序依次保存在contents数组中,结构如图:
在这里插入图片描述

统一的编码格式

为了快速定位数据,统一编码格式后,可以根据索引下标第一个数据的内存地址,算出偏移量.
这里的理解方式如下:
在这里插入图片描述
我们获得了第一个数据的内存地址之后,由于编码方式是统一的,那么这里的每一个整数数据都是占有2个字节,那么第三个数据的偏移量为2*2个字节,这样我们就可以直接得出第三个数据的地址,而不需要进行遍历(这也就是我们的索引要从0开始的原因)

计算公式:startptr+(sizeof(encoding)*index)

刚才的例子中,所有数据可以使用16Byte来表示,那么如果这时加入一个数据,无法使用16Byte来表示了,例如:5000,该怎么办?这就涉及我们的IntSet自动升级了

IntSet自动升级

在这里插入图片描述
升级的流程:

  • 升级编码为INTSET_ENC_INT32,每个整数占4字节,并按照新的编码方式以及元素个数扩容数组
  • 倒序依次将数组中的元素拷贝到扩容后的正确位置
  • 将待添加的元素放到数组末尾
  • 最后将intsetencoding属性改为INTSET_ENC_INT32 ,将length属性改为4

Dict

概念

我们知道Redis是一个键值型(Key-Value Pair)的数据库,我们可以根据键实现快速的增删改查.而键与值的映射关系正是通过Dict来实现的.
Dict是由三部分组成:哈希表(DictHashTable),哈希节点(DicEntry),字典(Dict)
当我们向Dict添加键值对的时候,Redis首先根据Key计算出hash值(h),然后利用h&sizemask(由于size=sizemask-1h%size的效果是一样的)来计算元素应该存储到数组中哪个索引的位置
下图是这三部分结构体的设计:

在这里插入图片描述

这三者的关系是:
在这里插入图片描述

Dict特点

Dict的伸缩

Dict的扩容

Dict的扩容:Dict中的HashTable就是数组结合单向链表来实现的,当集合中元素较多时,必然导致哈希冲突增多,链表过长,则查询效率会大大降低.
Dict每次新增键值对时都会检查负载因子(LoadFactor=used/size),满足以下两种情况时会触发哈希表扩容:

  • 哈希表的LoadFactor>=1,并且服务器没有执行bgsave或者bgrewriteaof后台进程(后台进程的对CPU的消耗是很大的);
  • 哈希表的LoadFactor>5;
    扩容的源码:
    在这里插入图片描述
    注意:这里的扩容大小为used+1,但并不是代表就是扩容used+1,而是扩容到第一个大于used+12^n
Dict收缩

Dict收缩:每次删除元素时,也会对负载因子做检查,当LoadFactor<0.1时,会做哈希收缩.
Dict执行哈希收缩的原码:
在这里插入图片描述

Dictrehash

不管是扩容还是收缩,必定会创建新的哈希表,导致哈希表的sizesizemask发生变化,而key的查询与sizemask有关.因此必须对哈希表中的每一个key重新计算索引,插入新的哈希表,这个过程称为rehash.过程是这样的:

  1. 计算新hash表的realeSize,值取决于当前要做的是扩容还是收缩:
  • 如果是扩容,则新的size为第一个大于等于dict.ht[0].used+12^n
  • 如果是收缩,则新的size为第一个的大于等于dict.ht[0].uesd2^n(不得小于4)
  1. 按照新的realeSize申请内存空间,创建dictht,并赋值给dict.ht[1]
  2. 设置dict.rehashidx=0,标示开始rehash
  3. dict.ht[0]中的每一个dictEntryrehashdict.ht[1]
  4. dict.ht[1]赋值给dict.ht[0],给dict.ht[0]初始化为空哈希表,释放原来的dict.ht[1]内存
    如图所示:
    在这里插入图片描述
渐进式哈希

因为新增或者删除操作一般来说都是在**Redis主线程中进行的,所以如果在主线程中触发了rehash操作,在数据量很大的情况下,可能会导致出现主线程阻塞的情况.
所以在Redis当中,rehash
分多次,渐进式**完成的,因此称之为渐进式rehash.流程如下:

  1. 计算新hash表的size,值取决于当前要做的是扩容还是收缩:
  • 如果是扩容,则新的size为第一个大于等于dict.ht[0].used+12^n
  • 如果是收缩,则新的size为第一个的大于等于dict.ht[0].uesd2^n(不得小于4)
  1. 按照新的size申请内存空间,创建dictht,并赋值给dict.ht[1]
  2. 设置dict.rehashidx=0,标示开始rehash(这里标记成0的原因是进行数据迁移的时候,下标就是从0开始的)
  3. dict.ht[0]中的每一个dictEntryrehashdict.ht[1]
    每次执行到新增,查询 ,修改,删除操作时,都检查一下dict.rehashidx是否大于-1,如果是,则将dict.ht[0].table[rehashidx]entry链表rehashdict.ht[1],并且将rehashidx++.直至dict.ht[0]的所有数据都rehash到了dict.ht[1]

    所以,渐进式哈希,相当于一次每次只是迁移了一个哈希节点上的链表
  4. dict.ht[1]赋值给dict.ht[0],给dict.ht[0]初始化为空哈希表,释放原来的dict.ht[0]内存
  5. rehashidx赋值为-1,代表rehash结束
  6. rehash过程中.新增操作,则直接写入ht[1],查询,修改和删除则会在dict.ht[0]dict.ht[1]依次查找并且执行.这样可以确保ht[0]的数据只减不增,随着rehash最终为空.

总结

Dict的结构

  1. 类似javaHashTable,底层是数组加链表来解决哈希冲突
    2. Dict包含两个哈希表,ht[0]平常用.ht[1]用来rehash

Dict的伸缩

  1. LoadFactor大于5或者LoadFactor大于1并且没有子进程任务时,Dict扩容
  2. LoadFactor<0.1时,Dict收缩
  3. 扩容大小为第一个大于等于user+12^n
  4. 收缩大小为第一个大于等于user2^n
    5. Dict采用渐进式hash,每次访问Dict时执行一次rehash
  5. rehashht[0]只减不增,新增操作只在ht[1]执行,其他操作在两个哈希表

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

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

相关文章

【后端开发】自动化部署、服务管理、问题排查工具(cicd流水线,k8s集群,ELK日志)

【后端开发】自动化部署、服务管理、问题排查工具&#xff08;cicd流水线&#xff0c;k8s集群&#xff0c;ELK日志&#xff09; 文章目录 1、Devops与CICD流水线(TeamCity, Jenkins&#xff0c;GitHub Actions)2、Kubernetes 集群的管理和操作&#xff08;对比Portainer&#x…

【解决】Set-ExecutionPolicy不是内部或外部命令

简介 当使用 VsCode 配置Django项目时&#xff0c;需要配置环境&#xff0c;但是当切换至虚拟环境时&#xff0c;出现了下面的情况。 无法加载文件&#xff1a;D:\django\Scripts\Activate.ps1&#xff0c; 上述问题可通过下面的命令进行解决 解决方法 1 命令行(最好是管理员…

JVM进阶调优系列(1)类加载器原理一文讲透

今天开始写JVM调优系列&#xff0c;并发编程系列也会继续穿插连载&#xff0c;让各位同学闲暇之余有更多阅读选择。 起笔写第一篇&#xff0c;并不好写。首先要构思整个系列的大概框架&#xff0c;一个好的框架一定是深度上由浅入深、逻辑上有严格顺序&#xff0c;读者订阅跟踪…

免费获取的8个SVG图标库,轻松下载与复制!

SVG图标相比传统的JPG、PNG图标具有诸多优势&#xff0c;适用于各种类型的图像&#xff0c;不仅能在不同尺寸下保持清晰度&#xff0c;还具备高度压缩性和轻量特性&#xff0c;支持静态和动态效果。因此&#xff0c;SVG格式在网页设计中往往是优选。尽管如今有很多免费的图标库…

风扇PD协议取电协议芯片-ECP 5702

随着USB-C的普及&#xff0c;市面上消费者PD充电器越来越多&#xff0c;如何让小家电产品也能够支持PD协议快充呢&#xff1f;加入一颗能芯科技PD协议取电协议芯片ECP5702试试看 USB PD协议受电端诱骗协议芯片 1、概述 ECP5702是能芯科技开发的一款专门PD协议的Sink控制器。 …

【论文速看】DL最新进展20241010-扩散模型、目标检测、行人检测

目录 【扩散模型】【目标检测】【行人检测】 【扩散模型】 []Faster Diffusion: Rethinking the Role of UNet Encoder in Diffusion Models 论文链接&#xff1a;https://arxiv.org/pdf/2312.09608 代码链接&#xff1a;https://github.com/hutaiHang/Faster-Diffusion 扩散…

No.10 笔记 | PHP学习指南:PHP数组掌握

本指南为PHP开发者提供了一个全面而简洁的数组学习路径。从数组的基本概念到高级操作技巧&#xff0c;我们深入浅出地解析了PHP数组的方方面面。无论您是初学者还是寻求提升的中级开发者&#xff0c;这份指南都能帮助您更好地理解和运用PHP数组&#xff0c;提高编码效率和代码质…

java批量发送邮件:如何实现高效邮件群发?

java批量发送邮件的教程指南&#xff1f;利用Java实现邮件批发&#xff1f; 随着技术的进步&#xff0c;java批量发送邮件已经成为企业实现高效邮件群发的关键工具。AokSend将探讨如何利用java批量发送邮件技术&#xff0c;实现高效的邮件群发&#xff0c;提升营销效果。 jav…

相当炸裂!495页看漫画学Python(全彩版)通俗易懂!Git首发破万Star

今天给大家分享一份由清华大学出品的《看漫画学Python》&#xff0c;本书作者对每一幅漫画表达的准确性也进行了N遍的推敲和打磨&#xff0c;向广大读者奉献一本精品漫画Python技术书。 总共495页&#xff0c;书中结合了幽默的故事情节和实用的编程知识&#xff0c;使得学习过…

【LeetCode】动态规划—673. 最长递增子序列的个数(附完整Python/C++代码)

动态规划—673. 最长递增子序列的个数 前言题目描述基本思路1. 问题定义2. 理解问题和递推关系3. 解决方法3.1 动态规划方法3.2 优化方法 4. 进一步优化5. 小总结 代码实现PythonPython3代码实现Python 代码解释 CC代码实现C 代码解释1. 初始化&#xff1a;2. 动态规划过程&…

Basic Pentesting靶机打靶记录

一、靶机介绍 下载链接&#xff1a;https://download.vulnhub.com/basicpentesting/basic_pentesting_1.ova 二、信息收集 确认靶机ip&#xff1a;192.168.242.136 arp-scan -l 扫描端口 nmap -p- -A -sS 192.168.242.136 这里开放了21&#xff0c;22&#xff0c;80端口 扫…

美发店数字化转型:SpringBoot管理系统

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

鸿蒙开发:文件推送到沙箱路径

最近一个项目需要基于沙箱路径下的文件进行操作&#xff0c;奈何应用沙箱路径下没有。找来找去方法都是要把文件推送进去。以下是我的一些拙见&#xff0c;请各位看官老爷指点一二。 沙箱路径 沙箱路径&#xff08;Sandbox Path&#xff09;通常是指在计算机安全和软件开发中…

【大学学习-大学之路-回顾-电子计算机相关专业-学习方案-自我学习-大二学生(2)】

【大学学习-大学之路-回顾-电子&计算机相关专业-学习方案-自我学习-大二学生&#xff08;2&#xff09;】 1、前言2、总体说明1-保证课程原因1&#xff1a;原因2&#xff1a; 2-打比赛3-自我适应 - 享受大学生活 3、 保证课程1、英语课程2、专业课程3、其他课程 4、 打比赛…

数据质量指标:如何衡量数据的准确性

数据质量是任何数据驱动运营的重要组成部分。即使对于不打算将数据集出售给其他公司的企业&#xff0c;数据的质量和准确性也会极大地影响决策效率。 不幸的是&#xff0c;没有单一指标可以确保数据质量达到标准。您必须跟踪多个指标并不断关注它们。因此&#xff0c;维护数据…

高通QCS6490开发(十):合并显示多路安防摄像头

视频分析时边缘侧AI应用的一个常见场景&#xff0c;边缘侧的单个节点能够同时视频流越多&#xff0c;这不仅提高了处理效率&#xff0c;还具有显著的经济性。本文将介绍如何使用QCS6490的VPU&#xff08;视频处理单元&#xff09;来支持H264/H265的视频硬件编解码&#xff0c;并…

C语言计算GPS卫星位置

1 概述 在用GPS信号进行导航定位以与制订观测计划时&#xff0c;都必须已知GPS卫星在空间的瞬间位置。卫星位置的计算是根据卫星电文所提供的轨道参数按一定的公式计算的。本节专门讲解观测瞬间GPS卫星在地固坐标系中坐标的计算方法。 2 卫星位置的计算 1. 计算卫星运行的平…

如何做好项目管理中的需求管理?

本人任职于某科技公司项目经理&#xff0c;主要帮助客户梳理现有的业务流程&#xff0c;借助公司自主研发的低代码平台实现流程的线上化&#xff0c;业务的数字化转型。 由于项目性质特殊&#xff0c;在实施期间&#xff0c;对于总体项目需要采用传统的瀑布式开发规划整个项目…

揭秘网络流量分析的秘密 WireShark使用教程

WireShark是一个网络包分析工具。该工具主要用来捕获网络数据包&#xff0c;并自动解析网络数据包&#xff0c;为用户显示数据包详细信息&#xff0c;供用户对数据包进行分析 网络管理员 使用WireShark来检查网络问题网络安全工程师 使用WireShark来检查咨询安全相关问题开发人…

腾讯云大牛亲码“redis深度笔记”在牛客网上火了,完整 PDF 开源

前言 作为这个时代码代码的秃头人员&#xff0c;对Redis肯定是不陌生的&#xff0c;如果连Redis都没用过&#xff0c;还真不好意思出去面试&#xff0c;指不定被面试官吊打多少次。 毕竟现在互联网公司和一些创业公司都要用到Redis&#xff0c;像亚马逊、谷歌、阿里、腾讯都要…