作者推荐 |【Redis技术进阶之路】「底层源码解析」揭秘高效存储模型与数据结构底层实现(链表)(一)

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 作者推荐 |【Redis技术进阶之路】「底层源码解析」揭秘高效存储模型与数据结构底层实现(链表)

【专栏简介】

随着数据需求的迅猛增长,持久化和数据查询技术的重要性日益凸显。关系型数据库已不再是唯一选择,数据的处理方式正变得日益多样化。在众多新兴的解决方案与工具中,Redis凭借其独特的优势脱颖而出。

【技术大纲】

为何Redis备受瞩目?原因在于其学习曲线平缓,短时间内便能对Redis有初步了解。同时,Redis在处理特定问题时展现出卓越的通用性,专注于其擅长的领域。深入了解Redis后,您将能够明确哪些任务适合由Redis承担,哪些则不适宜。这一经验对开发人员来说是一笔宝贵的财富。



在这个专栏中,我们将专注于Redis的6.2版本进行深入分析和介绍。Redis 6.2不仅是我个人特别偏爱的一个版本,而且在实际应用中也被广泛认为是稳定性和性能表现都相当出色的版本

【专栏目标】

本专栏深入浅出地传授Redis的基础知识,旨在助力读者掌握其核心概念与技能。深入剖析了Redis的大多数功能以及全部多机功能的实现原理,详细展示了这些功能的核心数据结构和关键算法思想。读者将能够快速且有效地理解Redis的内部构造和运作机制,这些知识将助力读者更好地运用Redis,提升其使用效率。

将聚焦于Redis的五大数据结构,深入剖析各种数据建模方法,并分享关键的管理细节与调试技巧。

【目标人群】

Redis技术进阶之路专栏:目标人群与受众对象,对于希望深入了解Redis实现原理底层细节的人群

1. Redis爱好者与社区成员

Redis技术有浓厚兴趣,经常参与社区讨论,希望深入研究Redis内部机制、性能优化和扩展性的读者。

2. 后端开发和系统架构师

在日常工作中经常使用Redis作为数据存储和缓存工具,他们在项目中需要利用Redis进行数据存储、缓存、消息队列等操作时,此专栏将为他们提供有力的技术支撑。

3. 计算机专业的本科生及研究生

对于学习计算机科学、软件工程、数据分析等相关专业的在校学生,以及对Redis技术感兴趣的教育工作者,此专栏可以作为他们的学习资料和教学参考。

无论是初学者还是资深专家,无论是从业者还是学生,只要对Redis技术感兴趣并希望深入了解其原理和实践,都是此专栏的目标人群和受众对象

让我们携手踏上学习Redis的旅程,探索其无尽的可能性!


链表

链表具备出色的节点重排与顺序访问能力,并且能够通过灵活地增删节点来调整链表的长度,从而满足各种实际应用需求。作为一种重要的数据结构,链表在多种高级编程语言中得到了内置支持。然而,由于Redis所依赖的C语言并未内置链表数据结构,Redis团队便自行实现了链表功能。

使用场景

在Redis中,链表的应用场景十分广泛,特别是在处理列表键时。当列表键包含的元素数量较多,或元素本身较长(如字符串)时,Redis会选择使用链表作为其底层实现。这是因为链表能够高效地处理大量数据,并且在处理长字符串时具有优势,能够确保数据的完整性和处理速度。通过利用链表的这些特性,Redis在处理复杂数据结构时表现出色,为用户提供了稳定、高效的数据存储和访问体验。

List(列表)和 链表的关系

列表(List)的底层实现是一个链表,其中的每个节点保存了一个值。除了用于链表之外,发布与订阅、慢查询、监视器等功能也利用了链表。Redis服务器本身还利用链表来保存多个客户端的状态信息,并构建客户端输出缓冲区(output buffer)。

链表的实现

链表是有多个链表节点组成,这一设计使得链表节点的数据结构和功能得到了清晰和统一的定义,进而确保了链表操作的准确性和一致性。下图是一个多个listNode组成的双端链表:



通过采用这种标准化的结构表示,我们能够实现更加高效和可靠的链表管理,为后续的链表操作和应用提供了坚实的基础。

链表的节点

每个链表节点均通过adlist.h中定义的listNode结构来具体表示,如下图源码所示:



