再谈Redis的数据类型String

1.String不是万能的

在我们日常使用Redis的过程中,String这个数据是最常用的,甚至我们会费尽心思的把一些比较别扭的数据,强制转换成String来存储在Redis中。为什么这么做呢?其实我觉得就是因为我们操作String简单、明了,还有就是,对Redis中的String不了解,只会用,别人这么用,我也这么用,反正也没见到出什么问题。那么我们下面考虑一个问题:假如有上亿个整数型的键值对需要存储,如(1111111111,2222222222)等等,我们应该怎么存储呢?可能不加思索,就直接是把1111111111作为key,把2222222222作为value来存储 ,即set 1111111111 2222222222 。这似乎没有问题,但是随着我们数据量的急剧上升,我们会发现,Redis内存占用也会变得很大。按理说,上面的这一个键值对占用的内存为(8+8)=16字节(Byte),可实际上它却占用了64个字节(Byte),直接扩大了3倍(48个字节)。天哪,我存了这么点数据,竟然占用了这么多的内存空间,真是不看不知道,一看吓一跳。那么令人奇怪的是,其他那48个字节被谁占用了呢?客官,我们往下继续谈。

2.简单动态字符串(Simple Dynamic String—SDS)

通过前面的几篇博客我们知道,Redis底层存储字符串是采用的一种叫做简单动态字符串的数据结构,以下我们简称为SDS。

struct sdshdr{
     //记录buf数组中已使用字节的数量
     //等于 SDS 保存字符串的长度
     int len;
     //记录 buf 数组中未使用字节的数量
     int free;
     //字节数组,用于保存字符串
     char buf[];
}

从结构体中我们可以看到,除了真实存储数据的buf外,还有两个元素len和free,由于每个都是int类型,所以占用了(4+4)=8个字节。一般的,我们把String类型中记录数据长度等这些信息的数据,称为元数据。所以当保存的数据比较小的时候,元数据的开销占比就比较大了,有点主次不分的感觉。
除了SDS中元数据造成的内存开销,RedisObject的结构体也会有额外的开销。一个RedisObject包含一个8个字节元数据和一个8个字节的指针,以String类型为例:
在这里插入图片描述
nter)
但是幸运的是,Redis对整数和SDS内存布局做了特殊的处理,主要体现在以下三点;

  1. 当保存的是整数类型时,RedisObject中的指针就直接赋值为整数数据了,这样就不需要额外的指针指向具体的整数值了,减少了内存开销;
  2. 如果保存的是字符串,当字符串小于等于44个字节(Byte)时,RedisObject中的元数据、指针、和SDS都存储在同一块连续的内存上,这样做的好处是在数据量小的时候,避免内存碎片的产生,这种方式也被称为embstr编码方式;
  3. 如果保存的是字符串,当字符串大小44个字节(Byte)时,SDS的数据量就大了,RedisObject会给SDSf分配独立的空间,并用指针指向SDS结构体,这种方式被称为raw编码方式。
    4.在这里插入图片描述
    根据上面我们分析,我们计算一下第一部分说的那个键值对(1111111111,222222222)占用多少空间。因为是int编码,所以RedisObject中的元数据占用8Byte,每个整数占用8Byte,所以每个RedisObject一共占用(8+8)=16Byte的内存空间,又由于key和value共两个RedisObject结构体,所以共占用了162=32Byte的内存空间。到了这里,好像离我们最初得出的64Byte的内存空间还差32Byte,那32Byte去哪里了呢?
    我们知道,Redis是使用一个全局哈希表来保存所有的键值对,哈希表的每一个每一项都是一个dictEntry(字典项),用来指向键值对。dictEntry共有3个8字节(Byte)的指针,分别指向key、value、以及下一个dictEntry,即dictEntry共有3
    8=24Byte。
    在这里插入图片描述
    dictEntry的三个指针实际占了24Byte,但为什么却是占用了32个Byte呢?原来是因为Redis使用了内存分配库jemalloc,它在分配内存的时候,会根据我们申请的字节数M,找一个比M大并且最接近M的2的幂次数作为分配空间,这样减少频繁分配的次数。
    如果你申请 10 字节空间,jemalloc 实际会分配 816字节空间;如果你申请 24 字节空间,jemalloc 则会分配 32 字节。所以,dictEntry 结构就占用了 32 字节。
    从上面的分析,我我们看到,键值对(1111111111,222222222)明明仅需要16 Byte的空间就足够了,但是作为String类型时,却需要使用64Byte的内存空间,其中48个Byte都没有真正用来保存实际的数据,额外的内存空间开销就很大了。

3.小结

从上面的分析我们看到,String这个数据是最常用的数据类型,却产生了极大的空间浪费,有些时候我们可以考虑一下使用其他类型的数据来处理,至于用什么类型来替换,我们可以根据不同的场景来,具体就不再进行阐述了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值