当前位置: 首页 > news >正文

Redis优化内存篇

【内存消耗】
场景:业务ID->图片ID(KV:partnerId->objectId)。
刚开始,我们保存了1亿张图片,大约用了6.4GB的内存。
随着图片数据量的不断增加,Redis变慢了。
新的认知:String类型并不是适用于所有场合的,它有一个明显的短板,就是它保存数据时所消耗的内存空间较多。
新的解法:集合类型有非常节省内存空间的底层实现结构。

1.String类型的内存空间消耗在哪儿了
2.用什么数据结构可以节省内存
3.如何用集合类型保存单值键值对

【损耗原因】
1.底层数据结构SDS损耗:除了记录实际数据,还需要额外的内存空间记录数据长度、空间使用等信息,这些信息也叫作元数据。

buf:字节数组,保存实际数据。为了表示字节数组的结束,Redis会自动在数组最后加一个“0”,这就会额外占用1个字节的开销。
len:占4个字节,表示buf的已用长度。
alloc:也占个4字节,表示buf的实际分配长度,一般大于len。
SDS中,buf保存实际数据,而len和alloc本身其实是SDS结构体的额外开销。

2.RedisObject损耗:不同数据类型都有些相同的元数据要记录(比如最后一次访问的时间、被引用的次数等),RedisObject结构体来统一记录这些元数据,同时指向实际数据。
一个RedisObject包含了8字节的元数据和一个8字节指针,这个指针再进一步指向具体数据类型的实际数据所在,例如指向String类型的SDS结构所在的内存地址,可以看一下下面的示意图


【类型优化】
当你保存64位有符号整数时,String类型会把它保存为一个8字节的Long类型整数,这种保存方式通常也叫作int编码方式。
为了节省内存空间,Redis还对Long类型整数和SDS的内存布局做了专门的设计。
1.Long类型整,RedisObject中的指针就直接赋值为整数数据了,这样就不用额外的指针再指向整数了,节省了指针的空间开销。
2.字符串数据。字符串小于等于44字节时,RedisObject中的元数据、指针和SDS是一块连续的内存区域,这样就可以避免内存碎片。这种布局方式也被称为embstr编码方式;当字符串大于44字节时,SDS的数据量就开始变多了,Redis就不再把SDS和RedisObject布局在一起了,而是会给SDS分配独立的空间,并用指针指向SDS结构。这种布局方式被称为raw编码模式。


【内存占用】
每个int编码的RedisObject元数据部分占8字节,指针部分被直接赋值为8字节的整数了。
每个ID会使用16字节,加起来一共是32字节。但是,另外的32字节去哪儿了呢?

Redis会使用一个全局哈希表保存所有键值对,哈希表的每一项是一个dictEntry的结构体,用来指向一个键值对。
dictEntry结构中有三个8字节的指针,分别指向key、value以及下一个dictEntry,三个指针共24字节。
但是,这三个指针只有24字节,为什么会占用了32字节呢?这就要提到Redis使用的内存分配库jemalloc了。

【jemalloc】
jemalloc在分配内存时,会根据我们申请的字节数N,找一个比N大,但是最接近N的2的幂次数作为分配的空间,这样可以减少频繁分配的次数。

举个例子。如果你申请6字节空间,jemalloc实际会分配8字节空间;如果你申请24字节空间,jemalloc则会分配32字节。所以,在我们刚刚说的场景里,dictEntry结构就占用了32字节。

好了,到这儿,你应该就能理解,为什么用String类型保存图片ID和图片存储对象ID时需要用64个字节了。

你看,明明有效信息只有16字节,使用String类型保存时,却需要64字节的内存空间,有48字节都没有用于保存实际的数据。我们来换算下,如果要保存的图片有1亿张,那么1亿条的图片ID记录就需要6.4GB内存空间,其中有4.8GB的内存空间都用来保存元数据了,额外的内存空间开销很大。那么,有没有更加节省内存的方法呢?

