Redis原理篇(一)

news2024/12/23 18:51:49

一、原理篇-Redis数据结构

1.1 Redis数据结构-动态字符串

我们都知道Redis中保存的Key是字符串,value往往是字符串或者字符串的集合。可见字符串是Redis中最常用的一种数据结构。

Redis虽由C语言开发,不过Redis没有直接使用C语言中的字符串,因为C语言字符串存在很多问题:

  • 获取字符串长度的需要通过运算
  • 非二进制安全
  • 不可修改

Redis构建了一种新的字符串结构,称为简单动态字符串(Simple Dynamic String),简称SDS。

其优点有:

  • 获取字符串长度的时间复杂度为O(1)
  • 支持动态扩容
  • 减少内存分配次数
  • 二进制安全

例如,我们执行命令:

那么Redis将在底层创建两个SDS,其中一个是包含“name”的SDS,另一个是包含“虎哥”的SDS。

其中SDS是一个结构体,源码如下:

例如,一个包含字符串“name”的sds结构如下:

SDS之所以叫做动态字符串,是因为它具备动态扩容的能力,例如一个内容为“hi”的SDS:

假如我们要给SDS追加一段字符串“ ,Amy ”,这里首先会申请新内存空间:

  • 如果新字符串小于1M,则新空间为扩展后字符串长度的两倍+1;
  • 如果新字符串大于1M,则新空间为扩展后字符串长度+1M+1。称为内存预分配。

1.2 Redis数据结构-intset

IntSet是Redis中set集合的一种实现方式,基于整数数组来实现,并且具备长度可变、有序等特征。 结构如下:

其中的encoding包含三种模式,表示存储的整数大小不同:

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

现在,数组中每个数字都在int16_t的范围内,因此采用的编码方式是INTSET_ENC_INT16,每部分占用的字节大小为:

  • encoding:4字节
  • length:4字节
  • contents:2字节 * 3 = 6字节

我们向该其中添加一个数字:50000,这个数字超出了int16_t的范围,intset会自动升级编码方式到合适的大小。 以当前案例来说流程如下:

  • 升级编码为INTSET_ENC_INT32, 每个整数占4字节,并按照新的编码方式及元素个数扩容数组

  • 倒序依次将数组中的元素拷贝到扩容后的正确位置

  • 将待添加的元素放入数组末尾

  • 最后,将inset的encoding属性改为INTSET_ENC_INT32,将length属性改为4

源码如下:

Intset可以看做是特殊的整数数组,具备一些特点:

  • Redis会确保Intset中的元素唯一、有序

  • 具备类型升级机制,可以节省内存空间

  • 底层采用二分查找方式来查询

1.3 Redis数据结构-Dict

我们知道Redis是一个键值型(Key-Value Pair)的数据库,我们可以根据键实现快速的增删改查。而键与值的映射关系正是通过Dict来实现的。 Dict由三部分组成,分别是:哈希表(DictHashTable)、哈希节点(DictEntry)、字典(Dict)

当我们向Dict添加键值对时,Redis首先根据key计算出hash值(h),然后利用 h & sizemask来计算元素应该存储到数组中的哪个索引位置。我们存储k1=v1,假设k1的哈希值h =1,则1&3 =1,因此k1=v1要存储到数组角标1位置。

Dict由三部分组成,分别是:哈希表(DictHashTable)、哈希节点(DictEntry)、字典(Dict)

Dict的扩容

Dict中的HashTable就是数组结合单向链表的实现,当集合中元素较多时,必然导致哈希冲突增多,链表过长,则查询效率会大大降低。 Dict在每次新增键值对时都会检查负载因子(LoadFactor = used/size) ,满足以下两种情况时会触发哈希表扩容: 哈希表的 LoadFactor >= 1,并且服务器没有执行 BGSAVE 或者 BGREWRITEAOF 等后台进程; 哈希表的 LoadFactor > 5 ;

