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 ...
随机推荐
- 计算机常用的快捷键以及常用的Dos命令
计算机的常用快捷键有哪些? 今天我重温了Java基础的课程,计算机的快捷键大家肯定不陌生. 计算机的常用快捷键 ctrl+c 复制 ctrl+v 粘贴 ctrl+s 保存 ctrl+x 剪切 ct ...
- abp(net core)+easyui+efcore实现仓储管理系统——供应商管理升级之下(六十四)
abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统--ABP总体介绍(一) abp(net core)+ ...
- ai问答:使用 Vue3 组合式API 和 TS 封装 echarts 折线图
使用这个组件时,只需要传入合适的chartData数组,就可以渲染一个折线图,并且响应数据变化. <template> <div ref="chart" styl ...
- 2021-05-12:给定一个数组arr,只能对arr中的一个子数组排序, 但是想让arr整体都有序。返回满足这一设定的子数组中,最短的是多长?
2021-05-12:给定一个数组arr,只能对arr中的一个子数组排序, 但是想让arr整体都有序.返回满足这一设定的子数组中,最短的是多长? 福大大 答案2021-05-12: 从左往右遍历,缓存 ...
- 2021-12-14:根据身高重建队列。 假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i 个
2021-12-14:根据身高重建队列. 假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序).每个 people[i] = [hi, ki] 表示第 i 个 ...
- Requested setting INSTALLED_APPS, but settings are not configured. You must either define the env...
解决办法 在test.py文件的最头部加上以下代码,配置环境 import os,django os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djb ...
- 8张图带你全面了解kafka的核心机制
前言 kafka是目前企业中很常用的消息队列产品,可以用于削峰.解耦.异步通信.特别是在大数据领域中应用尤为广泛,主要得益于它的高吞吐量.低延迟,在我们公司的解决方案中也有用到.既然kafka在企业中 ...
- 前端开发如何更好的避免样式冲突?级联层(CSS@layer)
作者:vivo 互联网前端团队 - Zhang Jiqi 本文主要讲述了CSS中的级联层(CSS@layer),讨论了级联以及级联层的创建.嵌套.排序和浏览器支持情况.级联层可以用于避免样式冲突,提高 ...
- deepin系统更新谷歌浏览器chrome的方法
deepin系统更新谷歌浏览器chrome的方法 1 为什么要更新谷歌浏览器谷歌浏览器更新频繁,隔一段时间不更新,打开浏览器时会自动弹出一个提示更新的窗口,有点烦,如果使用python自动处理程序时, ...
- Python潮流周刊#5:并发一百万个任务要用多少内存?
你好,我是猫哥.这里记录每周值得分享的 Python 及通用技术内容,部分为英文,已在小标题注明.(标题取自其中一则分享,不代表全部内容都是该主题,特此声明.) 博客原文:https://python ...