Java 杂谈程序员技术栈数据结构和算法分析

redis源码分析(三):基本数据结构

2018-07-31  本文已影响5人  msrpp

redis中使用的数据结构有:

本文只阐述inset,ziplist,skiplist三种数据结构。

1.ziplist

直接借用代码注释上ziplist的存储结构:

/*
area        |<---- ziplist header ---->|<----------- entries ------------->|<-end->|

size          4 bytes  4 bytes  2 bytes    ?        ?        ?        ?     1 byte
            +---------+--------+-------+--------+--------+--------+--------+-------+
component   | zlbytes | zltail | zllen | entry1 | entry2 |  ...   | entryN | zlend |
            +---------+--------+-------+--------+--------+--------+--------+-------+
                                       ^                          ^        ^
address                                |                          |        |
                                ZIPLIST_ENTRY_HEAD                |   ZIPLIST_ENTRY_END
                                                                  |
                                                        ZIPLIST_ENTRY_TAIL
size              1/5 bytes      1/2/5 bytes    ?   
            +---------------+---------------+-------+
component   | pre_entry_len | cur_entry_len | value |
            +---------------+---------------+-------+

这是个变长的结构,pre节点小于254则pre_entry_len占用1字节,否则5字节,为什么不是255字节呢?255会和zlend产生冲突;cur_entry_len中包含了编码信息(以字符串编码还是数字,数字编码能减少内存长度)和本节点的长度信息,数字格式编码固定占用1字节;字符串格式编码数据小于127字节占1字节,小于0x3fff 占用2字节,否则5个字节。

因此ziplist有如下特点:

了解了数据结构,思考下如何对其增加一个节点p,p的大小为sp。

经过以上步骤,就可以大概还原redis中的代码实现了,也可以看出来ziplist的增删代价是很大的,改变一个节点的复杂度最差是(n^2);一个节点改动,可能牵一发而动全身,所以ziplist是不适合大数据量的存储的。

2.inset

inset 实际是一个整数数组,结构如下:

typedef struct intset {
    
    uint32_t encoding;

    uint32_t length;

    int8_t contents[];

} intset;

其中encoding是编码方式,有16位,32位,64位三种取值;length是inset集合中保存的元素个数,contents为保存的数据。下面是inset的特点和实现方式.

根据这些特点,写出inset的实现,想必不难,inset的特点是在存储的数据为小值的时候,占用的内存较小,但是只要插入了一个64位的数据,那么inset就没有优势了,插入和删除的时间复杂度都是o(n),因此不适合做大数量的存储。

3.skiplist

跳表是一个特殊的有序链表,盗一张网图吧。

跳表结构

结构如下

typedef struct zskiplist {

    // 表头节点和表尾节点
    struct zskiplistNode *header, *tail;

    // 表中节点的数量
    unsigned long length;

    // 表中层数最大的节点的层数
    int level;

} zskiplist;

节点的结构

typedef struct zskiplistNode {

    // 成员对象
    robj *obj;

    // 分值
    double score;

    // 后退指针
    struct zskiplistNode *backward;

    // 层
    struct zskiplistLevel {

        // 前进指针
        struct zskiplistNode *forward;

        // 跨度
        unsigned int span;

    } level[];

} zskiplistNode;

简述下跳表的特点

如何插入一个数据?redis排序顺序是从头到尾,按照score从小到大。如果要增加一个值,首先需要确定插入后的顺序,假设跳表为skl ,新节点为n。

如何查找一个节点p?

迭代器如何设计?

上一篇 下一篇

猜你喜欢

热点阅读