【Redis】保存一亿张图片,如果利用redis的底层数据结构进行优化,使占用内存空间少?

当一亿张图片保存信息,用了6.4GB,Redis大内存实例生成 RDB 响应变慢的问题。

因为String类型的数据结构,在保存数据时消耗的内存空间较多。额外开销较大。

采用二级编码的方法,实现了用集合类型保存单键值对,Redis实例的内存空间消耗下降。

为什么String的额外开销较大?String类型保存图片和图片存储对象ID平均采用了64字节,而实际long类型只需要8字节,两个long = 16字节,为什么String却采用了64字节保存?

为什么 String 类型内存开销大?

为什么用 String 类型保存图片 ID 和图片存储对象 ID 时需要用 64 个字节?即,两个String(key-value)占用64字节?

16(原来数据) + 8 (len)+ 8(alloc) + 32(DictEntry)

DictEntry中,全局哈希表的哈希桶32B(*key + *value + *next = 8 + 8 + 8 =24,但是jemalloc内存分配库会分配大于24且最接近N的2的幂次数作为分配空间即使2^5=32B) ,实际数据的key的RedisObject 16B, 实际数据的value的RedisObject 16B,最终32 + 16 + 16 = 64 。

其中,实际数据的key value都采用int编码,所以RedisObject只有元数据8B + INT值8B,而不是一个指针8B指向实际的数据key/value Redis提供的数据类型只有string,没有long,这里的INT编码是指当string类型的数据实际是一个整数时,redis会用INT优化编码。

在这里插入图片描述

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

Redis 有一种底层数据结构,叫压缩列表(ziplist)

img

为什么压缩列表可以节省空间?

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

①, 压缩列表,是一种非常节省内存的数据结构,因为他使用连续用一系列连续的 entry 保存数据,不需要额外的指针进行连接,节省了指针本身的内存空间(8B)
②,Redis基于压缩列表实现List,Hash,Sorted Set集合类型,最大的好处是节省了dicEntry开销

如何用集合类型保存单值的键值对?

但是我们是一个图片ID对应一个图片实例对象,我们该怎么用集合类型呢?换句话说,在一个键对应一个值(也就是单值键值对)的情况下,我们该怎么用集合类型来保存这种单值键值对呢?

在保存单值的键值对时,可以采用基于 Hash 类型的二级编码方法

这里说的二级编码,就是把一个单值的数据拆分成两部分,前一部分作为 Hash 集合的 key,后一部分作为 Hash 集合的 value,这样一来,我们就可以把单值数据保存到 Hash 集合中了。以图片 ID 1101000060 和图片存储对象 ID 3302000080 为例,我们可以把图片 ID 的前 7 位(1101000)作为 Hash 类型的键,把图片 ID 的最后 3 位(060)和图片存储对象 ID 分别作为 Hash 类型值中的 key 和 value。

①,Hash类型设置了用压缩列表保存数据时的两个阀值,一旦超过就会将压缩列表转为哈希表,且不可回退
②,hash-max-ziplist-entries:表示用压缩列表保存哈希集合中的最大元素个数
③,hash-max-ziplist-value:表示用压缩列表保存时,哈希集合中单个元素的最大长度

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

总结

以前,我们认为 String 什么场合都适用,但是,在保存的键值对本身占用的内存空间不大时,String 类型的元数据开销就占据主导了,这里面包括了 RedisObject 结构、SDS 结构、dictEntry 结构的内存开销

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

posted @ 2021-05-21 17:34  your_棒棒糖  阅读(323)  评论(0编辑  收藏  举报