深入学习Redis(1):Redis内存模型

Redis的五个对象类型

字符串,哈希,列表,集合,有序集合

本节有关redis的内存模型

1.估算redis的内存使用情况

目前内存的价格比较的高,如果对于redis的内存使用情况能够进行计算,就可以选用合适的设备进行使用,可以有效的节省开支

2.优化内存占用

对于redis,选用合适的数据类型和编码,这样才能更好的利用redis现有的内存

3.分析解决问题

对于redis出现阻塞,内存占用等问题的时候,能够很快发现问题并解决

127.0.0.1:6379> info memory
# Memory
used_memory:1324768
used_memory_human:1.26M
used_memory_rss:11939840
used_memory_rss_human:11.39M
mem_fragmentation_ratio:9.30
mem_allocator:jemalloc-5.2.1

used_memory是redis分配器分配的内存总量,其中包括虚拟内存swap

used_memory_rss是redis内存占据操作系统的内存,和top/ps一致,包括内存和内存碎片

used_memory_human这个对于阅读比较的好

对于mem_fragmentation_ratio这个值来说的话,他是对于used_memory_rss/used_memory的比值,可以发现这个等式,如果redis启用了虚拟内存swap的话,这个比值就可能变成小于1,这个时候就需要对于redis进行问题排查,应为磁盘太慢了,对于内存增加的方法有,增加redis节点,对于redis的服务器内存进行增加,优化应用,也就是分为横向和纵向的区别

如果内存中并未存入什么数据,那么就会造成这个比值过大

mem_allocator这个参数是指当前使用的内存分配器的版本

Redis内存的划分

1.数据

数据是有内存分配器分配的内存,会被统计在used_memory中

五种类型,分别是字符串,哈希,列表,集合,有序集合,这五种类型是对外提供的,当然,在内部还有基数统计,位图,地理位置,对于对象需要进行redisObject,SDS的包装,才会被放到内存中去

2.进程本身需要的内存

redis本身的代码,例如代码,常量池,这些占据了几兆,这点空间可以忽略不计,然后应为不经过jemalloc的分配,所以不被记录到used_memory中

3.缓冲内存

缓冲内存包括客户端的缓冲区,复制积压缓冲区,AOF缓冲区等,其中客户端缓存客户端连接输入输出缓存;复制积压缓冲区用户部分复制功能,AOF缓冲区对于AOF进行重写,保存最近的写入命令。这部分是由jemalloc来分配的,所以记录到used_memory当中

4.内存碎片

对于数据进行频繁修改之后,数据之间的大小相差就很大,导致redis释放得空间在实际得物理内存中并没有释放,redis也无法合理利用,导致了内存碎片。内存碎片不会统计到used_memory当中。

内存碎片的产生是多方面的,和对于内存的操作,数据的特点等都有关,内存分配器也有关系,设计的好,内存碎片产生的也少,jemalloc在这方面做的很好

redis中的内存碎片很大的时候,可以进行安全重启,在重启之后,redis可以从备份的文件中读取数据,在内存中进行重排,为每个数据进行选择合适的内存单元,减小内存的碎片

redis数据的存储细节

前面提到,对于数据我们是需要对他进行封装,包装成redisObject,或者SDS

在这张图片中,可以看到dictEntry是基本的单位,每一个键值对都有一个dictEntry,然后就里面也有一个这样的指针,然后就指向过去,里面的key可以看到是存储在SDS结构中,然后就是值,这个值不是放在sds里面的,是存储在redisObject里面,然后type字段表面这是sds类型,然后ptr指针指向了sds对象所在的地址,里面存放着值,值也是需要sds结构进行存储的

之后就是jemalloc对于上面说的dictEntry对象、redisObject、SDS对象都进行了内存的分配,dictEntry这个对象有三个指针,也就是3*8字节为24字节,然后就向上取整,给他分配了一个32字节大小的内存单元

jemalloc

在redis对于内存分配器进行选择的时候,内存分配器有libc,jemalloc,tcmalloc,三个,当然默认的是jemalloc,这一个在64位系统中,对于内存空间进行了小中大的三个范围的划分,当然,在每一个范围内又进行了更加细致的划分,

就像这里如果需要存储一个字节的对象,那么就需要字节的内存单元中

redisObject

在redis中有五种对象,现在有八种,不管哪一种,redis都不会进行直接存储,都会对于redisObject对象进行存储

redisObject对象中包含了redis对象的类型,内部编码,内存回收,共享对象等功能,都需要redisObject的支持,

typedef struct redisObject {unsigned type:4;unsigned encoding:4;unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */int refcount;void *ptr;
} robj;

type,对象的类型,也就是那八个

直接使用type的命令

encoding 在字符串中有三种编码 int emmbstr raw

对于列表对象来说,元素少的时候,就是int型,如果元素多的话,会抓换成embstr类型

int是压缩列表,embstr是双端链表

lru记录的是对象最后一次被程序访问的时间,如果你想看,可以使用object idletime命令进行查看

这条指令并不会修改lru

