Redis 对象

    Redis并无直接使用SDS、双端链表、字典、压缩列表、整数集合等这些数据结构,而是基于这些数据结构建立了一个对象系统,这个对象系统包含字符串对象(String)、列表对象(List)、哈希对象(Hash)、集合对象(Set)和有序集合对象(SortedSet)这5种类型,每种对象都用到了至少一种数据结构。redis

    这种设计的好处是将咱们真实使用的对象提到了底层数据结构层级之上,让咱们能够对同一个对象类型有不一样的实现,而后根据不一样的场景使用不一样的数据结构实现,优化使用效率。算法

    除此以外,Redis对象系统还实现了基于引用计数范式的内存回收机制(即每一个对象还有一个引用计数字段,每当有其余对象引用便计数加1,当引用计数为0时,表示没有其余对象引用这个对象,便可以回收,这种访问有个明显缺点,即存在相互引用状况致使对象不能被回收),回收再也不使用对象的内存;另外,Redis还经过引用计数方式实现了对象共享机制,这一机制能够在适当条件下,经过让多个数据库键共享同一个对象节约内存。数据库

    最后,Redis对象带有访问时间记录信息,能够用于计算数据库键的空转时长,在服务器启用了maxmemory功能而且回收内存的算法为volatile-lru或者allkeys-lru的状况下,当内存超过maxmemory设置的阀值,空转时长较大的那些键可能会优先被服务器回收。具体能够查看配置文件maxmemory和maxmemory-policy选项的说明介绍。缓存

redis.h/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;
  • type:表示当前redis对象的类型,能够使用type命令查看某个key对应的值的redisObject的类型,包含5种可能值:
    • REDIS_STRING 字符串对象,type命令输出为"string"
    • REDIS_LIST 列表对象,type命令输出为"list"
    • REDIS_HASH 哈希对象,type命令输出为"hash"
    • REDIS_SET 集合对象,type命令输出为"set"
    • REDIS_ZSET 有序集合对象,type命令输出为"zset"
  • encoding:表示当前对象使用的数据结构类型,即ptr指针指向的对象对应的数据结构,能够使用obejct encoding <key>命令查出对应值的数据结构,可能值以下:
    • REDIS_ENCODING_INT long类型的整数,object encoding命令返回值“int”
    • REDIS_ENCODING_EMBSTR embstr编码的简单动态字符串,object encoding命令返回值“embstr”
    • REDIS_ENCODING_RAW 简单动态字符串,object encoding命令返回值“raw”
    • REDIS_ENCODING_HT 字典,object encoding命令返回值“hashtable”
    • REDIS_ENCODING_LINKEDLIST 双端链表,object encoding命令返回值“linkedlist”
    • REDIS_ENCODING_ZIPLIST 压缩链表,object encoding命令返回值“ziplist”
    • REDIS_ENCODING_INTSET 整数集合,object encoding命令返回值“intset”
    • REDIS_ENCODING_SKIPLIST 跳跃表,object encoding命令返回值“skiplist”

编码与类型对应关系以下:服务器

  • refcount:引用计数,当计数为0时,用于内存回收
  • ptr:指向对象实际结构的指针

 

字符串对象

字符串对象编码能够是int、raw或者embstr,字符串对象是惟一会潜逃到其余数据结构中使用的结构,编码切换条件以下:数据结构

  • 若是保存的是一个字符串值(不能转化成整数),且长度大于32字节,使用raw即SDS
  • 若是保存的是一个字符串值(不能转化成整数),且长度小于等于32字节,使用embstr
  • 若是保存的是一个整数,使用int即c语言的long类型

embstr与raw(SDS)结构彻底同样,惟一的却别是若是使用raw(SDS)会进行两次内存分配,先分配建立SDS,而后分配建立redisObject,再使ptr指针指向SDS,即SDS和redisObject的内存地址能够不连续。而embstr的SDS结构和redisObject结构内存连续,只须要一次内存分配,embstr优点:优化

  • 只须要一次内存分配
  • 只须要一次内存回收
  • 因为内存连续,更利于CPU高速缓存载入

字符串各个数据结构之间调用不一样方法的转换:编码

列表对象

列表对象的编码能够是ziplist或者linkedlist,转换规则,当知足下面任意一条将使用linkedlist:spa

  • 列表对象所存字符串元素长度有一个及以上大于等于64字节;
  • 列表对象所存元素数量大于等于512;

上面两个条件的阀值能够经过配置文件list-max-ziplist-value和list-max-ziplist-entries两个属性进行修改。设计

ziplist和linkedlist方法实现差别:

哈希对象

哈希对象的编码能够是ziplist或者hashtable,ziplist在存键值对时老是两个相关联的键值对紧挨在一块儿,建在前值在后,每次都在表尾添加,即越后添加的键值对越靠近表尾。

编码切换条件,知足任意一个,将使用hashtable:

  • 任意一个或以上键或者值长度大于等于64字节
  • 键值对数量大于等于512

上限阀值能够经过配置中的hash-max-ziplist-value和hash-max-ziplist-entries属性修改。

不一样数据结构方法实现差别:

集合对象

集合对象的编码能够是intset或者hashtable,切换条件以下,知足任一会使用hashtable:

  • 集合全部元素有一个或以上不是整数值
  • 集合元素数量大于等于512,能够经过配置set-max-intset-entries设置

方法实现差别:

有序集合对象

有序集合的编码能够是ziplist或者skiplist。ziplist编码也和集合对象使用同样,只是插入时会确保score的升序排列,有序列表的skiplist数据结构有些特殊,源代码以下:

/*
 * 有序集合
 */

typedef struct zset {

  // 字典,键为成员,值为分值
  // 用于支持 O(1) 复杂度的按成员取分值操做
  dict *dict;

  // 跳跃表,按分值排序成员
  // 用于支持平均复杂度为 O(log N) 的按分值定位成员操做
  // 以及范围操做
  zskiplist *zsl;

} zset;

同时使用zskiplist和dict,这样作是为了保证能够同时使用skiplist进行高效查找遍历,使用dict高效经过key获取score,因为使用了共享对象,因此内存并无浪费。

编码转换条件,知足任一对象使用ziplist编码:

  • 保存的元素数量大于等于128
  • 保存的元素长度由一个或以上长度大于等于64字节

能够用配置中zset-max-ziplist-entries和zset-max-ziplist-value属性修改

方法实现区别:

相关文章
相关标签/搜索