各种数据结构 ( sds、dict、skiplist、intset、ziplist 等 ),作为 Redis对外提供的各种数据类型的底层组成部分;但是各种数据类型的键值对并不是直接使用这些数据结构,而是基于这些数据结构构建了一个对象系统(字符串对象、列表对象、哈希对象、集合对象、有序集合对象 )
        每种对象都至少使用了一种数据结构。

对象特性
Redis 在执行命令之前,根据对象的类型就可以判断 命令是否可以执行
依托对象,可以针对不同的使用场景,为对象设置不同的数据结构实现,从而优化对象在不同场景下的使用效率
Redis 对象系统实现了基于引用计数技术的内存回收机制,当程序不再使用某个对象的时候,这个对象所占用的内存就会被自动释放
Redis 使用引用计数技术实现了对象共享机制,一定场景下,可以通过让多个数据库键共享同一个对象来节约内存
Redis 对象带有访问时间记录,可以用来计算数据库键的空转时间,在服务器启用 maxmemory 的情况下,空转时长较大的键可能被优先删除

typedef struct redisObject {   
    unsigned type:4;            // 对象类型  
    unsigned encoding:4;        // 对象中存放数据的编码方式   
    unsigned lru:LRU_BITS;      // 记录最后一次被命令访问的时间   
    int refcount;               // 对象的引用计数
    void *ptr;                  // 指向底层实现的数据结构指针
} robj;

type 
#define OBJ_STRING 0    // 字符串对象
#define OBJ_LIST   1    // 列表对象
#define OBJ_SET    2    // 集合对象
#define OBJ_ZSET   3    // 有序集合对象
#define OBJ_HASH   4    // 哈希对象

        Redis键值对,键总是字符串对象,而值是各种对象,比如常说的列表键,实际就是列表键对象,键为字符串对象,值是列表对象

encoding 

#define OBJ_ENCODING_RAW        0   // 简单动态字符串
#define OBJ_ENCODING_INT        1   // long 类型整数
#define OBJ_ENCODING_HT         2   // 字典
#define OBJ_ENCODING_LINKEDLIST 4   // 双端链表
#define OBJ_ENCODING_ZIPLIST    5   // 压缩列表
#define OBJ_ENCODING_INTSET     6   // 整数集合
#define OBJ_ENCODING_SKIPLIST   7   // 跳跃表
#define OBJ_ENCODING_EMBSTR     8   // embstr 编码的简单动态字符串
#define OBJ_ENCODING_QUICKLIST  9   // 由双端链表和压缩列表构成的快速列表

        Redis 的每一种对象类型可以对应不同的编码方式,这就极大地提升了 Redis 的灵活性和效率。可以根据不同的使用场景,来选择合适的编码方式,五种对象类型对应的底层编码方式如下

+------------------------------------------------+
+ 对象类型           编码方式
+------------------------------------------------+
+ OBJ_STRING        OBJ_ENCODING_RAW
+                   OBJ_ENCODING_INT
+                   OBJ_ENCODING_EMBSTR
+------------------------------------------------+
+ OBJ_LIST          OBJ_ENCODING_LINKEDLIST
+                   OBJ_ENCODING_ZIPLIST
+                   OBJ_ENCODING_QUICKLIST
+------------------------------------------------+
+ OBJ_SET           OBJ_ENCODING_INTSET
+                   OBJ_ENCODING_HT
+------------------------------------------------+
+ OBJ_ZSET          OBJ_ENCODING_ZIPLIST
+                   OBJ_ENCODING_SKIPLIST
+------------------------------------------------+
+ OBJ_HASH          OBJ_ENCODING_ZIPLIST
+                   OBJ_ENCODING_HT
+------------------------------------------------+

lru 24bit
        表示该对象最后一次被访问的时间,为了计算该对象的空转时长,便于后续根据空转时长来决定是否释放该键,回收内存。通过命令OBJECT IDLETIME查看对象的空转时间
        如果服务器打开了 maxmemory 选项,在一定情况下 会 启动 lru 算法进行淘汰:
        volatile-lru 根据LRU算法删除设置了超时属性(expire)的键,直到腾出足够空间为止
        allkeys-lru 根据LRU算法删除键,不管数据有没有设置超时属性,直到腾出足够空间为止

refcount
        引用计数。redis通过引用计数实现内存回收机制,程序可以通过引用计数在适当的时候释放内存并内存回收。在创建一个对象的时候,引用计数的值会初始化为 1。
        当对象被一个新程序使用时,它的引用计数值会增加 1
        当对象不再被一个程序使用时,它的引用计数值会减去 1
        当对象的引用计数值为 0 的时候,对象占用的内存会被释放

对象共享
        对象的引用计数还带有对象共享功能。Redis 默认会建立0 ~ 9999 ( 通过 server.h: line 92 的常量 OBJ_SHARED_INTEGERS 控制 ) 每个数的对象
        也就是说 Redis 在初始化的时候已经把这10000个数字对象建立了,只要后续使用到的这个范围内的数字都会直接引用这些对象。

为什么共享对象只是使用了数字字符串呢? 
        因为服务器考虑将一个对象设置为键的值对象时,需要先检查给定共享对象跟想要创建的对象是否完全相同,只有在共享对象和目标对象完全相同的时候,程序才会使用 共享对象,所以共享对象保存的值越复杂,那么这个对比过程CPU消耗率就越高。
        数字字符串的对比验证复杂度为 O(1),字符串对比验证复杂度为 O(n),列表对象或者哈希对象的验证复杂度为 O(n2)*。所以尽管共享对象更为节约内存,但是受CPU运算的限制,Redis只包含整数值字符串的共享对象