redis数据结构和数据类型

1.动态字符串SIMPLE DYNAMIC STRING(SDS)

观察上图中的SDS结构,头部包含字符串长度和分配的空间,可以以O(1)的时间复杂度计算出字符串长度,并且有了字符串长度后可以无视c语言的字符串缺陷(\0作为结尾标识,容易被误判),可以动态扩容以及二进制安全。

2.intset

intset是一个集合,用于存储整形数据,由于是set所以满足元素唯一性,在不需要扩容的情况下,插入新元素时,底层采用二分法进行查找插入位置,效率为Ologn。

intset支持动态扩容,如果插入的元素大小大于当前编码ecoding设计大小,那么就会进行inset升级,具体见下述流程。

当插入一个比int16大得元素,那么就涉及到了整体的扩容,但这种情况会造成相当大得空间浪费。

具体过程是:假设目前的编码是int16,那么就升级为int32(如果int32能够装下新插入的数据,如果不够就继续升级),然后按照排列的倒叙对所有数据进行移动,最后再插入新元素。因为新加入的元素所需容量比剩余元素都大,并且所有元素都是int整形,那么新插入的元素一定是最大的,所以存储在末尾。

intSet 是一个set,本身得元素必须是唯一的,为了保证整体数据的有序性,所以在插入元素的时候需要进行判断:

判断方式即对传入数据的值进行查找,如果没有查找到,那么就插入,否则直接返回(有相同元素)。这个特性使得在当set集合中存储的元素是整数时,并且插入数据不超过set中默认设置的intset最大值时,intset作为set数据类型的数据结构,否则dict会作为set的数据结构。

查找的过程使用二分查找,根据intset存储的整形数据的value进行查找插入位置,用于保证intset的有序性。

3.dict

3.1Dict的数据结构

dict数据结构会创建一个dictht表示字典的hashtable,里面有一个指向entry数组的指针、size数组大小、sizemask使用与运算“&”进行取余、used表示已有entry的数量。

需要注意,这里的dictEntry是一个键值对。

后续的set就是使用dict进行实现的,不过将dictEntry设置为了0。

3.2Dict的扩容

dict这个数据结构存储了两张表,一张表用于存储所有的entry的指针,一张表用于rehash。

rehash的原因在于dict需要进行扩容,最初的大小为size,如果此时used(即已存储的entry数量)已经大于size的值了(即负载因子>1),那么此时就会考虑dict的扩容,前提是后台不忙碌的情况,当负载因子>5之后,dict不管后台是否有处理过程,都会进行扩容。

此时会根据used的数量,找到离used最近的2^n,比如现在的used为5,那么扩容的size就为8,会在等待执行rehash的表创建相应的存储容量,再将原本数据复制到新表中,最后将原表置空,从而完成扩容。

3.3Dict的收缩

如果负载因子<0.1,就会触发收缩,如果当前size<4那么会将另一张等待rehash的空表大小置为4,否则,根据size找到离size最近的2^n来进行扩容,最后将源数据复制到新表,将原数据表置为rehash辅助表。

3.4总结

dict的数据结构包含两张dictht表,记为ht(0)与ht(1),ht(0)用于存储当前数据,ht(1)用于rehash辅助来扩容与收缩。ht(0)与ht(1)会反复翻转,来实现扩容与收缩。扩容与收缩都是根据负载因子来判断的,最终都会找到离size最近的2^n作为新的存储容量。

最后需要了解一个细节,rehash不是一次性转移整张表,而是批量处理部分数据内容,因为数据量大的情况下,处理时间会很长

4.ZipList

4.1数据结构

ziplist的头部会保存整个链表的长度、尾指针以及entry的数量,ziplist用于节省存储空间

ziplist节省空间的原因在于不会存储指针,entry中会记录上一个节点的entry大小、entry中自身节点大小与编码方式以及自身数据,这样会比存指针和数据节省更多空间。这里的entry就是指一个节点。

同时entry中存储的内容content是可变长的,所以不会因为等长如intset浪费掉空间。

又由于ziplist中每个entry有上个节点的大小,所以可以通过地址计算进行顺序以及逆序遍历(顺序遍历通过当前entry起始地址加上本节点的大小计算下个节点的起始地址、逆序遍历通过当前entry的起始地址-上个节点大小)

