Redis字符创类型内存消耗的奥秘

目录

一、String 类型的内存消耗问题

        1.1 RedisObject

二、简单动态字符串

        2.1 内存消耗原因


一、String 类型的内存消耗问题

        String 是 Redis 最基本的类型,可以理解成与 memcached 一模一样,一个 key 对应一个value,value 不仅可以是 String,也可以是数字。String 类型是二进制安全的,意思是 Redis 的String 类型可以包含任何数据,比如 jpg 图片或者序列化对象。String 类型的值最大能存储 512MB。

        1.1 RedisObject

        Redis 常用的数据类型有5种,分别为:String、Hash、List、Set、Zset。无论那种数据类型,Redis 都不会直接保存而是通过 RedisObject进行存储的。RedisObject 的内容如下:

typedef struct redisObject {
  unsigned type:4;
  unsigned encoding:4;
  unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */
  int refcount;
  void *ptr;
} robj;

        RedisObject 中所包含的内容如下:

  1. type:占4个bit,目前包括 REDIS_STRING(字符串)、REDIS_LIST(列表)、REDIS_HASH(哈希)、REDIS_SET(集合)、REDIS_ZSET(有序集合)。当执行 type 命令时,便是读取RedisObject 的type 字段获取类型。
  2. encoding:占4个bit,对 Redis 支持的数据类型,都有至少两种类型的内部编码,例如对于字符串,有 int、embstr、raw 三种编码,这三种编码后文介绍。通过 encoding 属性,Redis 可以根据不同的使用场景来为对象设置不同的编码,大大提高了 Redis 的灵活性和效率。通过 object encoding 命令,可以查看对象采用的编码格式。
  3. lru:记录的是对象最后一次被访问的时间,占据的比特数不同的版本有所不同。通过对比lru 与当前时间,可以计算某个对象的空转时间(单位是秒);object idletime 命令可以显示该空转时间。object idletime 的特殊之处是他不改变 lru 的值。
  4. refcount:记录的是对象被引用的次数,类型为整型。作用主要是用于对象引用的技术和内存回收。当创建新对象时,refcount 初始值为1,当有新程序使用该对象时,refcount 加1,当对象不再被一个程序使用时,refcount 减1。当 refcount 变为0时,对象的内存会被释放。Redis 中被多次使用的对象(refcount>1),称为共享对象。Redis 为了节省内存,当有一些对象重复出现时,新的程序不会创建新的对象,而是仍然使用原来的对象。这个被重复使用的对象,就是共享对象。目前共享对象仅支持整数值的字符串对象。
  5. ptr:ptr 指针指向具体的数据,如,set hello world,ptr 指向包含字符串 world 的 SDS。        

二、简单动态字符串

        要想了解字符串为和会消耗内存,要先了解下 Redis 的动态字符串 SDS,SDS 的结构如下:

  • buf:字节数组,保存实际数据。为了表示字节数组的结束。Redis 会自动在数组最后加一个"\0",这就会额外占用一个字节的开销。
  • len:4个字节,表示buf的已用长度。
  • alloc:4个字节,表示buf的实际分配长度

        2.1 内存消耗原因

        可以看到,在 SDS 中,buf 保存实际数据,而 len 和 alloc 本身其实是 SDS 结构体的额外开销。另外,对与 String 类型来说,除了 SDS 额外开销,还有一个来自 RedisObject 结构体的开销。

        为了节省内存空间,Redis 还对 Long 类型整数和 SDS 的内存布局做了专门设计。

  • 一方面,当保存的是 Long 类型数据时,RedisObject 的指针就直接赋值为整数数据了,这样就不用额外的指针再指向整数了,节省了指针的空间开销;
  • 另一方面,当保存的是字符串数据,并且字符串小于等于 44 字节时,RedisObject 中的元数据、指针和 SDS 是一块连续的内存区域,这样就可以避免内存碎片。这种布局方式称为embstr 编码方式;当字符串大于44字节时,SDS 的数据量就开始变多了,Redis 就不在把SDS 和 RedisObject 布局在一起了,而是会给 SDS 分配独立的空间,并用指针指向 SDS 结构。这种布局方式称为 raw 编码方式。

        到这里你会发现,如果保存的字符串数据本身很小的话,那消耗在 SDS 元数据上的内存消耗就会很大,如果保存了大量这样的字符串数据,那内存消耗可想而知,保存了很多与数据无关的内容。

        还有一点是以前文章提到的,就是 Redis 在内存分配时并不是正好分配所需要的内存,而是分配的内存略大,比如申请 24 字节的内存,实际上会分配 32 字节的内存,这也是造成了内存消耗的一个原因。

        因此,在设计和使用 Redis String 类型存储数据时,应当充分考虑数据的特点和规模,合理规划数据结构,避免不必要的内存浪费,同时充分利用 Redis 提供的各种内存优化策略和配置选项。

往期经典推荐:

揭开Spring Bean生命周期的神秘面纱-CSDN博客

深入浅出 Drools 规则引擎-CSDN博客

系统优化都没做过?看这篇就够了-CSDN博客

直击Redis集群痛点:数据倾斜优化实战,打造高效分布式缓存架构-CSDN博客

决胜高并发战场:Redis并发访问控制与实战解析_redis semaphore实现并发控制-CSDN博客

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

超越不平凡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值