多个listNode通过其内部的prev和next指针相互连接,从而构成了一个双端链表。这种链表结构在源代码中的实现如下所示,通过灵活使用这些指针,我们能够实现在链表中向前或向后遍历的能力,增强了链表的灵活性和实用性。

c

复制代码

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

这种设计不仅优化了链表的性能,同时也提高了链表操作的效率,使得我们可以更轻松地对其进行增删改查等操作。

list的源码实现

在Redis中,list数据结构并非仅由简单的listNode模型构成的单一链表结构。实际上,它结合了多种操作元素,形成了一个更为丰富和灵活的数据结构。通过采用adlist.h/list来持有和管理这个链表,Redis不仅实现了基本的链表操作,还加入了一系列优化和特性,如下面的源码所示:

c

复制代码

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模型结构提供了更高效、更便捷的数据处理能力。这种设计不仅增强了链表的实用性,也为Redis的广泛应用提供了强大的支持。

结构模型分析

List结构精心设计了表头指针head和表尾指针tail来指明链表的起始与终结,同时配备了链表长度计数器len来快速获取链表中元素的数量。此外,为了满足多态链表的需求,List结构还引入了dupfreematch这三个类型特定的函数成员:

  • dup函数:它负责复制链表节点中存储的值,确保在链表复制或扩展时能够准确地复制数据。
  • free函数:它负责释放链表节点中存储的值所占用的内存空间,防止内存泄漏,保持系统的稳定性。
  • match函数:它用于比较链表节点中存储的值与给定的输入值是否相同,为链表元素的查找和比较操作提供了便利。

这三个函数的引入不仅丰富了List结构的功能,还提高了其灵活性和可扩展性,使得Redis的list数据结构能够应对各种复杂的应用场景。

list和listNode和逻辑结构

由list结构和listNode结构共同构筑的链表,展现出一种既严谨又灵活的数据组织形式。


  • list结构:链表的整体视图,包括表头指针、表尾指针以及长度计数器,为链表的操作和管理提供了便利。
  • listNode结构:链表的基本组成单元,通过prev和next指针实现节点间的双向连接,形成了链表的骨架。
链表结构的特性

  • 双向性:链表中的每个节点均配备prev和next指针,使得无论是查找某个节点的前驱节点还是后继节点,操作复杂度均保持在O(1)水平,极大地提升了链表的遍历效率。
  • 无环设计:链表的表头节点的prev指针和表尾节点的next指针均指向NULL,这种设计确保了链表的访问始终有明确的终点,从而避免了循环引用导致的潜在问题。
  • 便捷的表头尾访问:Redis链表通过list结构中的head和tail指针,使得程序能够直接定位到链表的起始和结束节点,获取表头节点和表尾节点的操作复杂度同样为O(1),大大简化了链表的操作流程。
  • 高效的长度计数:list结构内置的len属性用于记录链表中的节点数量,这使得程序在需要获取链表长度时,无需遍历整个链表,即可直接获取结果,操作复杂度保持在O(1)。
  • 多态支持:链表节点采用void*指针来存储节点值,同时,list结构提供了dup、free和match三个属性,允许用户为节点值设置类型特定的函数。这种设计使得Redis的链表能够灵活地保存和处理各种不同类型的值,实现了多态链表的功能。
其余方法介绍和说明

以下是关于相关方法的介绍,这些方法定义在adlist.h头文件中,如以下源码所示。但值得注意的是,.h头文件通常只包含函数的声明和定义概念,而真正的函数实现机制则位于adlist.c源文件中。

c

复制代码