4.2连锁更新问题

ziplist如果本节点的内容进行更新,假设现在使得后一个节点记录的前一个节点的长度从1字节变为了5字节,也就是说当前节点的更新 导致了 下个节点的更新(因为后一个节点记录了前一个节点的长度,而当前一个节点的长度大于254字节,就会使得后一个字节的前一个节点大小从1字节升级为5字节)。

在极端情况下就会出现一个节点更新,导致连锁反应,后续节点都进行更新,更新需要使用到内核态,涉及到内核态与用户态的切换,进行内存分配,资源开销很大。

5.quicklist

quicklist的创建就是为了解决ziplist的自身问题,比如ziplist需要连续的存储空间,那么当数据量很大时,内存分配很大的连续空间的难度会很高,所以为了解决这个问题,将ziplist拆分为多段,使用一个辅助节点来连接对应的ziplist段,每个辅助节点都有一个pre指针与next指针还有一个zip指针。

这个辅助接点就是quicklist节点。

整个数据结构称为quicklist。

quicklist可以对每个ziplist进行压缩,一般而言对中间节点进行压缩,因为查询是通过头节点与尾节点进行查询的。

6.SkipList

总结:跳表就是一个多指针链表,会在链表之间建立新的指针来增加遍历的跨度,根据score的大小来具体判断使用哪个跨度对应的指针,从而加速查找速度。

在redis中最多支持32级跳表

Redis 的跳表主要应用在 有序集合(ZSet) 的底层实现中(配合哈希表),它通过如下结构来维持元素的顺序

  • 每个元素按照 score(分数)从小到大排列
  • 插入时按照 score 插入合适的位置;
  • 查询时可以快速跳跃查找,比如范围查找、排名查找、按顺序遍历等。

7.RedisObject

8.String数据类型

string数据类型的基本编码方式是RAW,redisobject与SDS是分块存储的,而当SDS的大小小于44字节时,此时RAW编码会更改为EMBSTR。

如果存储的字符串是整数类型,并且在long范围以下,那么会在内存中创建一个空间用于存储整形数据,然后将指针指向该空间即可。这种编码方式是INT编码

9.list数据类型

list数据结构底层实现使用了quicklist,lpush和rpush对应双端队列得队首与队尾,lpop与rpop同理。

10.set数据类型

set结构底层实现使用的是dict数据结构,dict数据结构存储得是一个entry,也就是一个键值对。在set应用dict作为数据结构时,会将键值对中的值置为NULL

当存储的值都为整数,并且存储的数据量没有超过set中设置的最大值时,使用的数据结构是intset

,否则使用dict

11.zset数据类型

zset这个数据类型存储的内容是一个键值对,键存储对应的value,而值存储score。

zset的特性要求键值存储、唯一键、可排序的特性。

所以对应的数据结构,满足键值存储的就只有dict字典。但是dict实际上是一个hash表,无法进行排序在zset中进行键值查询

跳表本身包含score,在zset中用于范围查询和排序功能。

所以zset选择将数据分别存储在skipList和dict中,使用dict来进行查询键值对,使用skipList来进行排序。

当zset中的数据量较少,单个数据的大小也较小,具体小于某个预设值时,为了节省空间,zset会转换为一张ziplist压缩表,ziplist本身不支持排序功能,但是其占用空间小,并且存储的数据少,相应查询时间也不会太长。相当于是一种时间换空间的策略,但是由于数据量少,这个时间的增长可以接收。

ziplist存储的entry只能包含一个内容,所以在zset中,两个连续的entry会分别存储value和score

12.hash数据类型

底层就是使用dict,完全适配,没什么好说的。

13.redis数据结构以及对应的数据类型总结

redis数据结构有SDS,这是redis自带一种字符串存储结构,其避免了c语言的字符串问题,同时支持空间的动态扩充,能以O(1)时间复杂度读取字符串长度以及具有二进制安全;

有intset,这是一种相对灵活的整形数组,所有元素存储大小都是相同的,如果某个元素大于当前存储元素的最大空间,那么intset会进行动态升级。在intset中会在头部记录存储的数据数量、尾部指针、当前的数据存储空间大小(8、16、32)。支持存储空间动态扩张、插入有序、底层使用了二分法来插入数据,所以元素是有序的,读取速度快,缺点是需要连续的存储空间,并且伴随相当一部分的空间浪费;

