redis设计与实现读书笔记

news2025/2/26 3:03:17

这里主要记录一下在阅读redis设计与实现中碰到的一些没有记录过的知识。

引用计数技术

Redis的对象系统实现了基于引用计数技术的内存回收机制,当程序不再使用某个对象的时候,这个对象所占用的内存就会被自动释放;另外,Redis还通过引用计数技术实现了对象共享机制,这一机制可以在适当的条件下,通过让多个数据库键共享同一个对象来节约内存。

关于引用计数技术:当一个程序开始使用这个对象时,它会将这个对象的引用计数加1;而当一个程序抛弃该对象时,它会将这个对象的引用计数减1。当某个对象的引用计数降至0时,Redis会自动回收该对象占用的内存空间。

关于redis的实现:每个redisObject对象在创建时都会初始化一个refcount属性,用来记录该对象被引用的次数。

关于redis通过引用计数技术实现了对象共享机制和垃圾回收机制

Redis通过引用计数技术和对象共享机制来节约内存空间。当多个Redis客户端使用相同的字符串值时,Redis会将它们的redisObject对象合并成一个,并且他们引用计数属性会自增以记录所有这些客户端都在使用这个对象。

具体地说,Redis的共享机制是通过字典结构实现的,Redis内部维护了一个字符串对象池(stringpool),里面保存着所有已经创建的字符串对象。当一个新的字符串被创建时,Redis首先检查stringpool中是否已经存在该字符串,如果存在,那么就直接返回这个对象的引用,并且将它的引用计数加1;如果不存在,则创建一个新的redisObject对象,并将该对象加入到stringpool中,同时返回该对象的引用。

对象共享机制的优点

这种实现方式能够显著减少Redis的内存占用,因为许多大量出现的字符串只需要被存储一次,并且可以被多个客户端共享使用。同时,由于引用计数技术的存在,只有当所有的客户端都不再使用某个共享对象时,Redis才会自动回收该对象所占用的内存空间。

redis对象系统介绍

对象的类型与编码

Redis 中的每个对象都由一个redis0bject结构表示,该结构中和保存数据有关的三个属性分别是type属性、encoding属性和ptr属性:

type属性:记录了对象的类型。

为下图中类型的一个

对于Redis数据库保存的键值对来说,键总是一个字符串对象,而值则可以是字符串对象、列表对象、哈希对象、集合对象或者有序集合对象的其中一种。

对象的ptr指针指向对象的底层实现数据结构,而这些数据结构由对象的encoding属性决定。 

encoding属性:记录了对象所使用的编码,也就是说这个对象使用了什么数据结构作为对象的底层实现。

encording属性的作用

通过encoding属性来设定对象所使用的编码,而不是为特定类型的对象关联一种固定的编码,极大地提升了Redis 的灵活性和效率,因为Redis可以根据不同的使用场景来为一个对象设置不同的编码,从而优化对象在某一场景下的效率。

 这就是encording属性的作用,可以方便的更改对象的底层数据结构。

字符串对象

首先,字符串对象可以使用的编码有三种int、 raw或者embstro。

int:如果一个字符串对象保存的是整数值,并且这个整数值可以用long类型来表示,就会使用int编码也就是以整数类型存储。

raw:如果字符串对象保存的是一个字符串值,并且这个字符串值的长度大于32字节,那么字符串对象将使用一个简单动态字符串(SDS)来保存这个字符串值。

embsto:如果字符串对象保存的是一个字符串值,并且这个字符串值的长度小于等于32字节,那么字符串对象将使用embstr编码的方式来保存这个字符串值。

embsto与raw的区别

 embstr编码是专门用于保存短字符串的一种优化编码方式,其与raw一样也是一个redisObject和sdshdr结构来表示字符串对象,区别在于,raw编码会调用两次内存分配函数来创建redisObject和sdshdr,而embstr通过一次内存分配函数来分配一块连续的空间依次包含redisObject和sdshdr。

