Redis底层数据结构之链表

Redis底层数据结构:跳表实现原理与链表优化 在Redis这个强大的内存数据库中,有着各种各样的数据结构来满足不同的需求。跳表就是其中一种非常重要的数据结构。我们的目的就是要搞清楚跳表到底是怎么实现的,它和普通的链表相比有哪些优势。本文会涵盖跳表的基本概念、实现原理、代码实现以及在Redis中的应用等方面。首先我们会介绍一些和跳表相关的术语和概念,然后用一个有趣的故事引出跳表这个主题,接着详细解释跳表和链表的核心概念,再说明它们之间的关系。之后会给出跳表的核心算法原理和具体操作步骤,还有相关的数学模型和公式。 阅读详情

Redis底层数据结构之链表

一、Redis中链表的实现

我们都知道在列表的插入与删除的操作,如果数组的中间插入一个元素,那么这个元素后的所有元素的内存地址都要往后移动。删除的话同理,只有对数据的最后一个元素进行插入删除操作时,才比较快。链表并不需要更改节点的内存地址,链表的优势在于增和删,查找时间复杂度为O(n),链表的扩展性比数组好。链表作为一种重要的数据结构广泛用于实现redis的各种功能,由于在数据结构/算法中很多时候都学过链表,这里就不啰嗦了,直接上代码;

Redis中对链表数据结构的定义在Adlist.h文件中listNode结构体,具体的结果如下:


    /* Node, List, and Iterator are the only data structures used currently. */
    /* listNode结点 */
    typedef struct listNode {
        //结点的前一结点
        struct listNode *prev;
        //结点的下一结点
        struct listNode *next;
        //节点的值
        void *value;
    } listNode;

这是链表节点的基本定义,但是为了实现链表的各项操作,方便用户调用,Redis又做了一层封装,使用list来持有链表,具体的结构定义如下:

/* listNode 列表 */
    typedef struct list {
        //链表头结点
        listNode *head;
        //链表尾结点
        listNode *tail;

        /* 下面3个指针函数为所有结点公用的方法,相当于我们在面向对象编程中类的方法*/
        // 复制链表节点所保存的值
        void *(*dup)(void *ptr);
        // 释放链表节点所保存的值
        void (*free)(void *ptr);
        // 匹配两个节点的值是否相等
        int (*match)(void *ptr, void *key);
        // 链表长度
        unsigned long len;
    } list;

还有一个数据结构是迭代器,熟悉C++ STL或者Java集合的,应该再熟悉不过了,迭代器就要是封装集合(这里是链表)的遍历规则,让用户不必拘泥于遍历细节,下面是Redis中链表的结构定义

    /* list迭代器,只能为单向 */
    typedef struct listIter {
        //当前迭代位置的下一结点
        listNode *next;
        //迭代器的方向
        int direction;
    } listIter;

列举完链表的数据结构完之后,下面我们重点挑几个函数配合代码讲解链表的具体实现过程,首先看查找函数listSearchKey,

/* Search the list for a node matching a given key.
     * The match is performed using the 'match' method
     * set with listSetMatchMethod(). If no 'match' method
     * is set, the 'value' pointer of every node is directly
     * compared with the 'key' pointer.
     *
     * On success the first matching node pointer is returned
     * (search starts from head). If no matching node exists
     * NULL is returned. */
    /* 查找链表中是否存在值为key的节点,存在则返回改节点,否则返回NULL */
    listNode *listSearchKey(list *list, void *key)
    {
        // 这里用到了迭代器,接下来我们可以看到时如何迭代遍历的
        listIter *iter;
        listNode *node;

        //获取迭代器,遍历方向是从head往后
        iter = listGetIterator(list, AL_START_HEAD);
        //遍历循环
        while((node = listNext(iter)) != NULL) {
           //如果list定义了match方法,则调用match方法比较节点的值
            if (list->match) {
                if (list->match(node->value, key)) {
                    //如果函数返回true,则代表找到结点,释放迭代器空间,返回找到的节点
                    listReleaseIterator(iter);
                    return node;
                }
            } else {
                //如果没有定义list 的match方法,则直接比较函数指针
                if (key == node->value) {
                    //如果相等,则代表找到结点,释放迭代器
                    listReleaseIterator(iter);
                    return node;
                }
            }
        }
        listReleaseIterator(iter);
        return NULL;
    }

接下我们再看一个函数的实现,头插法加入节点listAddNodeHead

