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

news2024/11/17 13:55:31

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.coloradmin.cn/o/1642918.html

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

相关文章

Docker高频使用命令

一、Docker常用命令总结 1.镜像命令管理 指令描述ls列出镜像build构建镜像来自Dockerfilehoistory查看历史镜像inspect显示一个或多个镜像的详细信息pull从镜像仓库拉取镜像push推送一个镜像仓库rm移除一个或多个镜像prune一处未使用的镜像,没有被标记或被任何容器…

上位机图像处理和嵌入式模块部署(树莓派4b和qt应用全屏占有)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 我们都知道,嵌入式应用一般都是为了某一个特定应用而存在的。也就是说,和pc不同,这个嵌入式板子一般都是为了解…

Java之LinkedHashMap

系列文章目录 文章目录 系列文章目录前言前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 LinkedHashMap是Map接口的哈希表和链接列表实现,具有可预知的迭代顺序。…

【数学建模】矩阵微分方程

一、说明 我相信你们中的许多人都熟悉微分方程,或者至少知道它们。微分方程是数学中最重要的概念之一,也许最著名的微分方程是布莱克-斯科尔斯方程,它控制着任何股票价格。 ​​ 股票价格的布莱克-斯科尔斯模型 微分方程可以由数学中的许多…

正在载入qrc文件 指定的qrc文件无法找到。您想更新这个文件的位置么?

打开Qt的ui文件&#xff0c;弹出提示框 如果需要用到qrc文件&#xff0c;选择Yes&#xff0c;再选择qrc文件所在的位置&#xff1b;如果不需要qrc文件&#xff0c;可以选择No&#xff0c;然后用普通文本编辑器打开&#xff0c;将“ <resources> <include location&q…

去哪儿网机票服务请求体bella值逆向

作者声明&#xff1a;文章仅供学习交流与参考&#xff01;严禁用于任何商业与非法用途&#xff01;否则由此产生的一切后果均与作者无关&#xff01;如有侵权&#xff0c;请联系作者本人进行删除&#xff01; 一、加密定位 直接全局搜索bella&#xff0c;在可疑的地方下断&…

2024/5/5 英语每日一段

Meanwhile, in a twist, Tesla this month settled a high-profile case in Northern California that claimed Autopilot played a role in the fatal crash of an Apple engineer, Walter Huang. The company’s decision to settle with Huang’s family—along with a ruli…

数据结构学习/复习7--栈的实现/括号匹配/队列的实现/两个队列实现栈

一、栈 1.概念及性质 2.栈的实现(top0版) 注意事项&#xff1a;top也可初始为-1,代码需要调整 二、栈练习 1.括号匹配 三、队列 1.概念及性质 2.队列的实现 四、队列练习 1.两个队列实现栈

2024年 Java 面试八股文——SpringBoot篇

目录 1. 什么是 Spring Boot&#xff1f; 2. 为什么要用SpringBoot 3. SpringBoot与SpringCloud 区别 4. Spring Boot 有哪些优点&#xff1f; 5. Spring Boot 的核心注解是哪个&#xff1f;它主要由哪几个注解组成的&#xff1f; 6. Spring Boot 支持哪些日志框架&#…

10个使用NumPy就可以进行的图像处理步骤

图像处理是一种数学计算。数字图像由称为像素的彩色小点组成。每个像素由红、绿、蓝(RGB)三个独立的颜色组成。每个像素中的主色由每个RGB分量的数值决定。 本文将介绍10个使用使用NumPy就可以进行的图像处理步骤&#xff0c;虽然有更强大的图像处理库&#xff0c;但是这些简单…

数据结构-二叉树结尾+排序

一、二叉树结尾 1、如何判断一棵树是完全二叉树。 我们可以使用层序遍历的思路&#xff0c;利用一个队列&#xff0c;去完成层序遍历&#xff0c;但是这里会有些许的不同&#xff0c;我们需要让空也进队列。如果队列里到最后只剩下空那么这棵树就是完全二叉树。具体的实现如下…