【ziplist】

用什么数据结构可以节省内存?
Redis有一种底层数据结构,叫压缩列表(ziplist),这是一种非常节省内存的结构。

我们先回顾下压缩列表的构成。表头有三个字段zlbytes、zltail和zllen,分别表示列表长度、列表尾的偏移量,以及列表中的entry个数。压缩列表尾还有一个zlend,表示列表结束。


压缩列表之所以能节省内存,就在于它是用一系列连续的entry保存数据。每个entry的元数据包括下面几部分。

prev_len,表示前一个entry的长度。prev_len有两种取值情况:1字节或5字节。取值1字节时,表示上一个entry的长度小于254字节。虽然1字节的值能表示的数值范围是0到255,但是压缩列表中zlend的取值默认是255,因此,就默认用255表示整个压缩列表的结束,其他表示长度的地方就不能再用255这个值了。所以,当上一个entry长度小于254字节时,prev_len取值为1字节,否则,就取值为5字节。
len:表示自身长度,4字节;
encoding:表示编码方式,1字节;
content:保存实际数据。
这些entry会挨个儿放置在内存中,不需要再用额外的指针进行连接,这样就可以节省指针所占用的空间。

我们以保存图片存储对象ID为例,来分析一下压缩列表是如何节省内存空间的。

每个entry保存一个图片存储对象ID(8字节),此时,每个entry的prev_len只需要1个字节就行,因为每个entry的前一个entry长度都只有8字节,小于254字节。这样一来,一个图片的存储对象ID所占用的内存大小是14字节(1+4+1+8=14),实际分配16字节。

Redis基于压缩列表实现了List、Hash和Sorted Set这样的集合类型,这样做的最大好处就是节省了dictEntry的开销。当你用String类型时,一个键值对就有一个dictEntry,要用32字节空间。但采用集合类型时,一个key就对应一个集合的数据,能保存的数据多了很多,但也只用了一个dictEntry,这样就节省了内存。

【hash】
这个方案听起来很好,但还存在一个问题:在用集合类型保存键值对时,一个键对应了一个集合的数据,但是在我们的场景中,一个图片ID只对应一个图片的存储对象ID,我们该怎么用集合类型呢?换句话说,在一个键对应一个值(也就是单值键值对)的情况下,我们该怎么用集合类型来保存这种单值键值对呢?

如何用集合类型保存单值的键值对?
在保存单值的键值对时,可以采用基于Hash类型的二级编码方法。这里说的二级编码,就是把一个单值的数据拆分成两部分,前一部分作为Hash集合的key,后一部分作为Hash集合的value,这样一来,我们就可以把单值数据保存到Hash集合中了。

以图片ID 1101000060和图片存储对象ID 3302000080为例,我们可以把图片ID的前7位(1101000)作为Hash类型的键,把图片ID的最后3位(060)和图片存储对象ID分别作为Hash类型值中的key和value。

按照这种设计方法,我在Redis中插入了一组图片ID及其存储对象ID的记录,并且用info命令查看了内存开销,我发现,增加一条记录后,内存占用只增加了16字节,如下所示:

127.0.0.1:6379> info memory
# Memory
used_memory:1039120
127.0.0.1:6379> hset 1101000 060 3302000080
(integer) 1
127.0.0.1:6379> info memory
# Memory
used_memory:1039136
在使用String类型时,每个记录需要消耗64字节,这种方式却只用了16字节,所使用的内存空间是原来的1/4,满足了我们节省内存空间的需求。

不过,你可能也会有疑惑:“二级编码一定要把图片ID的前7位作为Hash类型的键,把最后3位作为Hash类型值中的key吗?”其实,二级编码方法中采用的ID长度是有讲究的。

在第2讲中,我介绍过Redis Hash类型的两种底层实现结构,分别是压缩列表和哈希表。