对于是embstr编码还是raw编码的字符串对象来说,执行命令时产生的效果是相同的,但使用embstr编码字符串对象来保存短字符串值有以下好处:

 但是在长字符串的情况下,这种连续的内存结构,就会因为要为每个字符串值单独分配一块内存,这种情况导致内存浪费,所以在长字符串的情况下就会使用raw编码。 

对于redis来说,浮点数的保存也是以字符串来进行保存的。

编码转换

int编码的字符串对象和embstr编码的字符串对象在条件满足的情况下,会被转换为raw编码的字符串对象。

int变为字符串

对于这个对象保存的值进行修改让这个对象保存的值不再是整数,就会让int变成字符串。

embstr变成raw

这个主要是在修改embstr的时候会发生这种情况,因为在embstr编码的字符串对象实际上是只读的,也就是说对于embstr编码的对象来说,它没有修改程序,所以在对embstr编码的对象进行数据修改的时候一般来说是先进行转换出raw之后,才会进行修改。

列表对象

编码类型,ziplist或者linkedlist

ziplist底层是用压缩链表,linkedlist底层是双向链表。

不在这两个条件的情况下会变成linkedlist编码。

哈希对象

编码类型:ziplist或者hashtable。

ziplist底层是压缩链表,hashtable底层是dict

 不在这两个条件的情况下会变成hashtable编码。

集合对象

集合对象的编码可以是intset或者hashtable。

intset的底层是整数集合,集合对象的所有元素,都被包含在整数集合里面,hashtable的底层是dict,每一个键都是一个字符串对象,对象里包含了一个集合元素。值全部设置为null.

有序集合对象

有序集合的编码可以是ziplist或者skiplist。

ziplist底层是压缩链表,skiplist是跳表加字典

这里具体介绍一下skiplist为什么要现在跳表加字典来作为底层,

在理论上,有序集合可以单独使用字典或者跳跃表的其中一种数据结构来实现,但无论单独使用字典还是跳跃表,在性能上对比起同时使用字典和跳跃表都会有所降低。举个例子,如果我们只使用字典来实现有序集合,那么虽然以O(1)复杂度查找成员的分值这一特性会被保留,但是,因为字典以无序的方式来保存集合元素,所以每次在执行范围型操作―—比如 ZRANK、ZRANGE等命令时,程序都需要对字典保存的所有元素进行排序,完成这种排序需要至少o(NlogN)时间复杂度,以及额外的o(N)内存空间(因为要创建一个数组来保存排序后的元素)。

 另一方面,如果我们只使用跳跃表来实现有序集合,那么跳跃表执行范围型操作的所有优点都会被保留,但因为没有了字典,所以根据成员查找分值这一操作的复杂度将从0(1)上升为o(logN)。因为以上原因,为了让有序集合的查找和范围型操作都尽可能快地执行,Redis 选择了同时使用字典和跳跃表两种数据结构来实现有序集合。

所以redis使用了跳表加字典来作为底层,在需要执行那个数据结构更擅长的方面时,使用那个数据结构的api.

然后,因为这两种数据结构都会通过指针来共享相同元素的成员和分值,所以不会有同一个数据被存储两次这种事情,也就不会用内存浪费。

类型检查机制

这个机制相当于redis在用户输入针对键的命令后,由服务器检查输入数据库键的值对象是否为执行命令需要的类型,如果是就会执行。然后这样又有一个问题,那就是对于很多对象都要两种编码类型,我们不会一直清楚这个对象现在是什么编码类型,所以这里就有了redis的命令多态。

多态命令

Redis除了会根据值对象的类型来判断键是否能够执行指定命令之外,还会根据值对象的编码方式,选择正确的命令实现代码来执行命令。

这两种机制的实现图如下

 对象的空转时长

redis0bject结构包含的最后一个属性为1ru属性,该属性记录了对象最后一次被命令程序访问的时间,当服务器占用内存数超过maxmemory选项所设置的上限值,空转时长长的会优先被回收。

关于共享对象那个事情,有一个问题就是Redis不共享包含宇符串的对象