【Flask 系统教程 2】路由的使用

Flask 是一个轻量级的 Python Web 框架&#xff0c;其简洁的设计使得构建 Web 应用变得轻而易举。其中&#xff0c;路由是 Flask 中至关重要的一部分&#xff0c;它定义了 URL 与视图函数之间的映射关系&#xff0c;决定了用户请求的处理方式。在本文中&#xff0c;我们将深入探…

关于安装Tensorflow的一些操作及问题解决

关于conda和tensorflow&#xff1a; 由于在安装tensorflow遇到各种问题&#xff0c;遇坑则进&#xff0c;耗费了很多时间。由此想整理一些关于安装tensorflow的操作和方法。欢迎各位补充和指正&#xff01; 1.conda: 1&#xff09;conda list 查看安装了哪些包。 2&#xff…

OpenCV人脸识别C++代码实现Demo

OpenCV&#xff08;Open Source Computer Vision Library&#xff09;是一个开源的计算机视觉库&#xff0c;它提供了很多函数&#xff0c;这些函数非常高效地实现了计算机视觉算法。 官网&#xff1a;https://opencv.org/ Github: https://github.com/opencv/opencv Gitcode…

如何打包Apk适配32和64位

一个表格了解lib下的文件夹 .so文件描述armeabi-v7a第七代及以上的ARM处理器&#xff0c;2011年以后生产的大部分Android设备都使用。arm64-v8a第8代、64位ARM处理器&#xff0c;很少设备&#xff0c;三星GalaxyS6是其中之一。armeabi第5代、第6代的ARM处理器&#xff0c;早期…

C语言数据结构之队列

目录 1.队列的概念及结构2.队列的实现逻辑3.队列的代码实现4.相关例题选择题 •͈ᴗ•͈ 个人主页&#xff1a;御翮 •͈ᴗ•͈ 个人专栏&#xff1a;C语言数据结构 •͈ᴗ•͈ 欢迎大家关注和订阅!!! 1.队列的概念及结构 队列&#xff1a;只允许在一端进行插入数据操作&#x…

Ubuntu系统安装nvfortran详细步骤【笔记】

实践设备&#xff1a;华硕FX-PRO&#xff08;NVIDIA GeForce GTX 960M&#xff09; Ubuntu系统安装NVFORTRAN&#xff08;NVIDIA Fortran Compiler&#xff09;步骤如下&#xff1a; 安装依赖项&#xff1a;在安装NVFORTRAN之前&#xff0c;你需要确保系统已经安装了一些必要…

IoTDB 入门教程 基础篇①——时序数据库为什么选IoTDB ?

文章目录 一、前文二、性能排行第一三、完全开源四、数据文件TsFile五、乱序数据高写入六、其他七、参考 一、前文 IoTDB入门教程——导读 关注博主的同学都知道&#xff0c;博主在物联网领域深耕多年。 时序数据库&#xff0c;博主已经用过很多&#xff0c;从最早的InfluxDB&a…

c语言刷题——输出图案

1.输出用“*”组成的X形图案 题目&#xff1a;请打印用“*”组成的X形图案 描述&#xff1a; 多组输入&#xff0c;一个整数&#xff08;2~20&#xff09;&#xff0c;表示输出的行数&#xff0c;也表示组成“X”的反斜线和正斜线的长度。 输出描述&#xff1a; 针对每行输…

一觉醒来 AI科技圈发生的大小事儿 05月04日

&#x1f4f3;CVPR 2024 Highlight | 基于单曝光压缩成像&#xff0c;不依赖生成模型也能从单张图像中重建三维场景 本文介绍了一种基于单曝光压缩成像&#xff08;SCI&#xff09;系统和神经辐射场&#xff08;NeRF&#xff09;的三维场景拍摄与重建方法&#xff0c;实现了不…