有dict字典,这是一个典型的哈希表,通过hash code得到散列值,再将散列值与掩码进行与运算得到具体的存储位置。与经典哈希不同的点就在于使用与运算进行装填。dict包含两张ht,一张ht用于存放数据,一张ht用于rehash进行扩容与紧缩。扩容与紧缩都是根据负载因子来进行的,当负载因子大于1并且无后台操作时,或者负载因子大于5时,进行扩容,当负载因子<0.1时进行紧缩。扩容与紧缩都是找当前离size最近的2^n来作为新的size,比如原本size为5,现在就取8;

有zipList压缩链表,压缩链表特点是每个节点包含上一个节点的长度以及自身长度,可以看作是一个可变长的列表,虽然不支持随机访问,但是支持顺序、逆序访问。由于没有使用指针,并且每个数据内容都装满了存储空间(除了存储的辅助信息外),存储的密度非常高,基本不会出现空间浪费。缺点是需要申请连续的存储空间,当所需连续空间较大时,操作系统难以分配所需空间,同时还有可能出现连锁更新,即一个节点扩容,导致下个节点的encoding部分增大,刚好又引起下下一个节点的encoding部分增大,从而出现连锁反应;

quicklist快速链表,快速链表就是使用辅助接点将ziplist穿起来,并且辅助结点有pre behind、zip三个指针。quicklist解决了ziplist的问题,使得一个很长的数据内容能够切成多篇进行存储。quicklist是list数据类型的底层实现。同时quicklist能够将ziplist的中间部分进行压缩,因为list数据类型基本只会对队首与队尾进行操作,通过压缩中间部分内容可以节省存储空间;

skiplist跳表,跳表可以看作是一个多指针链表,链表中不同的节点间建立指针来加快查询速度,跳表的查询速度基本与红黑树相当。并且在跳表中维护了一个score值,根据score值来进行查询,所以跳表的数值是有序的。目前学到的数据结构中,就skiplist和intset是有序的,intset底层使用二分查找实现有序插入,而skiplist根据score来进行有序插入;

redisobject,所有redis数据存储的顶层封装就是redisobject,包含对应的数据编码方式,数据指针等。

在数据类型中学习了String、list、set、zset、hash

String使用SDS进行存储,没什么好说的;

list使用quicklist实现,quicklist通过多个辅助节点和多个ziplist组合而成,将中间ziplist进行压缩,留下首尾节点(可以调整首尾节点的数量),可见list结构不支持顺序检索、但是存储密度大、只需对首尾进行操作;

set集合底层使用dict字典数据结构,数据包含多个内容,比如Sadd text 01,02,03,这里就插入01、02、03三个数,显然01、02、03数据的结构不是一个键值对,所以使用dict时会保留键,而将值置空;

zset底层使用跳表和dict数据结构实现,跳表用于根据score进行范围查询,通过key查value(对应score)就需要使用到dict。同时需要注意zset的键唯一性是由dict实现的;

最后hash由dict实现没什么好说的。

.redis如何保证数据一致性

redis与数据库之间的一致性是极为重要的,但是数据的高一致性就意味着更大的性能开销:

1.先修改数据库再修改缓存,但这种方式很容易出现问题,比如某个线程再修改完数据库之后,再去修改缓存却因为未知原因挂掉了,此时没有后续干预,那么redis与数据库之间就出现了不一致问题。这种方法的一致性低,但是效率高,因为不需要进行重建缓存,如果一致性要求不高,可以使用该方法提升效率。

2.还有一种常见的作法就是,

写操作需要更新数据时:直接修改数据库,然后删除缓存,
而读操作如果缓存命中,那么直接返回缓存信息,如果未命中,那么就去读数据库进行缓存重建。

这种做法缓解了第一种方法的不一致情况,因为直接删除的开销会小于修改的开销,所以出错的概率会更小。

但是会增加缓存重建的开销,并且仍然可能出现问题。

3.延时双删,建立一个消息队列进行兜底,使用延时策略,在删除缓存之后的某个时间,比如200ms,来进行读缓存与数据库,如果数据一直,则不做操作,否则删除缓存。

