0
点赞
收藏
分享

微信扫一扫

Redis核心技术与实战-学习笔记(十一):为啥不只有String


一.图片存储系统

需求:能够快速记录图片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)结构体保存。

                                            

Redis核心技术与实战-学习笔记(十一):为啥不只有String_数据库

在SDS中,buf保存实际数据,len和alloc是SDS结构体的额外开销。

RedisObject

相同元数据要记录(最后一次访问时间,被引用的次数),所以Redis会用RedisObject记录这些相同元数据,同时指向实际数据。

元数据和一个8字节的指针,这个指针进一步指向具体数据类型的实际数据所在。

       

Redis核心技术与实战-学习笔记(十一):为啥不只有String_数据_02

  •  int编码模式:       保 保存是Long类型整数时,RedisObject中的指针就直接赋值为整数数据,不用额外开辟指针空间指向整数。
  • embstr编码:  保存的是字符串数据,但是字符串小于等于44字节,RedisObject中的元数据,指针和SDS是一块连续的内存区域,这样就可以避免内存碎片。
  • 大于44字节时,SDS数据量增多,Redis分配独立的空间,并用指针指向SDS结构。

Redis核心技术与实战-学习笔记(十一):为啥不只有String_database_03

每个 int 编码的 RedisObject 元数据部分占 8 字节,指针部分被直接赋值为 8 字节的整数了。此时,每个 ID 会使用 16 字节,加起来一共是 32 字节。但是,另外的 32 字节去哪儿了呢?

     Redis使用全局哈希表保存所有键值对,哈希表的每一项都是一个dictEntry的结构体,用来指向一个键值对。

     

Redis核心技术与实战-学习笔记(十一):为啥不只有String_redis_04

三个 8 字节的指针,分别指向 key、value 以及下一个 dictEntry,三个指针共 24 字节

内存分配库jemalloc

分配32字节。

可以节省内存的数据结构

           压缩列表:Redis中最省内存的结构。

           压缩列表的构成

zlbytes

列表长度


zltail

列表尾偏移量


zllen

列表中的entry个数


zlend

列表结束

尾部

Redis核心技术与实战-学习笔记(十一):为啥不只有String_元数据_05

连续的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集合一直底层采用压缩列表的方式
举报

相关推荐

0 条评论