一.图片存储系统
需求:能够快速记录图片id和图片存储对象id,通过图片id找到对应的图片存储对象id。
photo_id:1101000051
photo_obj_id:330100051
String类型提供了"一个键对应一个值得数据"保存形式。
实现:保存一亿张图片,大约用6.4GB内存。但是随着图片数据量的不断增加会产生大内存Redis实例生成RDB响应变慢的问题。
结论:String不是很好的选择,我们需要找到更好的存储方式。
String类型的底层结构
缺点:它保存数据时所消耗的内存空间较多。
集合类型保存数据模式:一个键对应一系列值,不适合直接保存单值的键值对。
String 类型内存开销大?
问题一
我们保存1亿张图片信息,用了6.4GB内存,一个图片id和对象存储对象id的记录平均用了64字节。
图片id和图片存储对象id都是10位数,可以用两个8字节的Long类型表示这两个id。
因为8字节的Long类型最大可表示2的64次方,肯定可以表示10位数。
一个string值需要记录实际数据+元数据(额外内存空间记录数据长度,空间使用信息)。
int编码方式
保存64位有符号整数时,string类型会把它保存为一个8字节Long类型整数。
当保存数据中包含字符。String类型会用简单动态字符串(Simple Dynamic String,SDS)结构体保存。
在SDS中,buf保存实际数据,len和alloc是SDS结构体的额外开销。
RedisObject
相同元数据要记录(最后一次访问时间,被引用的次数),所以Redis会用RedisObject记录这些相同元数据,同时指向实际数据。
元数据和一个8字节的指针,这个指针进一步指向具体数据类型的实际数据所在。
- int编码模式: 保 保存是Long类型整数时,RedisObject中的指针就直接赋值为整数数据,不用额外开辟指针空间指向整数。
- embstr编码: 保存的是字符串数据,但是字符串小于等于44字节,RedisObject中的元数据,指针和SDS是一块连续的内存区域,这样就可以避免内存碎片。
- 大于44字节时,SDS数据量增多,Redis分配独立的空间,并用指针指向SDS结构。
每个 int 编码的 RedisObject 元数据部分占 8 字节,指针部分被直接赋值为 8 字节的整数了。此时,每个 ID 会使用 16 字节,加起来一共是 32 字节。但是,另外的 32 字节去哪儿了呢?
Redis使用全局哈希表保存所有键值对,哈希表的每一项都是一个dictEntry的结构体,用来指向一个键值对。
三个 8 字节的指针,分别指向 key、value 以及下一个 dictEntry,三个指针共 24 字节,
内存分配库jemalloc
分配32字节。
可以节省内存的数据结构
压缩列表:Redis中最省内存的结构。
压缩列表的构成
zlbytes | 列表长度 | 头 |
zltail | 列表尾偏移量 | 头 |
zllen | 列表中的entry个数 | 头 |
zlend | 列表结束 | 尾部 |
连续的entry保存数据来节省内存。不需要额外的指针,节省指针所占用的空间。
prev_len | 前一个entry长度(小于254字节 用1字节,大于用5字节) |
len | 自身长度 4字节 |
encoding | 编码方式 1字节 |
content | 保存实际数据 |
保存图片存储对象ID
entry保存一个图片存储对象ID(8字节),
prev_len | 1字节 |
len | 4字节 |
encoding | 1字节 |
content | 8字节 |
内存大小是 14 字节(1+4+1+8=14),实际分配 16 字节。
同时哈希表中的dictEntry开销也减少了。
一个 dictEntry,变为一个key对应一个集合数据,只需要一个 dictEntry
。
基于 Hash 类型的存储方法
hset 1101000 060 3302000080
前一部分作为 Hash 集合的 key,后一部分作为 Hash 集合的 value,这样一来,我们就可以把单值数据保存到 Hash 集合中了
在使用 String 类型时,每个记录需要消耗 64 字节,这种方式却只用了 16 字节,所使用的内存空间是原来的 1/4,满足了我们节省内存空间的需求。
把最后 3 位作为 Hash 类型值中的 key 吗?”其实,二级编码方法中采用的 ID 长度是有讲究的。
Redis Hash类型的两种底层实现结构:
压缩列表和哈希表
压缩列表和哈希表的切换?
Hash类型设置了压缩列表保存数据的阈值
hash-max-ziplist-entries | 压缩列表保存时哈希集合中的最大元素个数 |
hash-max-ziplist-value | 压缩列表保存时哈希集合中单个元素的最大长度 |
Redis 就会自动把 Hash 类型的实现结构由压缩列表转为哈希表。一旦从压缩列表转为了哈希表,Hash 类型就会一直用哈希表进行保存,而不会再转回压缩列表了。
- 1000,同时设置hash-max-ziplist-entries 设置为 1000,这样保证hash集合一直底层采用压缩列表的方式