延时双删仍然会存在短暂的数据不一致情况。

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

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

相关文章

深度学习--神经网络

一、深度学习的简单概念深度学习是一种模仿人类大脑的运行方式&#xff0c;从大量数据中学习特征的学习模式。深度学习是机器学习的子集&#xff0c;它与机器学习的关系如下&#xff1a;二、感知神经网络2.1简单定义神经网络&#xff08;Neural Networks&#xff09;是一种模拟…

.NET 程序的强名称签名与安全防护技术干货

在 .NET 开发领域&#xff0c;保障程序的安全性和完整性至关重要。强名称签名和有效的安全防护措施是实现这一目标的关键手段。下面将详细介绍 .NET 程序的强名称签名以及相关的安全防护方法。一、什么是强名称签名强名称签名是 .NET 框架提供的一种安全机制&#xff0c;其主要…

DNS(Domain Name System,域名系统)

目录 **一、DNS的核心功能****二、DNS的工作原理****1. 解析流程(以车载导航请求为例)****2. 关键机制****三、车载以太网中DNS的特殊性**1. **高可靠性要求**2. **低延迟优化**3. **安全挑战与防护****四、DNS相关协议与技术****五、车载DNS配置示例****六、DNS故障排查工具…

优化 ECharts 多条折线:折线数据不完整导致的X轴日期错乱问题

目录 一、简单介绍 1.1 常见类型 二、时间轴错乱问题 2.1 示例 2.2 示例完整代码 2.3 问题分析 2.4 修复方法 第一步 第二步 2.5 优化后完整代码 一、简单介绍 ECharts 是一款基于 JavaScript 的数据可视化图表库&#xff0c;动态图表是 ECharts 的一个重要应用场景…

网络安全之注入攻击:原理、危害与防御之道

网络安全之注入攻击&#xff1a;原理、危害与防御之道 引言 在OWASP Top 10安全风险榜单中&#xff0c;注入攻击常年占据首位。2023年Verizon数据泄露调查报告显示&#xff0c;67%的Web应用漏洞与注入类攻击直接相关。本文从技术视角系统解析注入攻击的核心原理、典型场景及防御…

Python爬虫动态IP代理报错全解析:从问题定位到实战优化

目录 一、代理IP失效&#xff1a;爬虫的"隐形杀手" 1.1 失效场景复现 1.2 解决方案 二、403封禁&#xff1a;反爬机制的"精准打击" 2.1 封禁原理剖析 2.2 破解方案 三、速度瓶颈&#xff1a;代理性能的"致命短板" 3.1 性能对比测试 3.2…

机器学习基础知识【 激活函数、损失函数、优化器、 正则化、调度器、指标函数】

目录标题机器学习基础知识概览激活函数 (Activation Functions)损失函数 (Loss Functions / Cost Functions)优化器 (Optimizers)正则化 (Regularization)调度器 (Schedulers / Learning Rate Schedulers)指标函数 (Metric Functions)其他重要概念训练流程机器学习基础知识概览…

【达梦数据库|JPA】后端数据库国产化迁移记录

项目背景 经典的springbootjpa&#xff0c;java1.8数据库MySQL需要迁移到国产化数据库达梦上 开发环境安装 最简单的方式&#xff1a; 官方网站下载安装时选择“典型安装”即可 Linux安装 国产化一律上docer不要犹豫 下载三方提供的docker镜像按页面文档启动即可同上下载官…

ubuntu22默认安装firefox使用snap安装还老打不开解决办法

终极解决方案&#xff08;100% 避免 Snap 版 Firefox&#xff09; 步骤 1&#xff1a;彻底移除 Snap 版 Firefox bash sudo snap remove --purge firefox 步骤 2&#xff1a;添加 Mozilla 官方 PPA&#xff08;提供 .deb 版 Firefox&#xff09; bash sudo add-apt-repository …

MyBatis02-mybatis-config.xml配置文件讲解

mybatis-config.xml 是 MyBatis 的核心配置文件&#xff0c;用于配置整个 MyBatis 框架的全局行为&#xff0c;比如环境&#xff08;数据源&#xff09;、事务、类型别名、插件、Mapper 映射等。示例&#xff1a;<?xml version"1.0" encoding"UTF-8" ?…