那么,Hash类型底层结构什么时候使用压缩列表,什么时候使用哈希表呢?其实,Hash类型设置了用压缩列表保存数据时的两个阈值,一旦超过了阈值,Hash类型就会用哈希表来保存数据了。

这两个阈值分别对应以下两个配置项:

hash-max-ziplist-entries:表示用压缩列表保存时哈希集合中的最大元素个数。
hash-max-ziplist-value:表示用压缩列表保存时哈希集合中单个元素的最大长度。
如果我们往Hash集合中写入的元素个数超过了hash-max-ziplist-entries,或者写入的单个元素大小超过了hash-max-ziplist-value,Redis就会自动把Hash类型的实现结构由压缩列表转为哈希表。

一旦从压缩列表转为了哈希表,Hash类型就会一直用哈希表进行保存,而不会再转回压缩列表了。在节省内存空间方面,哈希表就没有压缩列表那么高效了。

为了能充分使用压缩列表的精简内存布局,我们一般要控制保存在Hash集合中的元素个数。所以,在刚才的二级编码中,我们只用图片ID最后3位作为Hash集合的key,也就保证了Hash集合的元素个数不超过1000,同时,我们把hash-max-ziplist-entries设置为1000,这样一来,Hash集合就可以一直使用压缩列表来节省内存空间了。

小结
这节课,我们打破了对String的认知误区,以前,我们认为String是“万金油”,什么场合都适用,但是,在保存的键值对本身占用的内存空间不大时(例如这节课里提到的的图片ID和图片存储对象ID),String类型的元数据开销就占据主导了,这里面包括了RedisObject结构、SDS结构、dictEntry结构的内存开销。

针对这种情况,我们可以使用压缩列表保存数据。当然,使用Hash这种集合类型保存单值键值对的数据时,我们需要将单值数据拆分成两部分,分别作为Hash集合的键和值,就像刚才案例中用二级编码来表示图片ID,希望你能把这个方法用到自己的场景中。

最后,我还想再给你提供一个小方法:如果你想知道键值对采用不同类型保存时的内存开销,可以在这个网址里输入你的键值对长度和使用的数据类型,这样就能知道实际消耗的内存大小了。建议你把这个小工具用起来,它可以帮助你充分地节省内存。

每课一问
按照惯例,给你提个小问题:除了String类型和Hash类型,你觉得,还有其他合适的类型可以应用在这节课所说的保存图片的例子吗?

欢迎在留言区写下你的思考和答案,我们一起交流讨论,也欢迎你把今天的内容分享给你的朋友。

相关文章:

Redis优化内存篇

【内存消耗】 场景:业务ID->图片ID(KV:partnerId->objectId)。 刚开始,我们保存了1亿张图片,大约用了6.4GB的内存。 随着图片数据量的不断增加,Redis变慢了。 新的认知:String类型并不是适…...

Vue原理解析

文章目录1. VUE的响应式原理1.1 ViewModel1.2 双向绑定的基本原理1.3 什么是响应性1.4 Vue 中的响应性是如何工作的2. Vue 渲染机制2.1 虚拟 DOM2.2 渲染管线2.3 带编译时信息的虚拟 DOM2.3.1 静态提升2.3.2 修补标记 Flags2.3.3 树结构打平2.3.4 对 SSR 激活的影响1. VUE的响应…...

C# Lambda表达式含义及各种写法

Lambda表达式在各个语言中的表达方式都不太相同,本文重点介绍C#的Lambda表达式。 首先,Lambda表达式就是一个匿名的方法/函数。 以下面的一个完整版作为例子,前面是参数,后面是返回值: 由于 Lambda表达式和委托常常一起…...

计算机组成原理:1. 计算机系统概论