Dict的rehash

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

  • 计算新hash表的realeSize,值取决于当前要做的是扩容还是收缩:

    • 如果是扩容,则新size为第一个大于等于dict.ht[0].used + 1的2^n

    • 如果是收缩,则新size为第一个大于等于dict.ht[0].used的2^n (不得小于4)

  • 按照新的realeSize申请内存空间,创建dictht,并赋值给dict.ht[1]

  • 设置dict.rehashidx = 0,标示开始rehash

  • 将dict.ht[0]中的每一个dictEntry都rehash到dict.ht[1]

  • 将dict.ht[1]赋值给dict.ht[0],给dict.ht[1]初始化为空哈希表,释放原来的dict.ht[0]的内存

  • 将rehashidx赋值为-1,代表rehash结束

  • 在rehash过程中,新增操作,则直接写入ht[1],查询、修改和删除则会在dict.ht[0]和dict.ht[1]依次查找并执行。这样可以确保ht[0]的数据只减不增,随着rehash最终为空

整个过程可以描述成:

小总结:

Dict的结构:

  • 类似java的HashTable,底层是数组加链表来解决哈希冲突

  • Dict包含两个哈希表,ht[0]平常用,ht[1]用来rehash

Dict的伸缩:

  • 当LoadFactor大于5或者LoadFactor大于1并且没有子进程任务时,Dict扩容

  • 当LoadFactor小于0.1时,Dict收缩

  • 扩容大小为第一个大于等于used + 1的2^n

  • 收缩大小为第一个大于等于used 的2^n

  • Dict采用渐进式rehash,每次访问Dict时执行一次rehash

  • rehash时ht[0]只减不增,新增操作只在ht[1]执行,其它操作在两个哈希表

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

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

相关文章

vulnhub靶机Thales:1

Thales:1 靶机地址:Thales: 1 ~ VulnHub 主机发现 arp-scan -l 扫描端口 nmap --min-rate 10000 -p- 192.168.21.135 nmap -sV -sT -O -p22,8080 192.168.21.135 简单的漏洞的扫描 nmap --scriptvuln -p22,8080 192.168.21.135 答题思路就是从8080端口拿到账号密…

MS VC 2022开发Linux应用记录之01篇

安装MSVS2022的时候勾上对开发Linux C程序的选项在Windows中安装Oracle Virtual Box程序,在里面安装Ubuntu最新稳定版,要选择多个CPU核在VirtualBox中添加一个网卡,选择Host Only在虚拟机中使用ifconfig命令,在宿主机中使用ipconfig, 可以看到两者存在同…

有限状态自动机

1 什么是有限状态自动机 1.1什么是计算 维基百科定义:计算(英语:Calculation)是一种将“单一或多个的输入值”转换为“单一或多个的结果”的一种思考过程。可以简单理解为给出一个问题得到一个答案的过程。如下图所示日常生活比…

AITO问界,先经沧海而后造船

IT领域最重要的原则之一,就是软件快速迭代。 对于科技产品来说,需求永远在升级。一项技术或软件系统问世之后,如果后续不再迭代,结果可能是灾难性的。 比如几年前,很多读者可能都买过一些“不了了之”的智能消费硬件&a…

性能测试Ⅳ

在进行性能测试的时候需要使用不同阶段的数据来测试,分析不同数据下资源的情况。 java -jar -Xms1M -Xmx1M -XX:MaxMetaspaceSize10m DBPlus-0.0.1-SNAPSHOT.jar 最小内存 最大内存 如果内存太小会导致内存泄露 启动程序 java -Djava.rmi.serv…

JavaWeb课程设计项目实战(09)——项目编码实践6

版权声明 本文原创作者:谷哥的小弟作者博客地址:http://blog.csdn.net/lfdfhl 在本节教程中,我们实现修改学生的功能。当在学生列表页面点击修改后首先将依据id查询该生的详细信息,然后将这些信息展示在修改页面。当完成学生信息…

Transformer Encoder (Bert)

参考:图解Self-Attention_子燕若水的博客-CSDN博客 举个例子: 假设输入数据形状为(243,34),表示的是243帧,每帧包含34个特征(比如17个关键点的x,y坐标)。那么这个数据在Transformer Encoder中的流动过程如下: 输入数据shape是(243, 34),表示243个时间…

【字符流】案例:点名器