/* Functions implemented as macros */
#define listLength(l) ((l)->len)
#define listFirst(l) ((l)->head)
#define listLast(l) ((l)->tail)
#define listPrevNode(n) ((n)->prev)
#define listNextNode(n) ((n)->next)
#define listNodeValue(n) ((n)->value)
#define listSetDupMethod(l,m) ((l)->dup = (m))
#define listSetFreeMethod(l,m) ((l)->free = (m))
#define listSetMatchMethod(l,m) ((l)->match = (m))
#define listGetDupMethod(l) ((l)->dup)
#define listGetFreeMethod(l) ((l)->free)
#define listGetMatchMethod(l) ((l)->match)
/* Prototypes */
list *listCreate(void);
void listRelease(list *list);
void listEmpty(list *list);
list *listAddNodeHead(list *list, void *value);
list *listAddNodeTail(list *list, void *value);
list *listInsertNode(list *list, listNode *old_node, void *value, int after);
void listDelNode(list *list, listNode *node);
listIter *listGetIterator(list *list, int direction);
listNode *listNext(listIter *iter);
void listReleaseIterator(listIter *iter);
list *listDup(list *orig);
listNode *listSearchKey(list *list, void *key);
listNode *listIndex(list *list, long index);
void listRewind(list *list, listIter *li);
void listRewindTail(list *list, listIter *li);
void listRotateTailToHead(list *list);
void listRotateHeadToTail(list *list);
void listJoin(list *l, list *o);
void listInitNode(listNode *node, void *value);
void listLinkNodeHead(list *list, listNode *node);
void listLinkNodeTail(list *list, listNode *node);
void listUnlinkNode(list *list, listNode *node);
/* Directions for iterators */
#define AL_START_HEAD 0
#define AL_START_TAIL 1
#endif /* __ADLIST_H__ */

通过头文件和源文件的配合,我们得以清晰地划分函数的接口与实现,从而确保代码的可读性、可维护性和可重用性。在adlist.c中,每个函数的具体实现都详细描述了其功能机制,使得我们能够深入理解并应用这些函数。

函数介绍说明
方法 说明
list *listCreate(void); 创建一个新的空链表,并返回链表的指针。
void listRelease(list *list); 释放给定链表的内存,包括链表中的所有节点。
void listEmpty(list *list); 清空给定链表中的所有节点,但不释放链表本身的内存。
list *listAddNodeHead(list *list, void *value); 在链表的头部添加一个新节点,节点值为给定值,并返回更新后的链表指针。
list *listAddNodeTail(list *list, void *value); 在链表的尾部添加一个新节点,节点值为给定值,并返回更新后的链表指针。
list *listInsertNode(list *list, listNode *old_node, void *value, int after); 在给定旧节点之后(如果after为1)或之前(如果after为0)插入一个新节点,节点值为给定值,并返回更新后的链表指针。
void listDelNode(list *list, listNode *node); 从链表中删除给定的节点。
listIter *listGetIterator(list *list, int direction); 为链表创建一个迭代器,并返回迭代器的指针。迭代方向由direction参数指定。
listNode *listNext(listIter *iter); 获取迭代器当前指向的下一个节点的指针。
void listReleaseIterator(listIter *iter); 释放给定迭代器的内存。
list *listDup(list *orig); 复制给定的链表,并返回新链表的指针。
listNode *listSearchKey(list *list, void *key); 在链表中搜索具有给定键值的节点,并返回找到的节点的指针。如果未找到,则返回NULL。
listNode *listIndex(list *list, long index); 根据给定的索引获取链表中的节点。如果索引超出范围,则返回NULL。
void listRewind(list *list, listIter *li); 重置给定的迭代器,使其指向链表的头部。
void listRewindTail(list *list, listIter *li); 重置给定的迭代器,使其指向链表的尾部。
void listRotateTailToHead(list *list); 将链表的尾部节点移动到头部。
void listRotateHeadToTail(list *list); 将链表的头部节点移动到尾部。
void listJoin(list *l, list *o); 将另一个链表o的所有节点添加到链表l的尾部。
void listInitNode(listNode *node, void *value); 初始化给定的链表节点,设置其值为给定值。
void listLinkNodeHead(list *list, listNode *node); 将给定的节点链接到链表的头部。
void listLinkNodeTail(list *list, listNode *node); 将给定的节点链接到链表的尾部。
void listUnlinkNode(list *list, listNode *node); 从链表中取消链接给定的节点,但不释放节点的内存。