更好的阅读体验\huge{\color{red}{更好的阅读体验}}更好的阅读体验 文章目录1.1 计算机系统简介1.1.1 计算机软硬件概念1.1.2 计算机的层次1.1.3计算机组成和计算机体系结构1.2 计算机的基本组成1.2.1 冯诺伊曼计算机的特点1.2.2 计算机的硬件框图1.2.3 计算机的工作步骤1.3 计…...

【c#】c#常用小技巧方法整理(5)—— 字符串操作类

1、GetStrArray(string str, char speater, bool toLower) 把字符串按照分隔符转换成 List 2、GetStrArray(string str) 把字符串转 按照, 分割 换为数据 3、GetArrayStr(List list, string speater) 把 List 按照分隔符组装成 string 4、GetArrayStr(List list) 得到数组列表以…...

用队列实现栈VS用栈实现队列

之前我们就讲过队列,栈的基础知识,笔者之前有过详细的介绍,感兴趣的可以根据笔者的个人主页进行查找:https://blog.csdn.net/weixin_64308540/?typelately225. 用队列实现栈请你仅使用两个队列实现一个后入先出(LIFO&…...

MY2480-16P语音模块的使用

MY2480-16P语音模块的使用开发环境:STM32CUBEMXKEIL5辅助软件:串口助手、迅捷文字转语音一、MY2480-16P语音模块引脚图及引脚定义二、选择触发方式三、使用串口控制MY2480-16P语音模块四、模块使用指南开发环境:STM32CUBEMXKEIL5 辅助软件&a…...

I/O 多路复用

。新到来一个 TCP 连接,就需要分配一个进程或者线程,那么如果要达到 C10K,意味着要一台机器维护 1 万个连接,相当于要维护 1 万个进程/线程,操作系统就算死扛也是扛不住的。 一个进程虽然任一时刻只能处理一个请求&…...

2023 最新版网络安全保姆级指南,从0到1,建议收藏!

一、网络安全学习的误区 1.不要试图以编程为基础去学习网络安全 不要以编程为基础再开始学习网络安全,一般来说,学习编程不但学习周期长,且过渡到网络安全用到编程的用到的编程的关键点不多。一般人如果想要把编程学好再开始学习网络安全往…...

力扣39.组合总数

文章目录力扣39.组合总数题目描述方法1:深搜回溯力扣39.组合总数 题目描述 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可…...

sql的case when用法详解

简单CASE WHEN函数: CASE SCORE WHEN A THEN 优 ELSE 不及格 END CASE SCORE WHEN B THEN 良 ELSE 不及格 END CASE SCORE WHEN C THEN 中 ELSE 不及格 END等同于,使用CASE WHEN条件表达式函数实现: CASE WHEN SCORE A THEN 优WHEN SCORE …...

AtCoder Grand Contest 061(题解)

A - Long Shuffle 这道题本质是一个找规律的题 既然是打表题,我们先暴力把他打出来 (盗一张图.jpg) 接下来就是在这张图中挖掘答案 我们可以明显的看到偶数行是有一些规律的 要么是相邻对的互换,要么不变 不变和互换的位置也有讲究,在二进制…...

生成系列论文:文本控制的3d点云生成 TextCraft(一):论文概览

TextCraft: Zero-Shot Generation of High-Fidelity and Diverse Shapes from Text 论文原文: https://arxiv.org/abs/2211.01427 论文的研究动机 DALL2已经在文本控制的图像生成上取得很好的效果,但是基于文本控制的3d点云生成的研究还不太成熟&#…...

IDEA常用插件

常用IDEA插件 Codota 插件下载地址:Codota AI Autocomplete for Java and JavaScript - IntelliJ IDEs Plugin | Marketplace IDEA的自动补全功能已经很强大了,但是这个插件的自动补全功能更加强大,这是一个基于AI技术,学习了大量…...

Spring的事务传播机制

多个事务方法相互调用时,事务如何在这些方法之间进行传播,Spring中提供了七种不同的传播机制,来保证事务的正常执行: REQUIRED:默认的传播机制,如果存在事务,则支持/加入当前事务,如…...

