likes
comments
collection
share

Redis 源码分析链表(list)

作者站长头像
站长
· 阅读数 78

「这是我参与2022首次更文挑战的第30天,活动详情查看:2022首次更文挑战」。

list 简介

redis 的链表没有什么特别之处,就是普通的双向链表 adlist.c/listNode。

typedef struct listNode {
    struct listNode *prev;
    struct listNode *next;
    void *value;
} listNode;

多个 listNode 可以通过 prev 和 next 组成双端链表,如下所示:

Redis 源码分析链表(list) 说明:头节点和尾节点的 prev, next 分别指向 null。

可以通过 listNode 结构来组成链表,但使用 adlist.c/list 来持有链表会更加方便:

typedef struct list {
    // 头节点
    listNode *head;
    // 尾节点
    listNode *tail;
    // 节点值复制函数
    void *(*dup)(void *ptr);
    // 节点值释放函数
    void (*free)(void *ptr);
    // 节点值对比函数
    int (*match)(void *ptr, void *key);
    // 链表所包含的字节数量
    unsigned long len;
} list;

一个 list 结构和三个 listNode 结构的链表

Redis 源码分析链表(list)

链表特征

  • 双端:链表节点带有 prev 和 next 指针, 获取某个节点的前置节点和后置节点的复杂度都是 O(1).
  • 无环:头节点的 prev 指针和表尾 next 指针都指向 null, 对链表的访问 null 为终点。
  • 带表头指针和表尾指针:通过 list 结构的 head 指针和 tail 指针,程序获取链表的表头节点和表尾节点的复杂度为 O(1).
  • 带链表长度计数器:程序使用 list 结构的 len 属性来对 list 持有的链表节点说开那个进行计数,程序获取链表中节点数量的负载度为 O(1).
  • 多态:链表节点使用 void* 指针来保存节点值,并且可以使用 list 结构中的 dup、free、match 三个属性为节点值来设置类型特定函数。所以链表可以用于保存各种不同类型的值。

迭代器

这里链表的迭代和数据是分配开的,采用的是类似迭代器模式,这个思想就是很多场景用到的 leveldb 中,大家可以搜索一下迭代器模式。

typedef struct listIter {
    listNode *next;
    int direction;
} listIter;

listIter *listGetIterator(list *list, int direction)
{
    listIter *iter;

    if ((iter = zmalloc(sizeof(*iter))) == NULL) return NULL;
    if (direction == AL_START_HEAD)
        iter->next = list->head;
    else
        iter->next = list->tail;
    iter->direction = direction;
    return iter;
}

运用场景

  • 链表广泛用韵用实现 Redis 的各种功能,比如列表键、发布订阅、满查询、监视器等。
  • 通过为链表设置不同的类型特定函数,Redis 链表可以用于保存各种不同类型的值。

参考资料

  • redis.io
  • 《Redis 设计与实现》黄健宏
转载自:https://juejin.cn/post/7064988976154673159
评论
请登录