/* Add a new node to the list, to head, contaning the specified 'value'
     * pointer as value.
     *
     * On error, NULL is returned and no operation is performed (i.e. the
     * list remains unaltered).
     * On success the 'list' pointer you pass to the function is returned. */
     /* 头插法加入节点 */
    list *listAddNodeHead(list *list, void *value)
    {
        listNode *node;
        //定义新的listNode,并赋值函数指针
        if ((node = zmalloc(sizeof(*node))) == NULL)
            return NULL;
        node->value = value;
        if (list->len == 0) {
            //当此时没有任何结点时,头尾结点是同一个结点,前后指针为NULL
            list->head = list->tail = node;
            node->prev = node->next = NULL;
        } else {
            //设置此结点next与前头结点的位置关系
            node->prev = NULL;
            node->next = list->head;
            list->head->prev = node;
            list->head = node;
        }
        //结点计数递增并返回
        list->len++;
        return list;
    }

链表其他的函数,这里就不一一分析了,有兴趣的朋友,把Redis源码,下载下来,一看便知。

下面来总结下Redis采用双端链表实现的好处有哪些:

  • 双端链表可实现在O(1)的实践复杂度访问前后节点
  • 无环,表头的前驱节点和表尾的后继节点指向NULL
  • 带表头和表尾节点,程序获取表头和表尾的时间复杂度为O(1)
  • 使用链表长度计数器,可在O(1)的时间复杂度内获取链表长度
  • 链表节点使用void*指针来保存节点值,并且可以通过list结构的dup、free、match三个属性为节点值设置类型特定函数,所以链表可以用于保存各种不同类型的值。
2025数学建模美赛C题完整建模思路——奥运会奖牌榜的模型 阅读详情

相关推荐

PDDL入门

PDDL的组成部分 Objects:对象,世界上我们感兴趣的事物。 Predicates:谓词,我们感兴趣的事实(例如对象的属性),可以为真,也可以为假。 An initial state:初始化状态,我们开始所处的世界状态,即开始时为真的事物。 Goal specification:目标状态,我们想要结束的世界的状态,即我们希望最终成为真实的事情。 Actions/Operators:动作/操作,改变世界状态的方式,即发生的事情改变了事实。 PDDL语法 PDDL 文件的扩展名通常是.pddl 要

工控小白2021的博客 4821

详解Redis对象的底层数据结构 — 链表

链表的实现是一个双端(具有头尾指针)、无环(头节点的头指针指向null,尾节点的尾指针指向null)、o(1)复杂度获取尾指针(便于从后往前遍历)、o(1)复杂度获取存储多少值(len属性记录大小)、多态(值由 void* 指针指向)链表则是由一个指向头节点的指针,一个指向尾节点的指针,包含所存储长度字段,以及节点值复制函数,节点值释放函数,节点值比较函数。节点包含一个指向前一个节点的指针,和一个指向后一个节点的指针,以及一个指向值的指针,因为是一个指针所以可以指向任意类型值。

qq_64481183的博客 286

SAP采购订单中项目类别B(限制)的控制逻辑

在笔者所在企业中,框架类采购订单中项目类别需要选择B(限制)。今天笔者从比较全面的角度分析了一下这个项目类别B到底可以决定些什么?控制到什么?先从后台SPRO中打开相应的配置菜单。 打开项目类别B之后,可以看到一些配置信息,这里有一点与其它配置不同的地方,即无法新增项目,也无法修改现有项目。这个疑惑今后再去解决吧。当前只研究项目类别B的控制逻辑。 下面就参考上图中的配置分段来分析和测试其控制逻辑, 1、如项目类别配置图,所需材料值为不允许;经测试,如下图在采购订单创建时,不能输入物料编号条目。

专注SAP应用技术文章写作,致力于拉近用户与系统间的距离; 6032

Redis底层之链表

Redis底层之链表Redis底层链表节点使用listNode来实现。每个listNode节点包含三个成员,一个prev指针指向前一个listNode,一个next指针指向下一个节点。一个value指针指向任何类型的值。多个listNode通过prev和next指针组成双端链表。通过一个list比较方便地来管理产生的listNode链表。list结构为链表提供了表头指针head,表尾指针tail,...

Dazhu233的博客 741

【Python深度学习】做深度学习训练时,如果数据集太大,无法一次性加载进GPU,应该如何做?

方法核心思想优点缺点适用场景实现复杂度小批量训练(Mini-Batch Training)与数据加载器(DataLoader)将数据集划分为小批量,每次只加载一个小批量到GPU中进行训练。- 易于实现- 是深度学习的标准做法- 支持多进程加载- 内存可能成为瓶颈- 需要调整批量大小避免内存不足适用于大部分场景低数据分片与分布式训练(Data Sharding & Distributed Training)将数据集分片,每个GPU或节点处理其中一部分数据,分布式训练。

