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

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

数据量多时,比较占空间

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

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

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

【redis进阶】Redis String数据类型为什么不好用_第1张图片

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

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

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

RedisObject

【redis进阶】Redis String数据类型为什么不好用_第2张图片

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

embstr编码方式:

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

raw编码方式

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

【redis进阶】Redis String数据类型为什么不好用_第3张图片

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

【redis进阶】Redis String数据类型为什么不好用_第4张图片

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

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

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

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

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

压缩列表

【redis进阶】Redis String数据类型为什么不好用_第5张图片

压缩列表能节省内存是因为它是用一系列连续的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,多级压缩列表

你可能感兴趣的:(redis进阶,redis,缓存,压缩列表)