Redis学习笔记——万金油string为什么不好用了?
Posted qq_34132502
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis学习笔记——万金油string为什么不好用了?相关的知识,希望对你有一定的参考价值。
为什么string不好用了
首先string太大了
在Redis中,保存一个string需要使用64字节。如果要保存一个long型整数,原数据只有8字节,而却需要八倍的空间去存储它,这在保存大量数据时时非常不划算的。
为什么需要用的64字节的空间?
因为string不光有自身数据,还有一些“辅助”数据,比如自身的长度以及所占空间,这两个分别需要4字节,目前需要16字节。这些数据称为简单动态字符串(Simple Dynamic String,SDS)
而这之外还需要“包裹一层”RedisObject,由元数据和指向SDS。这时总共是32字节。
又因为Redis会使用一个全局哈希表保存所有键值对,哈希表的每一项是一个 dictEntry 的结构体,用来指向一个键值对。dictEntry 结构中有三个 8 字节的指针,分别指向 key、value 以及下一个 dictEntry,三个指针共 24 字节。
而使用jemalloc系统分配空间时,为了防止分配的空间不够,造成反复的申请空间,所以会选择比申请空间大的,离得最近的2的n次幂作为分配空间的大小。所以会分配32字节。
至此总共需要64字节。
用什么数据结构可以节省内存?
Redis 有一种底层数据结构,叫压缩列表(ziplist),这是一种非常节省内存的结构。
表头有三个字段 zlbytes、zltail 和 zllen,分别表示列表长度、列表尾的偏移量,以及列表中的 entry 个数。压缩列表尾还有一个 zlend,表示列表结束。
压缩列表每一项有如下数据:
- prev_len,表示前一个 entry 的长度。prev_len 有两种取值情况:1 字节或 5 字节。取值 1 字节时,表示上一个 entry 的长度小于 254 字节;
- len:表示自身长度,4 字节;
- encoding:表示编码方式,1 字节;
- content:保存实际数据。
所以一次算下来需要14字节,系统分配16字节。
Redis 基于压缩列表实现了 List、Hash 和 Sorted Set 这样的集合类型,这样做的最大好处就是节省了 dictEntry 的开销。当你用 String 类型时,一个键值对就有一个 dictEntry,要用 32 字节空间。但采用集合类型时,一个 key 就对应一个集合的数据,能保存的数据多了很多,但也只用了一个 dictEntry,这样就节省了内存。
这个方案听起来很好,但还存在一个问题:在用集合类型保存键值对时,一个键对应了一个集合的数据,但是在我们的场景中,一个图片 ID 只对应一个图片的存储对象 ID,我们该怎么用集合类型呢?换句话说,在一个键对应一个值(也就是单值键值对)的情况下,我们该怎么用集合类型来保存这种单值键值对呢?
如何用集合类型保存单值的键值对?
在保存单值的键值对时,可以采用基于 Hash 类型的二级编码方法。这里说的二级编码,就是把一个单值的数据拆分成两部分,前一部分作为 Hash 集合的 key,后一部分作为 Hash 集合的 value,这样一来,我们就可以把单值数据保存到 Hash 集合中了。
以图片 ID 1101000060 和图片存储对象 ID 3302000080 为例,我们可以把图片 ID 的前 7 位(1101000)作为 Hash 类型的键,把图片 ID 的最后 3 位(060)和图片存储对象 ID 分别作为 Hash 类型值中的 key 和 value。
不过,为什么二级编码一定要把图片 ID 的前 7 位作为 Hash 类型的键,把最后 3 位作为 Hash 类型值中的 key 吗?其实,二级编码方法中采用的 ID 长度是有讲究的。
Redis Hash 类型的两种底层实现结构,分别是压缩列表和哈希表。
那么,Hash 类型底层结构什么时候使用压缩列表,什么时候使用哈希表呢?其实,Hash 类型设置了用压缩列表保存数据时的两个阈值,一旦超过了阈值,Hash 类型就会用哈希表来保存数据了。
一个是单个元素的最大长度,另一个是用压缩列表保存时哈希集合中的最大元素个数。
因为哈希表在压缩内存方面没有压缩列表高效,所以为了能充分使用压缩列表的精简内存布局,我们一般要控制保存在 Hash 集合中的元素个数。所以,在刚才的二级编码中,我们只用图片 ID 最后 3 位作为 Hash 集合的 key,也就保证了 Hash 集合的元素个数不超过 1000,同时,我们把 hash-max-ziplist-entries 设置为 1000,这样一来,Hash 集合就可以一直使用压缩列表来节省内存空间了。
以上是关于Redis学习笔记——万金油string为什么不好用了?的主要内容,如果未能解决你的问题,请参考以下文章