实践篇 11 | 万金油的string,为什么不好用了

string类型的内存空间消耗问题。

一个图片存储系统,需要

  • 快速记录图片ID和图片在存储系统中保存的ID(存储对象id)
  • 根据图片ID查找图片存储id

图片数量巨大,用10位数表示图片ID和存储对象ID,如图片ID:1101000051,存储系统中对象ID:3301000051

photo_id: 1101000051
photo_obj_id: 3301000051

键-单值模式,单值指的是键值对的值不是集合。和string类型提供的“一键一值”的保存形式刚好。

且String类型可保存二进制字节流,把数据转成二进制字节数组即可存储,所以说其“万金油”。

刚开始,存了1亿张图,用大约6.4G内存。随着图片数量增加,遇到了大内存redis实例因生成RDB而响应变慢的问题。需优化内存占用。

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

刚才的案例中:一个图片ID和图片存储对象的记录平均使用64字节。(1亿张~6.4GB)

实际,最多16字节即可存储(各10位的数字,可用8字节的Long类型)。但String类型额外记录数据长度,空间使用等元数据占据了空间,元数据开销就变大了。

64位有符号整数,String类型存储为一个8字节的Long类型整数,即int编码方式存储。

包含字符的数据,String类型就会用简单动态字符串(SDS)存储,

除了SDS还有一个RedisObject结构体记录元数据(最近访问时间、被引用次数)。实际String类型的存储结构如下,分为三种编码类型。

 

 基于该知识分析,图片ID和存储系统中对象ID,都采用int编码存储,值总共是32字节。还剩32字节是全局hash中的entry空间。

因此抛开key的大小,有效信息只需16字节,但实际要64字节,其余48字节没有实际存数据。换算后1亿图片大约4.8G浪费了。

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

 

Redis底层有一个结构,叫压缩列表ziplist,非常省内存。

entry挨个儿放置在内存中,无需额外的指针连接,可节省指针空间。

存储一个图片ID,entry大小为(1+4+1+8)14字节,实际分配16字节。Redis基于压缩列表实现了List,Hash和Sorted Set这样的集合类型,好处是省去了dictEntry开销。用String时,dictEntry耗去了32字节。集合类型,单key对应一个集合空间,只用1个dictEntry,省了内存。 

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

在保存单值的键值对时,可采用Hash的二级编码方法。将数据拆为两部分,一部分key,一部分value。

以图片ID:1101000060和图片存储对象ID 3302000080 为例,可以把图片ID的前7位(1101000)作为Hash类型的键,后3位(060)和图片存储对象ID作为Hash类型集合中的key和value。

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

一个图片只用了16字节,省了3/4。二级编码中采用的id长度有讲究。Redis的底层结构是压缩列表和hash表。Hash类型设置了用压缩列表保存数据时的两个阈值,一旦超过了就会用哈希表存储数据。

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

一旦超过了上述任意阈值,redis会自动把压缩列表转成哈希表,之后再也不会转回来。省内存方面,哈希表没有压缩列表那么高效。

所以刚才的二级编码方案,只用后3位作为键,就可保证hash类型中entry数量不会超过1000个。

小结

打破了对String的认识误区,虽然是“万金油”,但是本身数据不大的时候,元数据开销过大,包括RedisObject,SDS结构,dictEntry的内存开销。

针对这种可以用Hash这种集合类型保存单值键值对,将数据拆两部分,二级编码。

最后有一个工具链:可以知道采用不同类型保存的键值对的内存开销。

思考题

除了String和Hash还有其他合适类型可以应用在本案例所说的图片保存的例子?

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值