Redis并无直接使用SDS、双端链表、字典、压缩列表、整数集合等这些数据结构,而是基于这些数据结构建立了一个对象系统,这个对象系统包含字符串对象(String)、列表对象(List)、哈希对象(Hash)、集合对象(Set)和有序集合对象(SortedSet)这5种类型,每种对象都用到了至少一种数据结构。redis
这种设计的好处是将咱们真实使用的对象提到了底层数据结构层级之上,让咱们能够对同一个对象类型有不一样的实现,而后根据不一样的场景使用不一样的数据结构实现,优化使用效率。算法
除此以外,Redis对象系统还实现了基于引用计数范式的内存回收机制(即每一个对象还有一个引用计数字段,每当有其余对象引用便计数加1,当引用计数为0时,表示没有其余对象引用这个对象,便可以回收,这种访问有个明显缺点,即存在相互引用状况致使对象不能被回收),回收再也不使用对象的内存;另外,Redis还经过引用计数方式实现了对象共享机制,这一机制能够在适当条件下,经过让多个数据库键共享同一个对象节约内存。数据库
最后,Redis对象带有访问时间记录信息,能够用于计算数据库键的空转时长,在服务器启用了maxmemory功能而且回收内存的算法为volatile-lru或者allkeys-lru的状况下,当内存超过maxmemory设置的阀值,空转时长较大的那些键可能会优先被服务器回收。具体能够查看配置文件maxmemory和maxmemory-policy选项的说明介绍。缓存
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;
编码与类型对应关系以下:服务器
字符串对象编码能够是int、raw或者embstr,字符串对象是惟一会潜逃到其余数据结构中使用的结构,编码切换条件以下:数据结构
embstr与raw(SDS)结构彻底同样,惟一的却别是若是使用raw(SDS)会进行两次内存分配,先分配建立SDS,而后分配建立redisObject,再使ptr指针指向SDS,即SDS和redisObject的内存地址能够不连续。而embstr的SDS结构和redisObject结构内存连续,只须要一次内存分配,embstr优点:优化
字符串各个数据结构之间调用不一样方法的转换:编码
列表对象的编码能够是ziplist或者linkedlist,转换规则,当知足下面任意一条将使用linkedlist:spa
上面两个条件的阀值能够经过配置文件list-max-ziplist-value和list-max-ziplist-entries两个属性进行修改。设计
ziplist和linkedlist方法实现差别:
哈希对象的编码能够是ziplist或者hashtable,ziplist在存键值对时老是两个相关联的键值对紧挨在一块儿,建在前值在后,每次都在表尾添加,即越后添加的键值对越靠近表尾。
编码切换条件,知足任意一个,将使用hashtable:
上限阀值能够经过配置中的hash-max-ziplist-value和hash-max-ziplist-entries属性修改。
不一样数据结构方法实现差别:
集合对象的编码能够是intset或者hashtable,切换条件以下,知足任一会使用hashtable:
方法实现差别:
有序集合的编码能够是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编码:
能够用配置中zset-max-ziplist-entries和zset-max-ziplist-value属性修改
方法实现区别: