Redis数据结构之RedisObject

        Redis是一种高性能的开源内存数据存储系统,它支持多种不同类型的键值对。这些键值对的实现是基于一系列底层的数据结构。Redis底层数据结构包括字符串、双端链表、字典、压缩列表、整数集合等,但是Redis为了加快读写速度,并没有直接使用这些数据结构,而是在此基础上又包装了一层称之为RedisObject,RedisObject 有五种对象:字符串(String)、列表(List)、集合(Set)、有序集合(Sorted Set)和哈希(Hash)。

        RedisObject是 Redis 的对象系统的核心,它是所有数据类型的最外层的一层结构定义。任意数据类型的键和值都会被封装为一个RedisObject。        

 RedisObject底层设计

typedef struct redisObject {
    // 类型
    unsigned type:4个bit;
    // 编码
    unsigned encoding:4个bit;
    // 对象最后一次被访问的时间
    unsigned lru:REDIS_LRU_BITS; /* LRU_BITS为24bit*/
//引用计数
    int refcount;4个字节
    // 指向实际值的指针
    void *ptr;8个字节
} robj;

下面分别解释一下各个字段的含义:

  • type 记录了对象的类型占4个bit位,目前Redis支持的对象类型如下:

类型常量

常量对应的对象类型

OBJ_STRING

字符串(String)

OBJ_LIST

列表(List)

OBJ_SET

集合(Set)

OBJ_ZSET

有序集合(Sorted Set)

OBJ_HASH

哈希(Hash)

  • ptr 指向对象的底层实现数据结构,即为具体对象值,占8个字节,这个后面会具体分析。
  • encoding表示 ptr 指向的具体数据结构的编码方式,占4个bit位,Redis支持的编码如下:

编码常量

编码所对应的底展数据结构

OBJ_ENCODING_INT

long 类型的整数

OBJ_ENCODING_EMBSTR

embstr编码的简单动态字符串

OBJ_ENCODING_RAW

简单动态字符申

OBJ_ENCODING_HT

字典

OBJ_ENCODING_LINKEDLIST  

双端链表,Redis3.2之前使用

OBJ_ENCODING_ZIPLIST

压缩列表

OBJ_ENCODING_QUICKLIST

双端链表与压缩列表结合,Redis3.2之后使用

OBJ_ENCODING_INTSET

整数集合

OBJ_ENCODING_SKIPLIST

跳跃表

OBJ_ENCODING_ZIPMAP

压缩Map,Redis2.6之前使用

OBJ_ENCODING_STREAM

Stream流

         上面提到的type 用于标识 String、Hash、List、Set、Sorted Set五种数据类型、encoding 用于标识底层数据结构。通过这两个字段的组合,同一种数据类型也有多种实现方式,一个完整的映射关系如下表:

类型type

编码encoding

描述

OBJ_STRING

OBJ_ENCODING_INT

整数实现字符串对象

OBJ_STRING

OBJ_ENCODING_EMBSTR

embstr编码实现字符串对象

OBJ_STRING

OBJ_ENCODING_RAW

sds实现字符串对象

OBJ_LIST

OBJ_ENCODING_LINKEDLIST  

双端链表实现列表对象

OBJ_LIST

OBJ_ENCODING_ZIPLIST

压缩链表实现列表对象

OBJ_LIST

OBJ_ENCODING_QUICKLIST

双端链表+压缩链表相结合

OBJ_LIST

OBJ_ENCODING_LISTPACK

紧凑链表取代了压缩链表

OBJ_SET

OBJ_ENCODING_INTSET

整数集合实现集合对象

OBJ_SET

OBJ_ENCODING_HT

字典实现集合对象

OBJ_ZSET

OBJ_ENCODING_ZIPLIST

压缩链表实现有序集合对象

OBJ_ZSET

OBJ_ENCODING_SKIPLIST

跳跃表实现有序集合对象

OBJ_HASH

OBJ_ENCODING_ZIPLIST

压缩表实现Hash对象

OBJ_HASH

OBJ_ENCODING_HT

字典实现Hash对象

  • lru:表示该对象最后一次被访问的时间,其占用24个bit位。便于判断空闲时间太久的key。
  • refcount 表示引用计数,占四个字节,由于 C 语言并不具备内存回收功能,Redis 在自己的对象系统中添加了这个属性,当一个对象的引用计数为0时,则表示该对象已经不被任何对象引用,则可以进行垃圾回收了。

 refcount:记录了对象被引用的次数。在Redis中,每个数据对象都有一个引用计数器。

  • 当这个数据对象被新的键值对引用时,其引用计数就会增加1。
  • 当这个数据对象被删除或者没有任何键值对引用时,其引用计数就会减少1。
  • 当引用计数减少到0时,Redis就会自动释放这个数据对象所占用的内存空间。

QA扩展一下:Java中由于引用计数法解决不了循环引用的问题,所以 Java 中使用了可达性分析算法,那么 Redis 有没有考虑循环引用的问题呢?

        由于C语言跟贴近操作系统,直接跟操作系统交互,命令执行响应比较快,所以Redis选择C语言进行编写可以提高性能,但是C 语言不具备自动回收内存功能,于是乎Redis自己构建了一个内存回收机制。

        创建一个新对象,redisObject 对象中的refcount属性就会加1,对象被一个新程序使用,调用incrRefCount函数进行加 1,如果有对象不再被应用程序使用了,那么它就会调用decrRefCount函数进行减 1,当对象的引用计数值为 0 的时候,那么这个对象所占用的内存就会被释放。

        从这里可以看出来,这其实就是Java虚拟机中引用计数的内存回收机制,在Java中这种回收机制不被使用,因为它不能解决循环引用的问题。

循环引用举例:A引用B,B引用C,C引用A。

        Redis通过在配置文件中修改相关的配置,来达到解决循环引用的问题,在Redis的配置文件里,Windows的配置文件是redis.windows.conf,Linux系统的配置文件是redis.conf。

        在配置文件中有一个配置:maxmemory-policy,当内存使用达到最大值时,redis使用的清楚策略,默认配置是noeviction

  • volatile-lru 删除已有的过期时间的key
  • allkeys-lru 删除所有的key
  • volatile-random 已有过期时间的key 随机删除
  • allkeys-random 随机删除key
  • volatile-ttl 删除即将过期的key
  • noeviction 不删除任何key,只是返回一个写错误,这个是默认选项 对于整数值的字符串对象(例如:1,2,3这种的)可实现内存共享。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值