lru和redis的内存回收算法是有关系的,如果是redis打开了maxmemory选项,那么内存回收算法选择的是volatile-lru或allkeys-lru,这两种算法,每当redis的内存超过maxmemory的时候,redis就会优先选择lru的空转时间最长的对象进行释放

还有一个refcount

这个东西是和共享对象有关的,新对象创建的时候,初始话为1,之后如果有新程序使用了该对象,这个refcount+1没使用了就-1,如果refcount=0的时候,说明对象占用的内存会被释放

redis目前的共享对象支持的只有整数值的字符串对象,但是其他八种都可能使用共享对象

这里要注意,本篇文章是redis3.0那个时候还是五种,这里的八种我认为是可以延伸的,如果有不正确的,请指正

哈希,列表可以使用这种整数值的字符串对象

一般来说会初始化10000个字符串对象,分别是0~9999,如果你使用的话,就是使用共享对象了

最新版好像是一个好大好大的值

这一万个数字是可以进行调整的,可以自己百度下自己的版本怎么调整

之后就只剩下一个ptr了

ptr就是指向具体的数据了,这里可以认为指向的就是SDS结构,这个ptr的大小个系统有关

redis的所占大小就可以算了type是4个比特,encoding也是4个比特,然后就是lru,这个就看你的版本了,4.0是24比特,3.0是22比特,然后就是refcount一个int,4个字节,然后就剩下一个ptr指针了,现在都是64位的系统,所以都是8个字节,然后算一算就是16字节

接下来就是SDS结构了

SDS是简单动态字符串(Simple Dynamic String)

struct sdshdr {int len;int free;char buf[];
};

buf数组用来存放字符串的,len就是已经使用的长度,free就是还没有使用的长度

buf数组的长度就可以通过len+buf+1应为字符串都是末尾来个空字符'\0'来表示结束的

sds的优点为,可以在O(1)的情况下查找长度,获取字符串长度是O(1)的,这里我就想到了go语言中的切片slice了,里面也存在着cap最大容量上限

然后再缓冲区溢出方面,直接使用C字符串的api会导致溢出,如果字符串的长度增加的话,你没有分配内存就会导致缓冲区的溢出,这里的sds记录了长度,在对应的api造成缓冲区溢出的时候,可以自动分配内存,防止了缓冲区的溢出

在修改字符串内存方面的重分配,如果是C字符串的话,需要重新分配,也就是释放再重新申请,如果,字符串长度增大会导致内存缓冲区溢出,字符串长度减少时会造成内存泄漏。对于sds来说,应为有了len 和free,空间预分配之后,字符串长度增大在重新分配的概率就小很多,应为一般我们都会分配多一点内存给他们,在内存释放方面,对于值进行修改之后,直接修改len和free就可以了,所以也很方便

对于二进制数据来说,C字符串不可以存,但是sds是可以进行存储的,应为二进制数据中可能有'\0'这种,但是sds中有len这个长度,所以可以规避这个错误

如果sds里面存储的是文本数据时可以使用C字符串的函数,但是二进制数据不行,应为这个可能是以'\0'数据也可能在数据里面,会出错

在打印日志以及不更改字符串的情况下,才会使用C字符串,不然一般都是使用sds结构体

转换过程是不可逆的

只能是小内存编码向大内存编码进行转换

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

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

相关文章

基于MSOGI的交叉对消谐波信号提取网络MATLAB仿真