案例:点名器 1.需求: 我有一个文件里面存储了班级同学的姓名,每一个姓名占一行,要求通过程序实现随机点名器 2.思路: 创建字符缓冲输入流对象创建ArrayList集合对象调用字符缓冲输入流对象的方法读数据把读取到的字…

vue ---- filters过滤器中不能使用this问题

在日常开发中,使用filters是很正常,最近遇到切换单位,页面上显示的数据要根据单位转换,这时就需要根据data里面的变量去转换,可是filters里面不能使用this 解决: 1、先在return中声明一个变量that&#xf…

vuejs源码之模版编译原理

之前我们说过虚拟dom,也就是虚拟dom拿到vnode后所做的事情,而模版编译是如何让虚拟dom拿到vnode。 模版编译的目标就是生成渲染函数,而渲染函数的作用是每次执行它,它就会使用当前最新的状态生成一份新的vnode,然后用…

========Java基础——小结1========

一、Java 两大版本 Java 主要分为两个版本: Java SE 和Java EE。 Java SE 全称Java Platform Standard Edition,是 Java 的标准版,主要用于桌面应用程序开发,它包含了 Java 语言基础、JDBC (Java 数据库连接)、I/O (输入/输出)、TCP/IP 网络…

【问题记录】Ubuntu 22.04 环境下,程序报:段错误(核心已转储)怎么使用 core 文件和GDB调试器 解决?

目录 环境 问题情况 解决思路 原因分析 解决方法 番外知识 环境 VMware Workstation 16 Pro (版本:16.1.2 build-17966106)ubuntu-22.04.2-desktop-amd64 问题情况 本人在运行百万并发的服务端程序时,程序运行报&#xff1a…

语音基石模型Speech Foundation Models

语音基石模型(Speech Foundation Models) 主要包含三部分: 1.语音表示学习(Speech representation learning) 自监督学习模型(Self-suprevised learning, SSL model)Representation benchmark…

CMU 15-445 -- Embedded Database Logic - 12

CMU 15-445 -- Embedded Database Logic - 12 引言User-Defined Functions (UDF)SQL FunctionsExternal Programming Language Stored ProceduresStored Procedures 与 UDF 的区别 Database TriggersChange NotificationsUser-Defined Types (UDT)Viewsviews vs select...intov…

区别出过孔的内径、外径、单边孔环、电镀铜厚

自记: 这个参数是啥?下图区别出过孔的内径、外径、单边孔环、电镀铜厚 嘉立创单双面最小过孔内径0.3mm/外径0.6mm(极限0.56mm),四、六层最小过孔内径0.2mm/外径0.45mm(极限0.40mm),外…

学习day50

自定义指令总结: 一:定义语法: (1)局部指令: new Vue({ directives{指令名,配置对象} }) 或 new Vue({ directives{指令名,回调函数} }) (2)全局对象 Vue.dir…

基于Gym Anytrading 的强化学习简单实例

近年来强化学习(RL)在算法交易领域受到了极大的关注。强化学习算法从经验中学习并基于奖励优化行动使其非常适合交易机器人。在这篇文章,我们将简单介绍如何使用Gym Anytrading环境和GME (GameStop Corp.)交易数据集构建一个基于强化学习的交易机器人。 强化学习是…

【Java从0到1学习】05 Java 数组

1. 数组概述 需求:现在需要统计某公司员工的工资情况,例如计算平均工资、找到最高工资等。假设该公司有80名员工,用前面所学的知识,程序首先需要声明80个变量来分别记住每位员工的工资,然后在进行操作,这样…

MySQL一些知识

六、MySQL命令参数 七、远程登录 use mysql 八、SQL语句和常见的SQL操作 九、数据库和表的创建及插入 指定字段名称,按照表的字段名称顺序写: 指定字段名称: 字段名称可以不全部指定:

SpringBoot(三)

文章目录 前言一.日志的作用二.日志的使用2.1 自定义日志打印三.日志的级别3.1 日志级别的作用3.2 日志级别的分类和使用 四.⽇志持久化 前言 日志在应用程序中扮演着至关重要的角色,它是软件开发、运维和故障排查中不可或缺的工具。无论是大型企业级应用还是小型个…