合上电脑不关机

在Debian 系统上&#xff0c;如何实现合上电脑不关机的效果&#xff1f; 可以修改配置文件&#xff1a; sudo vim /etc/systemd/logind.conf1.找到 HandleLidSwitch &#xff0c;将其值改为 ignore &#xff08;处理盖子开关为忽略&#xff09; 2.将 LidSwitchIgnoreInhibited …

服务器深夜告警?可能是攻击前兆!

凌晨三点&#xff0c;刺耳的告警铃声把你从梦中惊醒&#xff1a;服务器CPU 100%&#xff0c;内存耗尽&#xff01;你手忙脚乱地登录服务器&#xff0c;发现某个进程疯狂占用资源。是程序Bug&#xff1f;还是业务突增&#xff1f;排查半天&#xff0c;最后在角落的日志里发现蛛丝…

重学前端003 --- CSS 颜色

文章目录文档声明head颜色模型div根据在这里 Freecodecamp 实践&#xff0c;记录笔记总结。 文档声明 在文档顶部添加 DOCTYPE html 声明 <!DOCTYPE html>head title 元素为搜索引擎提供了有关页面的额外信息。 它还通过以下两种方式显示 title 元素的内容&#xff1a…

学弟让我帮忙写一个学生管理系统的后端,我直接上科技

&#x1f4dd;个人主页&#xff1a;哈__ 期待您的关注 目录 一、飞算AI简介 二、系统开发 2.1 需求提出 2.2 系统模块的设计 2.3 数据库表格设计 2.4 接口规范设计 2.5 源码生成 三、总结 学弟这两天有一个小组合作的任务&#xff0c;应该是培训吧要写一个学生管理…

《P3038 [USACO11DEC] Grass Planting G》

题目描述 给出一棵有 n 个节点的树&#xff0c;有 m 个如下所示的操作&#xff1a; 将两个节点之间的 路径上的边 的权值均加一。 查询两个节点之间的 那一条边 的权值&#xff0c;保证两个节点直接相连。 初始边权均为 0。 输入格式 第一行两个整数 n,m&#xff0c;含义…

NestJS

文章的地址 NestJShttps://equinox-primrose-ceb.notion.site/NestJS-22d4b8031e0f80b39fc7fe1ff111f802 不产生测试的.spec.ts文件的配置 "generateOptions": {"spec": false }创建模型 nest g m xx 创建服务 nest g s xx 创建处理 nest g c xx CRU…

vue入门学习教程

一、介绍 vue是一款用于构建用户界面的 JavaScript 框架。基于标准 HTML、CSS 和 JavaScript 构建&#xff0c;并提供了一套声明式的、组件化的编程模型&#xff0c;帮助你高效地开发用户界面。 二、使用和安装 方法1&#xff1a;在html代码中直接使用<script>导入&…

C++类对象多态基础语法【超详细】

文章目录前言1. 虚函数1.1 现象1.2 多态1.3 析构函数1.4 override和final1.5 重载、隐藏、重写对比2. 抽象类2.1 抽象类特性2.2 抽象类的应用场景3. 多态实现的底层原理4. 静态绑定和动态绑定5. 总结前言 多态是面向对象三大特性之一&#xff0c;也是细节最多的语法之一。学习…

Flask 入门到实战(3):用 SQLAlchemy 优雅操作数据库

深入理解 Flask ORM&#xff1a;用 SQLAlchemy 优雅操作数据库一、前言&#xff1a;什么是 ORM&#xff1f;为什么要用它&#xff1f; 传统数据库操作要写 SQL&#xff0c;比如&#xff1a; SELECT * FROM users WHERE id 1;而使用 ORM 后&#xff0c;你可以这样写&#xff1a…

源表=电源+数字表?一文看懂SMU源表 2025-04-14

源表(Source Meter Unit, SMU)广泛用于半导体器件、材料、医疗、发光器件与光通信等行业,测量器件的伏安(I-V)特性曲线、绝缘材料的电阻值(电阻率)、电容的绝缘电阻(漏电流)、光电器件的暗电流或者L-I-V等。 源表的名称已经清晰的告诉我们,它包含了高精度电源输出和…