Redis String字符串
String类型结构
保存数据结构(SDS)
保存数据包含字符串类型,String类型简单字符串(simple Dynamic String, SDS)结构保存,len、alloc、buf。len、alloc是额外的开销
。
buf:字节数组,保存实际的数据。在字节最后加”\0”表示结束,额外占用1字符。
len:占4字节,表示buf的已用长度。
alloc:占4字节,表示buf实际分配长度,一般大于len。
RedisObject结构
RedisObject包含8字节元数据(最后访问时间、被引用次数等)和8字节指针(指向实际数据SDS指针),这个指针会根据数据结构具体数据类型做处理。
int、embstr和raw三种编码模式区别
int编码
当保存的数类型是整数是,RedisObject中的指针直接赋值为整数数据,不用额外的指针在指向整数,节约空间
embstr编码
保存的数据是字符串类型,并且小于等于44字节时,RedisObject中的元数据、指针和SDS是一块连续的内存空间区域,避免内存碎片化。
raw编码
当字符串大于44字节,SDS的数据量就开始变多,SDS和RedisObject不放在一起,单独给SDS分配独立空间,并用指针指向SDS
dictEntry结构
Redis是使用一个全局hash表保存所有键值对,哈希表每项都是一个dictEntry结构体,用来指向一个键值对。
dictEntry结构中有三个8字节的指针,分别是key、value以及下个dictEntry,三个指针共24字节。实际分配32字节空间。
redis使用内存分配库jemalloc,jemalloc是按照接近N的2的幂次作为分配空间。比如申请6字节空间,会分配8字节空间。
节省空间的数据结构
压缩列表是非常节省内存的结构。
压缩列表构成
表头三个字段zlbytes、zltail和zllen,代表列表长度、列表尾的偏移量以及列表中的entry个数。压缩列表尾部zlend,表示列表结束。
压缩列表节省空间是因为使用连续的entry保存数据,无需额外的指针连接,这样就节省指针所用的空间
entry结构
prev_len
表示前一个entry的长度。prev_len有两种取值情况:1字节和5字节。
- 取1字节时,表示上一个entry的长度小于254字节,255是zlend的默认值,所以不使用
- 取5字节时,表示上一个entry的长度大于254字节
1字节的值表示数值范围时0到255,有符号字节 数值范围127~-128
https://blog.csdn.net/weixin_38357164/article/details/87912475
len
表示自身长度,4字节
encoding
表示编码格式,1字节
content
保存实际数据
Redis 基于压缩列表实现了 List、Hash 和 Sorted Set 这样的集合类型,这样做的最大好处就是节省了 dictEntry 的开销。
Redis Hash 类型
Redis Hash 类型的两种底层实现结构,分别是压缩列表和哈希表。Hash 类型设置了用压缩列表保存数据时的两个阈值,一旦超过了阈值,Hash 类型就会用哈希表来保存数据了。
这两个阈值分别对应以下两个配置项:
- hash-max-ziplist-entries:表示用压缩列表保存时哈希集合中的最大元素个数。
- hash-max-ziplist-value:表示用压缩列表保存时哈希集合中单个元素的最大长度。
如果我们往 Hash 集合中写入的元素个数超过了 hash-max-ziplist-entries,或者写入的单个元素大小超过了 hash-max-ziplist-value,Redis 就会自动把 Hash 类型的实现结构由压缩列表转为哈希表。
一旦从压缩列表转为了哈希表,Hash 类型就会一直用哈希表进行保存,而不会再转回压缩列表了。在节省内存空间方面,哈希表就没有压缩列表那么高效了。
为了能充分使用压缩列表的精简内存布局,我们一般要控制保存在 Hash 集合中的元素个数。
注意
在保存的键值对本身占用的内存空间不大时,String 类型的元数据开销就占据主导了,这里面包括了 RedisObject 结构、SDS 结构、dictEntry 结构的内存开销。针对这种情况,我们可以使用压缩列表保存数据。
我还想再给你提供一个小方法:如果你想知道键值对采用不同类型保存时的内存开销,可以在这个网址里输入你的键值对长度和使用的数据类型,这样就能知道实际消耗的内存大小了。建议你把这个小工具用起来,它可以帮助你充分地节省内存。