作者推荐 |【Redis技术进阶之路】「底层源码解析」揭秘高效存储模型与数据结构底层实现(链表)(二)/article/1471149

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
2天前
|
域名解析 网络协议 安全
【域名解析DNS专栏】未来趋势:DNS解析技术的新发展与挑战
【5月更文挑战第30天】随着AI和物联网的发展,DNS解析正向智能化和安全增强迈进,利用大数据和DNSSEC保障速度与安全。同时,匿名解析技术将提升用户隐私保护。然而,面对复杂网络环境、性能与延迟挑战及国际标准兼容性问题,DNS技术需不断创新以应对未来挑战。Python示例展示了DNSSEC验证查询。DNS解析的持续进化对互联网的稳定和隐私至关重要。
|
3天前
|
负载均衡 网络协议 容灾
【飞天技术沙龙】云解析DNS上海站《多云+IDC融合场景下的DNS最佳实践》圆满落幕
【飞天技术沙龙】云解析DNS上海站《多云+IDC融合场景下的DNS最佳实践》圆满落幕
|
4天前
|
并行计算 算法 物联网
LLM 大模型学习必知必会系列(六):量化技术解析、QLoRA技术、量化库介绍使用(AutoGPTQ、AutoAWQ)
LLM 大模型学习必知必会系列(六):量化技术解析、QLoRA技术、量化库介绍使用(AutoGPTQ、AutoAWQ)
LLM 大模型学习必知必会系列(六):量化技术解析、QLoRA技术、量化库介绍使用(AutoGPTQ、AutoAWQ)
|
4天前
|
负载均衡 网络协议 容灾
【飞天技术沙龙】云解析DNS上海站《多云+IDC融合场景下的DNS最佳实践》圆满落幕
【飞天技术沙龙】云解析DNS上海站《多云+IDC融合场景下的DNS最佳实践》于5月22日在上海举行,吸引了20余家知名企业参与。本次沙龙聚集“多云+IDC的融合DNS场景”,从DNS在“企业上云”和“容灾调度”两个主题的最佳实践展开分享,沙龙活动取得了圆满成功。
|
4天前
|
人工智能 算法 计算机视觉
人工智能视觉:基于OpenCV的人脸识别技术的深度解析
人工智能视觉:基于OpenCV的人脸识别技术的深度解析
|
4天前
|
机器学习/深度学习 人工智能 算法
构建一个基于AI的语音识别系统:技术深度解析与实战指南
【5月更文挑战第28天】本文深入探讨了构建基于AI的语音识别系统,涵盖基本原理、关键技术及实战指南。关键步骤包括语音信号预处理、特征提取、声学模型、语言模型和解码器。深度学习在声学和语言模型中发挥关键作用,如RNN、LSTM和Transformer。实战部分涉及数据收集、预处理、模型训练、解码器实现及系统评估。通过本文,读者可了解构建语音识别系统的基本流程和技巧。
|
5天前
|
安全 算法 网络安全
网络安全与信息安全:防护之道与实战策略网络防线的构筑者:网络安全与信息保护技术解析
【5月更文挑战第27天】 在数字化时代,数据成为了新的货币,而网络安全则是保护这些宝贵资产不受威胁的盾牌。本文将深入探讨网络安全漏洞的概念、加密技术的最新进展以及提升个人和企业的安全意识。通过对网络攻击者的策略进行剖析,我们不仅揭示了常见的安全漏洞,还分享了如何通过多层次防御机制来增强系统的安全性。文章的目标是为读者提供实用的知识,以便构建一个更加坚固的网络安全防线。
|
5天前
|
域名解析 负载均衡 网络协议
【域名解析DNS专栏】DNS解析中的Anycast技术:原理与优势
【5月更文挑战第27天】Anycast技术是解决DNS解析高效、稳定和安全问题的关键。它将一个IP地址分配给多地服务器,客户端请求自动路由至最近的低负载服务器,减少延迟,提高解析速度。此外,Anycast实现负载均衡,缓解DDoS攻击,并确保高可用性。通过遍历Anycast服务器选择最低延迟者进行DNS解析,实现网络性能优化。随着技术发展,Anycast在DNS解析中的应用将更加广泛。
|
7天前
|
域名解析 网络协议 安全
【域名解析 DNS 专栏】动态 DNS(DDNS)技术解析及其应用
【域名解析 DNS 专栏】动态 DNS(DDNS)技术解析及其应用 动态DNS(DDNS)技术在应对动态IP地址环境下,提供了一种灵活的解决方案,使设备能通过固定域名被访问。当设备IP改变时,DDNS服务会更新域名与新IP的映射,确保访问畅通。广泛应用于家庭远程访问设备和企业网络管理。简单的DDNS更新Python示例展示了发送请求更新过程。然而,DDNS面临服务可靠性和安全性的挑战。总体而言,DDNS技术提升了网络环境的便利性和效率,并将持续发展和完善。
|
7天前
|
Java 编译器 开发者
Java注解(Annotation)技术深入解析
Java注解(Annotation)技术深入解析
402 1

推荐镜像

更多
http://www.vxiaotou.com