Redis的设计与实现(2)-链表
链表在 Redis 中的应用非常广泛, 比如列表键的底层实现之一就是链表: 当一个列表键包含了数量比较多的元素, 又或者列表中包含的元素都是比较长的字符串时, Redis 就会使用链表作为列表键的底层实现.
除了链表键之外, 发布与订阅, 慢查询, 监视器等功能也用到了链表, Redis 服务器还使用链表保存多个客户端的状态信息, 以及使用链表来构建客户端输出缓冲区(output buffer).
1. 链表的定义
每个链表节点使用一个 adlist.h/listNode 结构来表示:
typedef struct listNode {
// 前置节点
struct listNode *prev;
// 后置节点
struct listNode *next;
// 节点的值
void *value;
} listNode;
使用 adlist.h/list 来操作链表会更方便
typedef struct list {
// 表头节点
listNode *head;
// 表尾节点
listNode *tail;
// 链表所包含的节点数量
unsigned long len;
// 节点值复制函数
void *(*dup)(void *ptr);
// 节点值释放函数
void (*free)(void *ptr);
// 节点值对比函数
int (*match)(void *ptr, void *key);
} list;
可见, 它是一个双向链表.
head, tail 和 len 分别是表头表尾指针和节点数量, 而 dup, free 和 match 则是用于实现多态链表所需的类型特定函数:
- dup 函数用于复制链表节点所保存的值;
- free 函数用于释放链表节点所保存的值;
- match 函数则用于对比链表节点所保存的值和另一个输入值是否相等.
(有点面向对象的味道)
2. 链表的API
| 函数 | 作用 | 时间复杂度 |
|---|---|---|
| listSetDupMethod | 将给定的函数设置为链表的节点值复制函数 | O(1) |
| listGetDupMethod | 返回链表当前正在使用的节点值复制函数 | 复制函数可以通过链表的 dup 属性直接获得, O(1) |
| listSetFreeMethod | 将给定的函数设置为链表的节点值释放函数 | O(1) |
| listGetFree | 返回链表当前正在使用的节点值释放函数 | 释放函数可以通过链表的 free属性直接获得, O(1) |
| listSetMatchMethod | 将给定的函数设置为链表的节点值对比函数 | O(1) |
| listGetMatchMethod | 返回链表当前正在使用的节点值对比函数 | 对比函数可以通过链表的 `match` 属性直接获得, O(1) |
| listLength | 返回链表的长度(包含了多少个节点) | 链表长度可以通过链表的 `len` 属性直接获得, O(1) |
| listFirst | 返回链表的表头节点 | 表头节点可以通过链表的 `head` 属性直接获得, O(1) |
| listLast | 返回链表的表尾节点 | 表尾节点可以通过链表的 tail 属性直接获得, O(1) |
| listPrevNode | 返回给定节点的前置节点 | 前置节点可以通过节点的 prev 属性直接获得, O(1) |
| listNextNode | 返回给定节点的后置节点 | 后置节点可以通过节点的 next 属性直接获得, O(1) |
| listNodeValue | 返回给定节点目前正在保存的值 | 节点值可以通过节点的 value 属性直接获得, O(1) |
| listCreate | 创建一个不包含任何节点的新链表 | O(1) |
| listAddNodeHead | 将一个包含给定值的新节点添加到给定链表的表头 | O(1) |
| listAddNodeTail | 将一个包含给定值的新节点添加到给定链表的表尾 | O(1) |
| listInsertNode | 将一个包含给定值的新节点添加到给定节点的之前或者之后 | O(1) |
| listSearchKey | 查找并返回链表中包含给定值的节点 | O(N), N 为链表长度 |
| listIndex | 返回链表在给定索引上的节点 | O(N), N 为链表长度 |
| listDelNode | 从链表中删除给定节点 | O(1) |
| listRotate | 将链表的表尾节点弹出, 然后将被弹出的节点插入到链表的表头, 成为新的表头节点 | O(1) |
| listDup | 复制一个给定链表的副本 | O(N), N 为链表长度 |
| listRelease | 释放给定链表, 以及链表中的所有节点 | O(N), N 为链表长度 |
3. 总结
双端: 节点带有 prev 和 next 指针, 获取其前置和后置节点的复杂度都是 O(1);
无环: 表头节点的 prev 指针和表尾节点的 next 指针都指向 NULL, 对链表的访问以 NULL 为终点;
带表头指针和表尾指针: 通过 list 结构的 head 指针和 tail 指针, 获取表头和表尾节点的复杂度为 O(1);
带链表长度计数器: 通过 list 结构的 len 属性, 程序获取链表中节点数量的复杂度为 O(1);
多态: 链表节点使用 void* 指针来保存节点值, 并且可以通过 list 结构的 dup, free, match 三个属性为节点值设置类型特定函数, 可以用于保存各种不同类型的值.
4. 重点回顾
- 链表被广泛用于实现 Redis 的各种功能, 比如列表键, 发布与订阅, 慢查询, 监视器, 等等;
- 每个链表节点由一个 listNode 结构来表示, 每个节点都有一个指向前置节点和后置节点的指针, 所以 Redis 的链表实现是双端链表;
- 每个链表使用一个 list 结构来表示, 这个结构带有表头节点指针, 表尾节点指针, 以及链表长度等信息;
- 因为链表表头节点的前置节点和表尾节点的后置节点都指向 NULL , 所以 Redis 的链表实现是无环链表;
- 通过为链表设置不同的类型特定函数, Redis 的链表可以用于保存各种不同类型的值.
文章来源于本人博客,发布于 2018-04-12,原文链接:https://imlht.com/archives/122/
Redis的设计与实现(2)-链表的更多相关文章
- [Redis]Redis的设计与实现-链表/字典/跳跃表
redis的设计与实现:1.假如有一个用户关系模块,要实现一个共同关注功能,计算出两个用户关注了哪些相同的用户,本质上是计算两个用户关注集合的交集,如果使用关系数据库,需要对两个数据表执行join操作 ...
- Redis --> Redis架构设计
Redis架构设计 一.前言 Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库.缓存和消息中间件. 它支持多种类型的数据结构,如 字符串(strings), 散列 ...
- Redis核心设计原理(深入底层C源码)
Redis 基本特性 1. 非关系型的键值对数据库,可以根据键以O(1) 的时间复杂度取出或插入关联值 2. Redis 的数据是存在内存中的 3. 键值对中键的类型可以是字符串,整型,浮点型等,且键 ...
- 11.Redis缓存设计
11.Redis缓存设计11.1 缓存的收益和成本11.2 缓存更新策略11.3 缓存粒度控制11.4 穿透优化11.5 无底洞优化11.6 雪崩优化11.7 热点key重建优化11.8 本章重点回顾
- Redis缓存设计及常见问题
Redis缓存设计及常见问题 缓存能够有效地加速应用的读写速度,同时也可以降低后端负载,对日常应用的开发至关重要.下面会介绍缓存使 用技巧和设计方案,包含如下内容:缓存的收益和成本分析.缓存更新策略的 ...
- Python 基于python+mysql浅谈redis缓存设计与数据库关联数据处理
基于python+mysql浅谈redis缓存设计与数据库关联数据处理 by:授客 QQ:1033553122 测试环境 redis-3.0.7 CentOS 6.5-x86_64 python 3 ...
- redis数据库设计(转)
原文:http://segmentfault.com/q/1010000000316112 redis是什么 redis就是一个存储key-value键值对的仓库,如何使用redis在于如何理解你需要 ...
- 17 redis -key设计原则
书签系统 create table book ( bookid int, title char(20) )engine myisam charset utf8; insert into book va ...
- 细说分布式Redis架构设计和踩过的那些坑
细说分布式Redis架构设计和踩过的那些坑_redis 分布式_ redis 分布式锁_分布式缓存redis 细说分布式Redis架构设计和踩过的那些坑
- Redis设计与实现 (二): 链表
Redis实现为双链表结构, 列表键的底层实现之一就是链表, 发布与订阅, 慢查询, 监视器等功能都用到了链表. Redis本身也使用链表维持多个客户端. 节点定义, 位于 adlist.h/lis ...
随机推荐
- 如何在 vue3 中使用 jsx/tsx?
我们都知道,通常情况下我们使用 vue 大多都是用的 SFC(Signle File Component)单文件组件模式,即一个组件就是一个文件,但其实 Vue 也是支持使用 JSX 来编写组件的.这 ...
- 【LeetCode动态规划#08】完全背包问题实战与分析(零钱兑换II)
零钱兑换II 力扣题目链接(opens new window) 给定不同面额的硬币和一个总金额.写出函数来计算可以凑成总金额的硬币组合数.假设每一种面额的硬币有无限个. 示例 1: 输入: amoun ...
- Sitecore10 Demo演示环境Azure一键部署(Step By Step Guide to installing Sitecore10 in Azure Paas)
本文演示Sitecore XP Single(XP0)在Azure上的一键部署,即"30分钟生成Sitecore演示环境"的一环. 关于XP(即Sitecore Experienc ...
- MQTT-主题基础
MQTT主题 MQTT的主题是一个utf-8编码的字符串,最大长度65535字节,严格区分大小写 MQTT主题支持分层结构,主题分隔符用'/'表示,主题的层级长度可以为0 # 将主题划分为3个层级 ' ...
- 文心一言 VS chatgpt (15)-- 算法导论3.2 4~5题
四.函数 $\lceil \lg n \rceil !$ 多项式有界吗?函数 $\lceil \lg \lg n \rceil !$ 多项式有界吗? 文心一言: chatgpt: 对于第一个问题,函数 ...
- 2022-11-24:小团在地图上放了3个定位装置,想依赖他们进行定位! 地图是一个n*n的棋盘, 有3个定位装置(x1,y1),(x2,y2),(x3,y3),每个值均在[1,n]内。 小团在(a,
2022-11-24:小团在地图上放了3个定位装置,想依赖他们进行定位! 地图是一个n*n的棋盘, 有3个定位装置(x1,y1),(x2,y2),(x3,y3),每个值均在[1,n]内. 小团在(a, ...
- 2022-03-06:金币路径。 给定一个数组 A(下标从 1 开始)包含 N 个整数:A1,A2,……,AN 和一个整数 B。 你可以从数组 A 中的任何一个位置(下标为 i)跳到下标 i+1,i+
2022-03-06:金币路径. 给定一个数组 A(下标从 1 开始)包含 N 个整数:A1,A2,--,AN 和一个整数 B. 你可以从数组 A 中的任何一个位置(下标为 i)跳到下标 i+1,i+ ...
- 2022-02-23:如何搭建k8s单机环境(用k3s),并且搭建dashboard?
2022-02-23:如何搭建k8s单机环境(用k3s),并且搭建dashboard? 答案2022-02-03: 使用场景:个人电脑.需要安装虚拟机,操作系统是centos. 个人电脑上测试,不需要 ...
- el-table自适应列宽
这里可对内容为文本的列进行自适应列宽 以下为 工具方法 /** * 使用span标签包裹内容,然后计算span的宽度 width: px * @param valArr */ function get ...
- 「P2」试下1个半月能不能水出个毕设
0.目标 将上个 springboot 项目 + html 中的html用Vue来重写,也就是在原springboot项目中集成Vue 1.在界面上,将html改成vue的形式 1.1.原html & ...