Redis 3.0中的列表数据结构:基于gh_mirrors/re/redis-3.0-annotated的双向链表实现

Redis 3.0中的列表数据结构:基于gh_mirrors/re/redis-3.0-annotated的双向链表实现

【免费下载链接】redis-3.0-annotated 带有详细注释的 Redis 3.0 代码(annotated Redis 3.0 source code)。 【免费下载链接】redis-3.0-annotated 项目地址: https://gitcode.com/gh_mirrors/re/redis-3.0-annotated

在Redis 3.0中,列表(List)是一种常用的数据结构,它采用双向链表(Doubly Linked List)实现,支持高效的节点插入、删除和遍历操作。本文将深入解析Redis 3.0中列表数据结构的实现细节,基于gh_mirrors/re/redis-3.0-annotated项目的源代码展开分析。

双向链表的核心结构

Redis的双向链表实现主要包含三个核心数据结构:节点结构(listNode)、迭代器结构(listIter)和链表结构(list),定义在src/adlist.h头文件中。

节点结构(listNode)

typedef struct listNode {
    struct listNode *prev;  // 前置节点
    struct listNode *next;  // 后置节点
    void *value;            // 节点的值
} listNode;

每个节点包含指向前驱节点和后继节点的指针,形成双向链接。value字段使用void*类型,可以存储任意类型的数据,体现了Redis链表的通用性。

链表结构(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;

链表结构封装了链表的元数据,包括表头指针、表尾指针、节点数量以及三个函数指针(用于自定义节点值的复制、释放和对比操作)。len字段使得获取链表长度的操作可以在O(1)时间内完成。

迭代器结构(listIter)

typedef struct listIter {
    listNode *next;         // 当前迭代到的节点
    int direction;          // 迭代的方向
} listIter;

迭代器用于遍历链表,支持正向(从表头到表尾)和反向(从表尾到表头)两种遍历方向,由direction字段控制(AL_START_HEAD表示正向,AL_START_TAIL表示反向)。

双向链表的基本操作

Redis为双向链表提供了一系列完整的操作函数,实现了链表的创建、节点添加/删除、遍历、复制等功能,这些函数定义在src/adlist.c文件中。

链表的创建与释放

创建链表listCreate函数初始化一个新的空链表,设置表头和表尾指针为NULL,长度为0,并初始化函数指针为NULL

list *listCreate(void) {
    struct list *list;
    if ((list = zmalloc(sizeof(*list))) == NULL)
        return NULL;
    list->head = list->tail = NULL;
    list->len = 0;
    list->dup = NULL;
    list->free = NULL;
    list->match = NULL;
    return list;
}

释放链表listRelease函数遍历链表,释放所有节点及其值(如果设置了free函数),最后释放链表结构本身。

节点的添加与删除

添加节点:Redis提供了多种节点添加方式:

  • listAddNodeHead:在表头添加节点
  • listAddNodeTail:在表尾添加节点
  • listInsertNode:在指定节点的前后插入新节点

listAddNodeHead为例,其实现如下:

list *listAddNodeHead(list *list, void *value) {
    listNode *node;
    if ((node = zmalloc(sizeof(*node))) == NULL)
        return NULL;
    node->value = value;
    if (list->len == 0) {
        list->head = list->tail = node;
        node->prev = node->next = NULL;
    } else {
        node->prev = NULL;
        node->next = list->head;
        list->head->prev = node;
        list->head = node;
    }
    list->len++;
    return list;
}

删除节点listDelNode函数从链表中移除指定节点,并根据free函数释放节点值。该操作通过调整相邻节点的指针实现,时间复杂度为O(1)。

链表的遍历

链表遍历通过迭代器实现,主要函数包括:

  • listGetIterator:创建迭代器
  • listNext:获取下一个节点
  • listReleaseIterator:释放迭代器
  • listRewind/listRewindTail:重置迭代器到表头/表尾

遍历示例:

listIter *iter = listGetIterator(list, AL_START_HEAD);
listNode *node;
while ((node = listNext(iter)) != NULL) {
    // 处理节点值
    doSomethingWith(node->value);
}
listReleaseIterator(iter);

双向链表的应用场景

在Redis中,双向链表被广泛应用于多个功能模块,例如:

  1. 列表键(List):当列表键包含的元素较多,或元素为字符串且较长时,Redis会使用双向链表作为底层实现。

  2. 发布订阅(Pub/Sub):用于存储订阅频道的客户端列表。

  3. 慢查询日志src/slowlog.c中使用双向链表存储慢查询日志条目,支持高效地添加新日志和遍历日志。

  4. 监视器(Monitor):用于存储所有连接的监视器客户端。

  5. 事件循环src/ae.c中的文件事件和时间事件均通过链表管理。

双向链表的性能特点

Redis的双向链表实现具有以下性能特点:

  • 高效的节点操作:表头/表尾节点的添加、删除操作时间复杂度为O(1)。
  • 灵活的遍历方式:支持正向和反向遍历,迭代器模式避免了遍历过程中对链表结构的直接操作。
  • 内存友好:使用Redis自定义的内存分配函数zmalloczfree,便于内存使用统计和调试。
  • 多态支持:通过函数指针dupfreematch,使得链表可以存储任意类型的数据,并支持自定义的复制、释放和比较操作。

总结

Redis 3.0中的双向链表实现(src/adlist.hsrc/adlist.c)是一个高效、灵活且通用的数据结构,为Redis的多种核心功能提供了基础支持。其设计充分体现了面向对象的思想,通过封装数据和操作,实现了代码的模块化和复用。理解这一实现不仅有助于深入掌握Redis的内部工作原理,也为高性能链表的设计提供了有益的参考。

【免费下载链接】redis-3.0-annotated 带有详细注释的 Redis 3.0 代码(annotated Redis 3.0 source code)。 【免费下载链接】redis-3.0-annotated 项目地址: https://gitcode.com/gh_mirrors/re/redis-3.0-annotated

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值