若北辰 1142

基于单片机的宠物狗智能项圈设计

GPS定位功能:利用GPS模块实时获取宠物的位置,帮助主人追踪宠物的活动轨迹。活动监测:使用加速度传感器监测宠物的活动情况,如运动、休息等,并进行分析。健康监测:集成心率传感器,实时监控宠物的心率,以确保其健康。声音识别功能:通过麦克风捕捉宠物的叫声,检测宠物的状态(如饥饿、疼痛等)。蓝牙/Wi-Fi通信:通过蓝牙或Wi-Fi模块与手机APP或云端平台进行数据交互,实时反馈宠物的状态信息。电池管理:实现低功耗设计,确保设备的续航能力。

2201_76125393的博客 2281

Redis数据结构—链表

链表提供了高效的节点重排能力,以及顺序性的节点访问方式,并且可以通过增删节点来灵活地调整链表的长度,但是redis使用的C语言并没有内置这种数据结构,所以redis构建了自己的链表实现。 链表在redis中的应用非常广泛,比如列表键的底层实现之一就是链表,当一个列表键包含了数量比较多的元算,又或者列表中包含的元素都是比较长的字符串时,Redis就会使用链表作为列表键的底层实现 1.链表和链表节...

weixin_43299321的博客 384

181、LLC谐振变换器的PCB设计实战(文件归档)