因为当服务器考虑将一个共享对象设置为键的值对象时,程序需要先检查给定的共享对象和键想创建的目标对象是否完全相同,只有在共享对象和目标对象完全相同的情况下,程序才会将共享对象用作键的值对象,而一个共享对象保存的值越复杂,验证共享对象和目标对象是否相同所需的复杂度就会越高,消耗的CPU时间也会越多:

如果共享对象是保存整数值的字符串对象,那么验证操作的复杂度为O(1);如果共享对象是保存字符串值的字符串对象,那么验证操作的复杂度为O(N);如果共享对象是包含了多个值(或者对象的)对象,比如列表对象或者哈希对象,那么验证操作的复杂度将会是O(N)。

因此,尽管共享更复杂的对象可以节约更多的内存,但受到CPU时间的限制,Redis只对包含整数值的字符串对象进行共享。

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

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

相关文章

低调且强大--iVX低代码平台

iVX目录前言一、低代码那么多 为什么选择iVX?二、“拼”出来的低代码平台,真的好用吗?三、iVX与其他低代码有啥可比性?前言 首先我们应该明白自动编程突破可能是:领域内Mini LLM 现在的思路都是搞LLM,几乎像…

通俗举例讲解动态链接、静态链接

参考动态链接 - 知乎 加上我自己的理解,比较好懂,但可能在细节方面有偏差,但总体是一致的 静态链接的背景 静态链接使得不同的程序开发者和部门能够相对独立的开发和测试自己的程序模块,从某种意义上来讲大大促进了程序开发的效率&#xf…

NPC 也有了生命?当 ChatGPT 注入游戏你能想象吗

🍎道阻且长,行则将至。🍓 目录引言:西部世界元宇宙,还记得吗ChatGPT 的世界?下图就是一个 ChatGPT 小镇: 引言:西部世界 《西部世界》以一个虚构的游戏般的“西部世界”为背景&am…

springboot验证码生成及验证功能

1.easy-captcha工具包 生成验证码的方式有许多种,这里选择的是easy-captcha工具包。 github开原地址为:easy-captcha工具包 其支持Java图形验证码,支持gif、中文、算术等类型,可用于Java Web、JavaSE等项目。 2添加依赖 首先需…

SQL Server的死锁说明

死锁指南一、了解死锁二、检测并结束死锁2.1、可能死锁的资源三、处理死锁四、最大限度地减少死锁4.1、以相同的顺序访问对象4.2、避免事务中的用户交互4.3、保持交易简短且在一个批次中4.4、使用较低的隔离级别4.5、使用基于行版本控制的隔离级别4.6、使用绑定连接4.7、停止事…

【云原生|Docker】04-docker的资源限制

目录 前言 容器的生命周期 1. 容器的启动过程 2. 容器的生命周期 ​编辑 内存限制 1. 内存限制的相关参数 2. 内存限制方式 2.1 设置-m,--memory,不设置--memory-swap 2.2 设置-m,--memorya,--memory-swapb,且b >a 2.…

本地从0搭建Stable Diffusion WebUI及错误记录

从0开始搭建本地Stable Diffusion WebUI环境 一.环境配置 1.使用的电脑配置 系统Windows10处理器英特尔 i7内存24GB显卡NVIDIA GTX 1060(6GB) 2.镜像源 阿里云 清华大学 中国科技大学 3.电脑环境变量配置 我的电脑–属性–高级系统设置–系统属性(高级)–环境变量 新建…

spring框架注解

3.Spring有哪些常用注解呢? Spring常用注解 Web: Controller:组合注解(组合了Component注解),应用在MVC层(控制层)。 RestController:该注解为一个组合注解,相当于Con…

首个ChatGPT开发的应用上线;ChatMind思维导图工具;中文提示词大全;Copilot平替 | ShowMeAI日报

👀日报&周刊合集 | 🎡生产力工具与行业应用大全 | 🧡 点赞关注评论拜托啦! 🤖 『一本与众不同的AI绘本』ChatGPT 编写故事 Midjourney 绘制插图 作者的女儿特别喜欢迪士尼动画《海洋奇缘》里的主人公莫阿娜&#…

