Redis learning object system source code analysis

background knowledge:

Redis did not directly use sds, double-ended lists, dictionaries, packing lists, tables and other data structures jump directly implement key-value pair database, but based on these objects to create an object system, the target system contains five objects: characters strings, lists, hash object, a collection of objects and ordered collection of objects, string objects are only four other objects nested objects

1. We can set a variety of different data structures for the object for different usage scenarios, in order to optimize the efficiency of the use of the object under different scenarios

2.Redis object system implemented based on reference counting garbage collection mechanism

3.Redis object system also enables object sharing mechanism that under appropriate conditions, to save memory by allowing multiple databases share the same object keys

4.Redis the case of objects with access time record information that can be used to calculate the length of time idling key database, the server is enabled maxmemory function when idling long those larger key priority server may be deleted

5. When we create a new key pair redis, we will create at least two objects, an object used as a key, as the value of another object


A .Redis object system

typedef struct redisObject { 

    // type, the key value of type 
    unsigned type: . 4 ; 

    // coding decided what type of data structure implemented 
    unsigned encoding: . 4 ; 

    // time the object was last accessed 
    unsigned lru: REDIS_LRU_BITS ; / * LRU Time (relative to server.lruclock) * / 

    // reference count 
    int the refcount; 

    // pointer to the actual value of the 
    void * PTR; 

} robj;

Used to set the encoding target by encodeing property, rather than a fixed encoding type is associated with a particular object, Redis greatly enhance the flexibility and efficiency, as Redis more different usage scenarios can object to set a different coding, in order to optimize efficiency at the object of a scene

For example, the following situation:

When the object list contains fewer elements, the Redis a compression list as a list of objects in the underlying implementation, as compared to the compression list listing more economical two-terminal memory, and when a small number of elements, the memory block is stored in a continuous compression a list of lists can be faster than the dual-end loaded into the cache, but! With the increase of elements, use compression to bring a list of the advantages of getting smaller and smaller, the underlying object system will be turned to achieve a list of more powerful, more suitable for the big brother to save elements of double-ended list of other types of objects are also similar optimization is performed by using a plurality of different coding


The following are the different types of coding corresponding to the different objects:

                  | - 1. Use of a String object to achieve an integer value
                  |
string object: | - encoded string object 2. embstr dynamic string of simple realization SDS
                  |
                  | - 3. simple dynamic strings implemented using SDS the string object
           
               . | --1 ziplist use compression to achieve the object list list of
list of objects: |
               | --2 list object list using double-ended linked list implementation.        
           
               | --1 use ziplist compression hash list of objects to achieve.
Ha Greece objects: |
               . | --2 use hash object dictionary dict implementation


               |--1.使用intset整数集合实现的集合对象
集合对象: |
               |--2.使用dict字典实现的集合对象
           
                    |--1.使用ziplist压缩列表实现的有序集合对象
有序集合对象:|
                    |--2.使用跳跃表和字典实现的有序集合对象



1.字符串对象

字符串对象的编码可以是int,raw,embstr

1)当字符串对象保存的是整数值时,字符串对象的编码是int

2)当字符串对象保存的是字符串时,并且这个字符串的长度大于39字节,那么字符串对象的编码是raw

3)当字符串对象保存的是字符串时,并且这个字符串的长度小于等于39字节,那么字符串对象的编码是embstr

graphviz-c0ba08ec03934562687cc3cb79580e76edef81e3

graphviz-8ab5e59accdfa496f966cf90b45b146fb4959335graphviz-900c13b23ce79372939259603be936c955ccaa62

说明:

embstr编码是专门用于保存短字符串的一种优化编码方式

embstr编码和raw编码的区别:

raw编码会调用两次内存分配函数来分别创建redisobject和sdsstr结构,而embstr编码则通过调用一次内存分配函数来分配一块连续的空间,空间中依次包含redisobject和sdsstr两个结构

使用embstr编码来保存短字符串值有以下两个好处:

1)调用内存分配函数的次数减少了一次,并且调用内存释放函数的次数也减少了一次

2)embstr编码的字符串对象的所有数据都保存在一块连续的内存中,这样可以更好的利用缓存带来的优势、

ps:浮点类型的数据也是通过字符串值来保存的

编码可以进行特定的相互转换


2.列表对象

1.列表对象的编码可以是ziplist压缩列表或者linkedlist链表

graphviz-a8d31075b4c0537f4eb6d84aaba1df928c67c953

graphviz-84c0d231f30c740a431407c7aaf3851b96399590

问题:列表对象什么时候采用ziplist压缩列表编码,什么时候采用linkedlist链表编码?

当列表对象满足下面两个条件的时候,采用ziplist压缩列表进行编码

1)列表对象保存的所有字符串元素长度小于64字节

2)列表保存的元素数量小于512个

若不能通过满足上面两个条件的话列表将采用双端链表编码


3.哈希对象