PCB设计实战经验总结:LLC谐振变换器的关键要点 本文通过实际案例总结了LLC谐振变换器PCB设计中的核心经验和常见误区。重点包括: 文件命名规范(项目代号_板卡类型_版本号_日期格式) 四层板标准层叠结构设计要点 谐振回路走线原则(宽度、长度控制) 地线处理技巧(功率地/控制地隔离) 散热设计注意事项(MOSFET过孔阵列、变压器布局) 完整的文件归档清单(Gerber、钻孔、装配图等) 作者特别强调,良好的设计归档习惯能显著降低生产问题概率,建议采用双重备份机制。文中多个真实案例(如谐振频率偏移、EM

Jack010169的博客 43

Redis 底层数据结构之链表

文章参考:《Redis设计与实现》黄建宏 链表 链表提供了高效的节点重排能力,以及可以顺序访问,也可以通过增删节点灵活调整链表长度,Redis中的列表、发布订阅、慢查询、监视器等功能均用到了链表。 链表和链表节点的实现 链表节点的定义 typedef struct listNode { // 前置节点 struct listNode *prev; // 后置节点 struct listNode *next; // 节点的值 void *value; }listNode; 多个 .

weixin_44046117的博客 221

基于LSTM算法实现交通流量预测(Pytorch版)

本研究采用PyTorch框架,设计并实现了一种基于LSTM(Long Short-Term Memory)网络的交通流量预测模型。针对时序性显著的交通流量数据,模型首先将多节点、多通道的时空信息整合为一维特征输入至多层LSTM单元,利用其捕捉长期依赖及短期波动的能力进行深度学习。全连接层随后调整时间步长以适应预测周期需求。模型结构灵活,能适应不同节点数、输入输出长度及通道配置,实现在给定历史流量数据基础上对未来交通流量的精准预测,为交通管理与规划提供数据驱动支持。

打酱油的葫芦娃 4638

腾讯面试——机器学习岗面试总结

先写合并两个有序数组,接着写合并n个有序的数组,假设总共m个数,讲了一个思路两两合并直到只剩下一个数组,分析一下时间复杂度(MlogN),面试官不满意,然后想了一下说了另一个思路,用n个指针开始指向各个数组的首端,用一个优先队列维护他们的最小值,每次拿出最小值的指针然后插入该指针的下一个值。感觉跟二面面试官聊得还是挺开心的,他详细地介绍了他们部门所做的业务,然后问了一下什么时候能来实现,我说只能暑假七八两个月,他觉得有点晚,这样子不利于转正,有些人都实习半年的,回去讨论一下,正式批之前给我答复。

FPGA/MATLAB学习教程/源码/项目合作开发 1231

Redis的链表底层原理

一、前言     链表提供了高效的节点重排能力,以及顺序性的节点访问方式,并且可以通过增删节点来灵活地调整链表的长度。     作为一种常用数据结构,链表内置在很多高级的编程语言里面,因为Redis使用的C语言并没有内置这种数据结构,所以Redis构建了自己的链表实现。 二、链表的使用场景   &n...

weixin_42146366的博客 515

Redhawk:如何gds2def?

使用redhawk gds2def功能 这其中gds_layer.map的书写方式如下: m代表metal,v代表via。 ICC/ICC2用户可以从tech file中获取layer number。 innovus用户可以从stream out layer map中获取layer number。 最后运行gds2def -m gds2def.config即可。 ...

m0_61544122的博客 5340

redis底层数据结构——链表

redis底层数据结构——链表

SO_zxn的博客 674

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Redis底层数据结构之快链表(QuickList)

本文介绍了Redis中的QuickList数据结构。QuickList是一种结合了双向链表和ZipList的混合结构,每个链表节点存储一个ZipList。

zErO__N的博客 1285

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

最新redis底层数据结构之双向链表

简介 双向链表是redis中数据类型List的底层数据结构之一,双向链表也是一种常见的数据结构,在redis中也是大同小异 主要包括三个元素分别为链表,节点,链表迭代器,其设计比较简单 api易懂 为了方便大家阅读 本文做了详细的源码介绍 。 性能优化分析 链表的双向指针prev和next,方便通过迭代器从头至尾或从尾至头遍历; 链表的头尾指针head和tail,对链表的头尾插入,以及头尾节点读取,高效地实现list的一些指令(如push 和pop相关指令) 链表的长度len,得计算链表长度的时间复杂度为

eno_yang的博客 517

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

Redis底层数据结构之压缩链表(ZipList)

Redis的ZipList是一种为节省内存设计的紧凑双向链表结构。它支持O(1)时间复杂度的头尾操作,通过zlbytes、zltail、zllen等头部字段管理列表。Entry节点包含prevlen(前驱长度)、encoding(编码类型)和entry-data(实际数据),其中prevlen可能引发连锁更新问题。

zErO__N的博客 835

Redis底层数据结构-链表

Redis底层数据结构-链表

weixin_45275107的博客 473

Redis底层数据结构-双向链表

链表被广泛用于实现 Redis 的各种功能, 比如列表键, 发布与订阅, 慢查询, 监视器, 等等。每个链表节点由一个 listNode 结构来表示, 每个节点都有一个指向前置节点和后置节点的指针, 所以 Redis 的链表实现是双端链表。每个链表使用一个 list 结构来表示, 这个结构带有表头节点指针、表尾节点指针、以及链表长度等信息。因为链表表头节点的前置节点和表尾节点的后置节点都指向 NULL , 所以 Redis 的链表实现是无环链表。

Sajor的博客 1070

Redis系列 - Redis底层数据结构(简单动态字符串(SDS)、链表、字典、跳跃表、整数集合、压缩列表)

Redis系列 - Redis基本数据类型

u011485472的博客 2133

Redis底层数据结构:链表、字典、跳跃表原理详解

Redis核心数据结构解析:1) 双向链表(List底层)实现有序存储,支持高效头尾操作;2) 字典(Hash底层)采用渐进式rehash机制,确保扩容无阻塞;3) 跳跃表(ZSet底层)通过多层索引实现高效有序查询。三者在不同场景下各具优势:链表适合顺序操作,字典提供O(1)访问,跳跃表兼顾排序与查询效率,共同支撑Redis高性能特性。

2402_87794035的博客 63

使用动态链表实现字符的频度统计_Redis中五大数据结构的底层实现

一、概述Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,与Memcached类似,却优于Memcached的一个高性能的key-value数据库。下面让我们来详细介绍一下redis中五大数据结构的底层实现。二、简单动态字符串1、概述Redis是一个开源的使用ANSI C语言编写的key-value 数据库,我们可能会...

weixin_39889792的博客 258

【Redis学习笔记】底层数据结构之链表

链表 Redis 使用链表作为列表的键底层实现。 3.1 链表和链表节点的实现 每个链表节点使用一个 adlist.h/listNode 结构来表示: typedef struct listNode{ // 前置节点 struct listNode *prev; // 后置节点 struct listNode *next; // 节点的值 void *value; }listNode; 多个 listNode 可以通过 prev 和 next 指针组成双端链

kingdow-liu 273

Redis底层数据结构--链表

这是普通的链表实现, 链表结点不直接持有数据, 而是通过void *指针来间接的指向数据. 其实现位于 src/adlist.h与src/adlist.c中, 关键定义如下: typedef struct listNode { struct listNode *prev; struct listNode *next; void *value; } listNode; ...

pythonluo的专栏 282
上一篇: Redis底层数据结构之简单动态字符串
下一篇: Android组件安全
code&poetry
博客等级 码龄14年 134粉丝 34原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值