微❤关注“电气仔推送”获得资料(专享优惠) 模型简介: 此模型利用二阶广义积分器(SOGI)对基波电流和相应次的谐波电流进行取 ,具体是通过多个基于二阶广义积分器的正交信号发生器 ( S&#xf…

JavaScript入门:用JS点亮你的第 1 个网页圣诞树!

你好,我是云桃桃。 一个希望帮助更多朋友快速入门 WEB 前端的程序媛。 云桃桃-大专生,一枚程序媛,感谢关注。回复 “前端基础题”,可免费获得前端基础 100 题汇总,回复 “前端工具”,可获取 Web 开发工具合…

JVM笔记1--Java内存区域

1、运行时数据区域 从上图可以看出来,Java虚拟机运行时数据区域整体上可以分成5大块: 1.1、程序计数器 程序计数器是一块较小的内存空间。它可以看做当前线程所执行的字节码的行号指示器。在Java虚拟机的概念模型里,字节码解释器工作时就是…

vue实现滚动条联动(一个滚动条控制两个或多个)

两个表格需要进行比对,两个表格是互相独立的,如果滚动条不能同步,用户就要操作两个两次,体验不是太好,如下图: 因此想使两个滚动条同步,思路如下: 给两个表格定义ref(便…

环形链表的判断方法与原理证明

(题目来源:力扣) 一.判读一个链表是否是环形链表 题目: 解答: 方法:快慢指针法 内容:分别定义快慢指针(fast和slow),快指针一次走两步,慢指…

机器学习的指标评价

之前在学校的小发明制作中,在终期答辩的时候,虽然整个项目的流程都答的很流畅。 在老师提问的过程中,当老师问我recall,precision,accuracy等指标是如何计算的,又能够表示模型的哪方面指标做得好。我听到这个问题的时候&#xff…

如何选购骨传导耳机?精选五大拔尖宝藏骨传导耳机,闭眼入也不踩雷!

尽管目前市面上的骨传导耳机热度非常高,一度成为当下最热门的耳机款式,但作为有着资深工作经验的数码测评师,我仍然要提醒大家:在选择骨传导耳机的时候,不要盲目选择网红品牌,因为市场上的许多骨传导耳机过…

用LM Studio搭建微软的PHI3小型语言模型

什么是 Microsoft Phi-3 小语言模型? 微软Phi-3 模型是目前功能最强大、最具成本效益的小型语言模型 (SLM),在各种语言、推理、编码和数学基准测试中优于相同大小和更高大小的模型。此版本扩展了客户高质量模型的选择范围&#x…

golang判断通道chan是否关闭的2种方式

chan通道在go语言的办法编程中使用频繁,我们可以通过以下2种方式来判断channel通道是否已经关闭,1是使用 for range循环,另外是通过 for循环中if 简短语句的 逗号 ok 模式来判断。 示例代码如下: //方式1 通过for range形式判断…

现代循环神经网络(GRU、LSTM)(Pytorch 14)

一 简介 前一章中我们介绍了循环神经网络的基础知识,这种网络 可以更好地处理序列数据。我们在文本数据上实现 了基于循环神经网络的语言模型,但是对于当今各种各样的序列学习问题,这些技术可能并不够用。 例如,循环神经网络在…

centos7 openresty lua 自适应webp和缩放图片

目录 背景效果图准备安装cwebp等命令,转换文件格式安装ImageMagick,压缩文件下载Lua API 操控ImageMagick的依赖包 代码参考 背景 缩小图片体积,提升加载速度,节省流量。 效果图 参数格式 : ?image_processformat,…

PyVista 3D数据可视化 Python 库 简介 含源码

Pyvista是一个用于科学可视化和分析的Python库 ;我认为它适合做一些网格数据的处理; 它封装了VTK(Visualization Toolkit)之上,提供了一些高级接口, 3D数据可视化变得更加简单和易用。 1.安装 pyvista&…

蓝桥杯-路径之谜

题目描述 小明冒充X星球的骑士,进入了一个奇怪的城堡。城堡里面什么都没有,只有方形石头铺成的地面。 假设城堡的地面时n*n个方格。如下图所示。 按习俗,骑士要从西北角走到东南角。可以横向或者纵向移动,但是不能斜着走&#x…

【设计模式】函数式编程范式工厂模式(Factory Method Pattern)

目录标题 定义函数式接口函数式接口实现类工厂类封装实际应用总结 定义函数式接口 ISellIPad.java /*** 定义一个函数式接口* param <T>*/ FunctionalInterface public interface ISellIPad<T> {T getSellIPadInfo();}函数式接口实现类 HuaWeiSellIPad.java pu…

JAVA系列 小白入门参考资料 接口

目录 接口 接口的概念 语法 接口使用 接口实现用例 接口特性 实现多个接口和实现用例 接口间的继承 接口 接口的概念 在现实生活中&#xff0c;接口的例子比比皆是&#xff0c;比如&#xff1a;笔记本上的 USB 口&#xff0c;电源插座等。 电脑的 USB 口上&am…

初识C语言——第九天

ASCII定义 在 C 语言中&#xff0c;每个字符都对应一个 ASCII 码。ASCII 码是一个字符集&#xff0c;它定义了许多常用的字符对应的数字编码。这些编码可以表示为整数&#xff0c;也可以表示为字符类型。在 C 语言中&#xff0c;字符类型被定义为一个整数类型&#xff0c;它占…

WPF之绑定验证(错误模板使用)

1&#xff0c;前言&#xff1a; 默认情况下&#xff0c;WPF XAML 中使用的绑定并未开启绑定验证&#xff0c;这样导致用户在UI上对绑定的属性进行赋值时即使因不符合规范内部已抛出异常&#xff08;此情况仅限WPF中的数据绑定操作&#xff09;&#xff0c;也被程序默认忽略&…

C#调用skiasharp操作并绘制图片

之前学习ViewFaceCore时采用Panel控件和GDI将图片及识别出的人脸方框和关键点绘制出来&#xff0c;本文将其修改为基于SKControl和SKCanvas实现相同的显示效果并支持保存为本地图片。   新建Winform项目&#xff0c;在Nuget包管理器中搜索并安装一下SkiaSharp和ViewFaceCore…

AD单通道/多通道

1.AD单通道&#xff08;单次转换&#xff0c;非扫描模式&#xff09; 1.1 接线图 1.2 AD.c #include "stm32f10x.h" // Device headervoid AD_Init(void) {RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启GPIOA时钟RCC_APB2PeriphCl…

node.js 解析post请求 方法二

前提&#xff1a;以前面发的node.js解析post请求方法一为模板&#xff0c;具体见 http://t.csdnimg.cn/ABaIn 此文我们运用第二种方法&#xff1a;使用第三方模块formidable对post请求进行解析。 1》代码难点 *** 在Node.js中使用formidable模块来解析POST请求主要涉及到处理…