今天,我们先了解下 String 类型的内存空间消耗问题,以及选择节省内存开销的数据类型的解决方案 。
我想和你分享一个之前我面临的需求案例 。
曾经,我们面临着一个任务,要创建一个高效的图片存储系统,要求这个系统能够快速记录图片 ID 和图片在存储系统中的唯一标识(我们称之为图片存储对象 ID) 。此外,还需要能够通过图片 ID 快速检索到相应的图片存储对象 ID 。
考虑到图片数量庞大,我们决定使用 10 位数字来表示图片 ID 和图片存储对象 ID 。举个例子,图片 ID 可能是 1101000051 , 对应的存储对象 ID 则是 3301000051 。
photo_id: 1101000051
photo_obj_id: 3301000051
这个案例很明显地展现了“键 - 单值”模式 。在这种模式中,每个键值对中的值都是一个单一的值,而不是一个值的集合,与 String 类型的数据存储方式完美契合 。
另外 , String 类型的数据可以保存二进制字节流 , 这使得它非常灵活,只需将数据转换成二进制字节数组 , 就可以轻松地进行存储 。
因此,我们的初始解决方案是使用 String 类型来存储数据 。我们将图片 ID 和图片存储对象 ID 分别用作键值对中的键和值,其中图片存储对象 ID 使用了 String 类型 。
最初,我们成功地存储了一亿张图片,大约使用了 6.4GB 的内存 。但是,随着图片数据不断增加 , 我们开始遇到了问题,redis 实例的内存使用量不断上升,导致生成 RDB 文件时出现延迟的情况 。显然,String 类型并不是一个适合大规模数据存储的理想选择,因此我们需要寻找更为节省内存开销的数据类型解决方案 。
在这个过程中,我深入研究了 String 类型的底层结构,找出了它内存开销较大的原因 。这让我对这个“通用型”的 String 数据类型有了新的认识,它并不适用于所有情况,尤其在内存空间消耗方面存在明显短板 。
与此同时,我还仔细研究了集合类型的数据结构 , 发现它们具有非常高效的内存管理结构 。但是,集合类型的数据结构通常用于保存一键多值的数据,不太适用于直接存储单一键对应的单一值 。因此,我们采用了二级编码的方法,成功地使用集合类型来存储单一键值对 。这种改变显著降低了 Redis 实例的内存开销 。
在本篇文章中,我将与你分享我在解决这一问题过程中所获得的经验和方法 , 包括 String 类型的内存开销问题,可节省内存的数据结构选择,以及如何使用集合类型来存储单一键值对 。如果你在使用 String 类型时也遇到了内存开销较大的问题,那么今天的解决方案可能会对你有所帮助 。
接下来,我们先来看看 String 类型的内存都消耗在哪里了 。
为什么 String 类型内存开销大?在刚才的案例中,我们保存了 1 亿张图片的信息 , 用了约 6.4GB 的内存,一个图片 ID 和图片存储对象 ID 的记录平均用了 64 字节 。
但问题是,一组图片 ID 及其存储对象 ID 的记录,实际只需要 16 字节就可以了 。
我们来分析一下 。图片 ID 和图片存储对象 ID 都是 10 位数,我们可以用两个 8 字节的 Long 类型表示这两个 ID 。因为 8 字节的 Long 类型最大可以表示 2 的 64 次方的数值,所以肯定可以表示 10 位数 。但是,为什么 String 类型却用了 64 字节呢?
其实,除了记录实际数据,String 类型还需要额外的内存空间记录数据长度、空间使用等信息,这些信息也叫作元数据 。当实际保存的数据较小时,元数据的空间开销就显得比较大了,有点“喧宾夺主”的意思 。
那么,String 类型具体是怎么保存数据的呢?我来解释一下 。
当你保存 64 位有符号整数时,String 类型会把它保存为一个 8 字节的 Long 类型整数,这种保存方式通常也叫作 int 编码方式 。
但是,当你保存的数据中包含字符时,String 类型就会用简单动态字符串(Simple Dynamic String,SDS)结构体来保存,如下图所示:
文章插图
图片
buf:字节数组,保存实际数据 。为了表示字节数组的结束,Redis 会自动在数组最后加一个“”,这就会额外占用 1 个字节的开销 。
len:占 4 个字节,表示 buf 的已用长度 。
alloc:也占个 4 字节,表示 buf 的实际分配长度,一般大于 len 。
可以看到,在 SDS 中,buf 保存实际数据,而 len 和 alloc 本身其实是 SDS 结构体的额外开销 。
另外,对于 String 类型来说 , 除了 SDS 的额外开销 , 还有一个来自于 RedisObject 结构体的开销 。
推荐阅读
- C++中的多线程编程:一种高效的并发处理方式
- HTTP协议中Cookie和Session的区别是什么?
- 无敌到寂寞!Redis进军磁盘存储!
- Kubernetes中的优雅关闭和零停机时间部署
- cdr中如何制作表格,cdr应该怎么才能画表格
- 为什么很多男生夏天穿中筒袜 丝袜和裤袜有什么区别
- cdr如何复制粘贴文字,cdr中要怎么样复制文字
- 7位老戏骨,在《凡人修仙传》中出演仙风道骨的角色,期待吗?
- ps出血位怎么做,PS中的出血线该怎样使设置
- 歌手黄安晒出与蔡英文结婚照,网友纷纷表示:为中国的统一而结婚