Mybatis分解式查询

目录 一、Mybatis一对多分解式查询 1. 新增持久层接口方法 2. 新增映射文件对应的标签 3. 新增测试方法 4. 运行效果 二、Mybatis一对一分解式查询 1. 新增持久层接口方法 2. 新增映射文件对应的标签 3. 新增测试方法 4. 运行效果 三、Mybatis延迟加载 1. 开启延迟加…

超实用的十个超级实用事半功倍的Python自动化脚本

一淘模板 56admin.com在日常的工作学习当中,我们总会遇到各式各样的问题,其中不少的问题都是一遍又一遍简单重复的操作,不妨直接用Python脚本来自动化处理,今天小编就给大家分享十个Python高级脚本,帮助我们减少无谓的…

【数据结构与算法】栈的实现(附源码)

目录 一.栈的概念和结构 二.接口实现 A.初始化 Stackinit 销毁 Stackdestroy 1.Stackinit 2.Stackdestroy B.插入 Stackpush 删除 Stackpop 1.Stackpush 2.Stackpop C.出栈 Stacktop D. 栈的有效元素 Stacksize 判空 Stackempty 1.Stacksize 2.Stackempty …

Flink进阶篇-CDC 原理、实践和优化采集到Doris中

简介 基于doris官方用doris构建实时仓库的思路,从flinkcdc到doris实时数仓的实践。 原文 Apache Flink X Apache Doris 构建极速易用的实时数仓架构 (qq.com) 前提-Flink CDC 原理、实践和优化 CDC 是什么 CDC 是变更数据捕获(Change Data Captur…

Spring《三》DI依赖注入

🍎道阻且长,行则将至。🍓 上一篇:Spring《二》bean的实例化与生命周期 下一篇:敬请期待 目录一、setter注入🍉1.注入引用数据类型2.注入简单数据类型二、构造器注入🍊1.注入引用数据类型2.简单数…

数据挖掘(2.3)--数据预处理

目录 三、数据集成和转换 1.数据集成 2.数据冗余性 2.1 皮尔森相关系数 2.2卡方检验 3.数据转换 四、数据的规约和变换 1.数据归约 2数据离散化 三、数据集成和转换 1.数据集成 数据集成是将不同来源的数据整合并一致地存储起来的过程。 不同来源的数据可能有不同…

Qt优秀开源项目之十七:QtPromise

QtPromise是Promises/A规范的Qt/C实现。该规范的译文见附录。 QtPromise基于Qt5.6及以上版本,当然也包括Qt6。 github地址:https://github.com/simonbrunel/qtpromise 新手导航:Getting Started | QtPromise API手册:API Referenc…

详解Spring、SpringBoot、SpringCloud三者的联系与区别

一、Spring二、Spring Boot三、Spring Cloud四、三者的关系一、Spring Spring 是一个轻量级的Java 开发框架,主要依存于SSM 框架,即Spring MVC Spring Mybatis,定位很明确,Spring MVC主要负责view 层的显示,Spring …

Scala 一文搞定

第一节:概述为什么学习Scala ?Apache Spark 是专为大规模数据快速实时处理的计算引擎/内存级大数据计算框架。Apache Spark 是由Scala 语言编写。Scala 与Java 关系总结三点:java 编译器与Scala 编译器可以相互使用。Java SDK 的类库可以被Scala使用,Sc…

理解什么是sql注入攻击 + xss攻击 + cors 攻击

SQL注入 SQL注入就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。 SQL注入攻击的总体思路: 寻找到SQL注入的位置判断服务器类型和后台数据库类型针对不同的服务器和数据库特点进行SQL注入攻击 SQL注入…

Cookie和Session的工作流程及区别(附代码案例)

目录 一、 HTTP协议 1.1 为什么HTTP协议是无状态的? 1.2 在HTTP协议中流式传输和分块传输编码的区别 二、Cookie和Session 2.1 Cookie 2.2 Session 2.3 Cookie和Session的区别 三、servlet中与Cookie和Session相关的API 3.1 HttpServletRequest 类中的相关方…