Python:路径之谜(DFS剪枝)

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

阿里巴巴在开源压测工具 JMeter 上的实践和优化

Apache JMeter [1] 是 Apach 旗下的开源压测工具,创建于 1999 年初,迄今已有超过 20 年历史。JMeter 功能丰富,社区(用户群体)庞大,是主流开源压测工具之一。 性能测试通常集中在新系统上线或大型活动前&…...

React Draggable插件实现拖拽功能

React Draggable插件实现拖拽功能1.下载Draggable插件2.引入Draggable插件3.设置一个div,并设置样式,并用Draggable包裹起来4.设置拖拽的范围5.Draggable常用props1.下载Draggable插件 npm install react-draggable2.引入Draggable插件 // 引入拖拽插件…...

MySQL-运算符

算术运算符: 加法运算-: 减法运算*: 乘法运算/: 除法运算,返回商%: 求余运算,返回余数例:创建n5表,插入数字100,查看数据表分别查看、-、*、/、%mysql> create table n5(-> num int); Query OK, 0 rows affected…...

Hudi-基本概念(时间轴、文件布局、索引、表类型、查询类型、数据写、数据读、Compaction)

文章目录基本概念时间轴(TimeLine)文件布局(File Layout)Hudi表的文件结构Hudi存储的两个部分Hudi的具体文件说明索引(Index)原理索引选项全局索引与非全局索引索引的选择策略对事实表的延迟更新对事件表的去重对维度表的随机更删…...

数据分享|中国各省、各市、各区县分年、分月、逐日平均气温数据(2000年~2019年)

