【redis进阶】Redis String数据类型为什么不好用

保存1 亿张10字节图片标识,String结构用了 6.4GB 的内存,为什么?如何优化?

数据量多时,比较占空间

存储数量量较多的时候,可以使用list数据结构来替代String,以二级编码的方式将数据存入redis

为什么String的内存开销大呢?

除了记录数据外,还需记录元数据

buf:字节数组,保存实际数据。为了表示字节数组的结束,Redis 会自动在数组最后加一个"\0",这就会额外占用 1 个字节的开销。

len:占 4 个字节,表示 buf 的已用长度

alloc:也占个 4 字节,表示 buf 的实际分配长度,一般大于 len。

RedisObject

针对Long类型的优化:指针直接赋值为对应的值,节省空间

embstr编码方式:

字符串小于等于44字节时,RedisObject的数据是一块连续的内存空间。

raw编码方式

字符串大于等于44字节时,SDS会有独立的空间,用指针指向SDS结构

全局hash结构内存占用情况:

计算10字节图片用String存入redis的内存占用情况?

首先,由于id用的是Long类型,所以使用的是int编码,key和value各占16字节,结果是32字节

又由于全局hash表指向数据需要指针,key、value、next,又占用24字节

又由于分配内存时都是每8字节分配一次,即指针也占32字节

用什么数据结构可以节省内存?

压缩列表

压缩列表能节省内存是因为它是用一系列连续的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:保存实际数据;

指针会挨个放入内存中,无需额外指针指向,这样就可以节省指针所占用的空间

计算图片ID存储问题(优化版)

每个entry保存一个图片存储对象id(8字节),此时perv_len只需1字节,所以一个图片id存储对象Id所占空间大小是:14字节(1 + 4 + 1 + 8)。且采用集合类型时,一个key对应一个集合,能保存的数据多了很多,但也就用了一个dictEntry。

二级编码:把一个单值的数据拆分成两部分,前一部分作为hash结构的key,后一部分作为hash结构的value。

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

这样一条数据才占用16字节,相当于使用String类型的1/4

redis什么情况下使用压缩列表,什么情况下使用哈希表呢?

其实hash类型设置了用压缩列表保存数据的两个阈值,一旦超过阈值,Hash类型就会使用Hash表来保存数据。

  • hash-max-ziplist-entries:表示用压缩列表保存时哈希集合中的最大元素个数。
  • hash-max-ziplist-value:表示用压缩列表保存时哈希集合中单个元素的最大长度。

超过其中一个就会导致使用哈希表

二级编码采用的长度有何讲究?

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

总结

String内存的主要开销,RedisObject结构、SDS结构、dictEntry结构的内存开销。

可以使用压缩列表来保存数据,只需将单值数据拆分成两部分,分别作为Hash集合的key和value,即以二级编码的方式存入集合中。

二级编码类似分库,将主键多后三位作为分库的依据,后三位相同的主键会被映射到同一个哈希表中,再在这个哈希表内查询。第一层采用压缩列表的方式,降低了直接存储字符串单值数据的元数据冗余,第二层则采用保存具体的数据。这样两层的时间复杂度都是O1,但由于是两层查询,哈希速度和网络io如果不在服务端进行的话,时间最起码翻倍。这也相当于用时间换空间。平时可以使用压缩列表做一级表,二级哈希做主键,三级采用二叉树来做时间索引。这样主要是对主键做了两层映射。

彩蛋:可以使用以下网站来计算内存

Redis容量预估-极数云舟

问题

除了String类型和Hash类型,还有其他类型可以保存图片ID吗?

可以使用常规数组来存储,用二进制保存图片位置ID,多级压缩列表

相关推荐
Albert Edison2 小时前
【Redis】Centos7.9 安装 Redis 5 教程
数据库·redis·缓存
Steadfast_GG2 小时前
Redis中的通用命令
redis·缓存
小二·2 小时前
Redis 内存溢出(OOM)排查与恢复实战
数据库·redis·bootstrap
pqk6V6Vep2 小时前
Redis 分布式锁进阶第一篇讲解
数据库·redis·分布式
giaz14n9X3 小时前
Redis 分布式锁进阶第六十一篇
数据库·redis·分布式
JAVA面经实录9176 小时前
Redis 知识体系(完整版)
java·redis·nosql数据库·nosql
颜笑晏晏6 小时前
长输入短输出场景下的 SGLang 推理性能实测前缀缓存、PD 分离配比与参数调优
缓存·推理优化·sglang·ai infra·pd分离
ManageEngine卓豪7 小时前
数据库可观测性:MySQL与Redis监控核心监控指标与全栈运维解决方案
数据库·redis·mysql·数据库性能·数据库监控
真实的菜7 小时前
Redis 从入门到精通(十四):Redis 7.x 新特性全解 —— 系列收官之作
数据库·redis·缓存
小小工匠8 小时前
Redis - 缓冲区管理:避免溢出引发的“惨案“
redis·性能优化·集群·内存管理·持久化