哈希对象的编码可以是ziplist压缩列表或hashtable哈希表


当哈希对象采用ziplist压缩列表进行编码的时候:

保存了同一键值对的两个结点总是紧紧挨在一起,保存键的结点在前,保存值的结点在后

graphviz-d2524c9fe90fb5d91b5875107b257e0053794a2a

graphviz-7ba8b1f3af17e2e62cdf43608914333bf14d8e91

当哈希对象满足下面两个条件时,哈希对象采用ziplist压缩列表进行编码:

1)哈希对象保存的键值对的字符串长度都小于64字节

2)噶厦镀锡保存的键值对数量小于512个

当不满足上面两个条件时会进行编码转换采用hashtable进行编码

graphviz-68cb863d265a1cd1ccfb038d44ce6b856ebbbe3a


4.集合对象

集合对象的编码可以是intset整数集合或者hashtable哈希表

graphviz-fbd8f0e1aaad0bdef314af55d01212f83cba8b59

graphviz-3f77c5cca338422f418d6d11bc02109fc945e790
当集合对象满足下面两个条件时,集合对象采用intset编码:

1)集合对象保存的所有元素都是整数值

2)集合对象保存的元素数量不超过512个

如果不满足上面两个条件,集合对象将采用hashtable哈希表进行编码


5.有序集合对象

有序集合对象的编码可以是ziplist或者skiplist

ziplist编码:ziplist结构

skiplist编码:skiplist结构和dict结构

graphviz-61b04c9bb72915ec0374125ba9455bc6783db4ff

graphviz-243db42f3ae9ad5bb64108c999f7ce7144f166a6

graphviz-122e7ebdcd23e888fae17c21813be048c2d3f0a8

graphviz-75ee561bcc63f8ea960d0339768aec97b1f570f0

当有序集合对象满足下面两个条件时,有序集合对象采用ziplist编码

1)有序集合保存的元素数量小于128个

2)有序集合保存的所有元素成员长度小于64字节

否则的话,有序集合对象将采取skiplist编码,底层用skiplist和dict数据结构实现


关于有序集合对象采用skiplist编码的说明:

在采用skiplist编码的时候,其底层采用的是skiplist跳表和dict字典

1.通过跳跃表,有序集合可以高效的进行范围性操作

2.通过字典,有序集合可以高效的查找成员到分值的映射

值得一提的是,虽然有序集合对象在采用skiplist编码的时候,同时采用了跳跃表和字典来保存原始,但是着两种数据结构都会通过指针来共享相同原始的成员和分值,不会因此而浪费额外的内存来保存相同元素


为什么有序集合在采用skiplist编码的时候,需要通过采用跳表和字典来进行底层的实现?

1)采用字典,不采用跳表:虽然查找成员到分值的映射的复杂度为O(1),但是执行比如ZRANK命令时,程序需要对字典保存的所有元素进行排序,至少需要O(log N),以及额外的O(N)内存空间

2)采用跳表,不采用字典:虽然执行范围性操作的时间复杂度是O(log N),但是根据成员查找映射的分值的操作的复杂度将从O(1)上升到O(log N)

所以为了让有序集合的查找和范围性操作都尽可能的快速执行,Redis选择同时使用字典和跳跃表两种数据结构来实现有序集合,同时有序集合中的字典和跳跃表会共享元素的成员和分值,所以并不会造成数据重复,也不会因此浪费任何内存


Redis的内存回收

1.Redis采用引用计数法进行内存回收,该方法最大的问题就是不能解决互相引用的问题

2.对象的空转时长:当前时间减去该对象最后一次被访问的时间就是该对象的空转时长,空转时长较长的对象在内存不够时会被回收


Redis的对象共享

采用对象共享机制是为了节约内存,数据库保存的相同值的对象越多,对象共享机制就节约越多的内存

目前来说,Redis会在初始化服务器时,创建一万个字符串对象,这些对象包含了从0到9999的所有整数值,当服务器需要用到值为0到9999的字符串对象时,服务器就会共享这些对象,而不是创建新对象

需要注意的是

尽管共享复杂的对象可以节约更多的内存,但是复杂的对象比较起来也比较耗时间,比如如果共享的对象包含了多个值的对象,那么比较两个对象的复杂度为O(N*N),这个比较操作非常耗费CPU时间,所以收到CPU时间的现在,Redis只多包含整数值的字符串对象进行共享


总结:

1)Redis数据节中每个键值对的键和值都是一个对象

2)Redis共有字符串对象,列表对象,哈希对象,集合对象,有序集合对象,这五种对象,每种类型的对象至少都有两种以上的编码方式,不同的编码方式可以在不同的使用场景上使用,可以优化对象的使用效率

3)服务器在执行某些命令前,需要先检查给定的键是否可以执行指定的命令,而检查一个键的类型就是检查键对应的值的对象类型


Guess you like

Origin www.cnblogs.com/yinbiao/p/11258708.html