今天分享给大家的是 2000 年~2019 年中国各省、各市、各县的分年、分月、逐日的平均气温数据(单位:摄氏度) 原始数据来源于国家气象科学数据共享服务平台-中国地面气候资料日值数据集(V3.0),原始数据是各个观测站点的日度数据,为了方便大家使用,我使用 Barnes 方法(…...

steam/csgo搬砖,2023年最暴利的项目

这个项目赚钱主要来源于两个地方: 1.比如说今天美元的汇率是1美元6.8人民币,那我们有特定的渠道能拿到1美元5.0-5.5左右人民币的价格,100美元的汇率差利润就有180元左右的利润,当然这个价格是根据国际的汇率上下会有浮动的。 2.…...

RDSDRDSPolarDBPolarDB-X的区别

RDS 阿里云关系型数据库(Relational Database Service,简称RDS),是一种稳定可靠、可弹性伸缩的在线数据库服务。 基于阿里云分布式文件系统和高性能存储,RDS支持MySQL、SQL Server、PostgreSQL和PPAS(Post…...

【Python学习笔记】30.Python3 命名空间和作用域

前言 本章介绍Python的命名空间和作用域。 命名空间 先看看官方文档的一段话: A namespace is a mapping from names to objects.Most namespaces are currently implemented as Python dictionaries。 命名空间(Namespace)是从名称到对象的映射,大…...

后量子 KEM 方案:Kyber

参考文献: Bos J, Ducas L, Kiltz E, et al. CRYSTALS-Kyber: a CCA-secure module-lattice-based KEM[C]//2018 IEEE European Symposium on Security and Privacy (EuroS&P). IEEE, 2018: 353-367.Avanzi R, Bos J, Ducas L, et al. Crystals-kyber[J]. NIST…...

2019年广东工业大学腾讯杯新生程序设计竞赛(同步赛)

同步赛链接 A-原初的信纸(最值&#xff0c;STL&#xff09; 题意&#xff1a; 找 n 个数的最大值. 参考代码&#xff1a; void solve() {int n;std::cin >> n;std::vector<int> a(n);for (auto &c : a)std::cin >> c;std::cout << *max_element…...

生产Nginx现大量TIME-WAIT,连接耗尽,该如何处理?

背景说明&#xff1a; 在尼恩读者50交流群中&#xff0c;是不是有小伙伴问&#xff1a; 尼恩&#xff0c;生产环境 Nginx 后端服务大量 TIME-WAIT &#xff0c; 该怎么办&#xff1f; 除了Nginx进程之外&#xff0c;还有其他的后端服务如&#xff1a; 尼恩&#xff0c;生产环境…...

Linux服务器clang-13安装(环境变量配置)

1.从llvm的github网址选择合适的release合适的运行平台进行下载&#xff0c;下载官方预编译的二进制压缩包。 2.将下载好的压缩包进行本地上传。 使用scp命令进行上传 scp -r -P 端口号 本地文件路径 服务器ID等:服务器上目标地址 3.解压(tar命令&#xff09; 4.环境变量配…...

【C++】C/C++内存管理模板初阶

文章目录一、 C/C内存管理1. C/C内存分布2. C内存管理方式3. operator new与operator delete函数4. new和delete的实现原理5. 定位new表达式6. 常见面试题malloc/free和new/delete的区别内存泄漏二、模板初阶1. 泛型编程2. 函数模板3. 类模板一、 C/C内存管理 1. C/C内存分布 …...

笙默考试管理系统-index展示

public class PageList<T> : List<T> { public int PageIndex { get; private set; } //页索引 public int PageSize { get; private set; }//页大小 public int TotalPage { get; private set; }//总页数 public int TotalCo…...

做网站的开题报告/成都seo招聘

datetime.timedelta对象代表两个时间之间的时间差&#xff0c;两个date或datetime对象相减就可以返回一个timedelta对象。 如果有人问你昨天是几号&#xff0c;这个很容易就回答出来了。但是如果问你200天前是几号&#xff0c;就不是那么容易答出来。而在Python中datetime模块…...

wordpress网站速度慢/seo顾问服务深圳

文章目录心流是当一个人完全沉浸在某种活动中&#xff0c;无视其他事物存在的状态&#xff0c;这种体验本身会获得莫大的喜悦&#xff0c;使人愿意付出巨大的代价。心流是一种最优体验&#xff0c;这种最优体验产生于一种自我掌控的感觉&#xff0c;虽然我们未必会在现实生活中…...

wordpress 后台管理模板/比较正规的代运营

来源&#xff1a;网络整理根据企查查消息显示&#xff0c;华为电动技术有限公司已经成立&#xff0c;注册资本 2.5 亿人民币&#xff0c;法定代表人为王军&#xff0c;由华为技术有限公司100%持股。其经营范围包括工程和技术研究和试验发展&#xff1b;智能车载设备销售&#x…...

wordpress改变域名/深圳做网站的公司

环境及工具 手机 &#xff1a;小米手机 MI 2A 系统版本: Android 4.1.1 工具 : IDA pro 6.6 、C32Asm 、VS2005 一&#xff1a;第一次打开加密视频会出现如下验证: 输入用户名与密码登录成功后如下图 点击“支付并获取许可证”成功后就可以播放加密的视频了&#xff0c;并…...

怎么在虚拟机中做网站/太原网络推广价格

一、火灾报警控制器火灾报警控制器是火灾自动报警控制系统的核心,能接收火灾探测器传输的信号转换成声、光报警信号&#xff0c;显示火灾发生的位置、时间和记录报警信息等强大功能。还可通过手动报警装置启动火灾报警信号&#xff0c;或通过自动灭火控制装置启动自动灭火设备和…...

惠州市两学一做网站/seo还可以做哪些推广

预备知识&#xff1a;缓冲区的刷新方式 fflush(stdout)\n能启动刷新缓冲区的作用 1.进程终止的5种方式 正常退出 从main函数返回–语言级别的返回操作 调用exit–C库函数 调用_exit–系统调用异常退出 调用abort 产生SIGAORT信号 由信号终止 ctrlc /SIGINT 2.exit和_exit的…...