Redis内存优化之编码优化

本文详细介绍了Redis中的压缩列表(ziplist)数据结构,包括其节省内存的设计、结构示图、连锁更新和复杂度分析。同时,讨论了Redis对象的编码类型及其转换触发机制,如String、List、Hash、Set和ZSet的不同编码实现及转换条件。此外,还提到了Redis的内存回收、对象共享、类型检查和对象空转时长的管理策略。通过对这些内容的理解,有助于优化Redis内存使用和提升性能。
摘要由CSDN通过智能技术生成

一、压缩列表ziplist

1.1 ziplist数据结构

  • 压缩列表是一种顺序存储的数据结构,为节约内存而设计。当存储的内容是数量比较少或者比较短的时候,Redis就会使用压缩列表存储,比如列表,Hash,跳表都有可能。
  • 其优点是节约内存,不过因为复杂度是O(N),元素规模不宜过大

1.2 ziplist结构示图

  • ziplist是由特殊的编码和连续内存块组成的顺序型数据结构,如下:
    在这里插入图片描述
  • 右图中可以看到压缩列表通过几个描述字段来表示压缩列表的长度和元素个数,主体元素紧密排布。
  • 压缩列表内部没有指针,但是通过字段长度来实现遍历,比如得到前面一个元素,就是当前地址减去previous_entry_length就得到前一元素的地址,这样实现元素遍历。
  • 编码:encoding包含内容的编码和长度信息,其高位包含编码信息,低位包含content的长度信息
  • 内容:content是value部分

1.3 ziplist连锁更新

压缩列表中每个节点通过previous_entry_length记录前一个节点的长度,如果小于254只需要1个字节,如果大于则需要5字节,因此假设其中一个节点重新设置大于254,则其后面的节点长度字段都会变化,由此引发后面连锁的更新,删除也可能触发连锁更新。连锁更新会降低性能,但是因为压缩列表的元素一般不多,因此对性能几乎没有影响。

1.4 ziplist复杂度

复杂度:ziplist的操作命令最坏复杂度是O(N2),平均是O(N),前后获取节点是O(1),因为保存了前面节点的长度。

二、Redis对象和编码

2.1 Redis对象数据结构

  • Redis中每一个对象都是由RedisObj来表示,其表示了数据的相关属性以及指向数据的指针,如下:
typedef struct redisObject{
    //数据类型
    unsigned type:4;
    
    //编码
    unsigned encoding:4;

    //引用计数
    int refcount;
    
    //最后访问时间
    unsigned lru:22;

    //值指针
    void *ptr;
} robj;
字段含义相关命令
type对象类型type keyName
encoding对象编码object encoding keyName
refcount对象引用计数object refcount keyName
lru对象最后访问时间object idletime keyName
  • 查看编码类型
object encoding keyName

127.0.0.1:6379> OBJECT encoding name
"embstr"

2.2 编码类型

  • 对于Redis的五种数据类型,都有至少2种不同的编码实现,通过Object Encoding xx 查看xx的编码方式
基础类型编码类型Object Encoding命令输出转换条件
String整数 <-> SDS <-> embstr int/embstr/raw满足整数则使用整数,否则使用embstr(长度小于39,分配和释放次数减少)/SDS RAW(大于39)
Listziplist <-> 双向链表ziplist/linkedlist字符串元素小与64并且元素小与512个使用ziplist
Hashziplist <-> 字典ziplist/hashtable所有键值长度小于64且键值对少于512使用ziplist
Set整数集合 <-> 字典intset/hashtable全为整数且不超过512个则使用整数集合,反之使用字典(value为null)
ZSetziplist <-> 跳表ziplist/skiplist集合元素长度均小于64且元素少于128时使用ziplist,反之使用跳表

2.3 转换触发机制

  • String:对于整数的类型,如果不满足了就会触发编码转换,比如123的字符串变为了123a,就会转换为embstr。embstr内存块是连续的并且分配和释放只需要一次调用,适合短字符串。
  • List转换:字符串元素小于64并且元素少于512个则使用ziplist,反之使用双向链表,值可配置
list-max-ziplist-value
list-max-ziplist-entries
  • Hash:所有键和值长度都小于64且键值对少于512,使用ziplist,反之使用字典
hash-max-ziplist-value
hash-max-ziplist-entries
  • Set:元素全为整数且不超过512个则使用整数集合,反之使用字典,此时值都会置为NULL,保留key作为集合元素
set-max-intset-entries
  • ZSet:集合元素长度均小于64且元素少于128时使用ziplist,反之使用跳表;
zset-max-ziplist-value
zset-max-ziplist-entries
  • 这里补充一点,有序集合内部会维护一个字典保存元素->分值的映射,因为跳表适合根据分值进行范围查询,其擅长通过分值来操作,但不擅长通过对象查找分数,而这个就是字典擅长的,二者互补,保证了分值范围检索和对象检索分值这2个特性。

三、其他

3.1 Redis类型检查

  • Redis在执行一个命令的时候会检查该命令是否可以作用在该键上,比如rpush只能对list操作,检查是通过对象的type字段来实现的

3.2 内存回收

  • redisObject内部通过一个refCount来做引用计数,当对象不被使用的时候会减少为0,然后释放内存,通过object refcount xx 查看xx的引用计数

3.3 对象共享

  • Redis只对包含整数值的字符串对象做了共享对象,Value相同时,将key指向同样的vaule,默认0-9999一共会有1万个共享对象。
  • 注意redis不包含字符串的共享对象,因为判定字符串是否相等复杂度比价高,相对整数更加苛刻,消耗性能。

3.4 对象空转时长

  • redisObject的lru字段记录了对象最后一次被访问的时间,object idletime xx得到xx对象未被访问的时长。
  • 注意服务器设置了maxmemory的时候,如果使用lru回收算法,会将空转时长最大的优先淘汰,注意开启了lru淘汰的时候共享对象会失效。

四、小结

主要了解编码格式,有助于我们优化内存,比如对于hash或者列表我们尽量用较短的key,元素个数拆分少一点之类,对于可以使用整数的场景,尽量使用整数,可以复用内部的共享对象。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值