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 ...
随机推荐
- vue 项目全局修改element-ui的样式/主题颜色
一,安装sass 1 npm i sass sass-loader --save 二,安装element主题生成工具 // 全局安装npm i element-theme --save // 安装主体 ...
- RedisTemplate在拦截器前没有注入的问题
RedisTemplate为null的问题 最近在搭建一个项目,然后项目框架采用的是spring boot,然后登录我就使用新学习的JWT嘛,然后就想着在请求进来的时候使用拦截器先对传进来的token ...
- OpenResty学习笔记03:再探WAF
一. 再谈WAF 我们上一篇安装的WAF来自另一位技术大神 赵舜东,花名 赵班长,一直从事自动化运维方面的架构设计工作.阿里云MVP.华为云MVP.中国SaltStack用户组发起人 .新运维社区发起 ...
- 微信小程序客服、支付、定位、下拉加载功能
一.客服功能 1.只要你微信小程序,后台添加了客服,引用以下button,就可以进入聊天(在小程序官网如何添加客服用户,请自行百度,谢谢) 2.通过按钮方式 <button open-type= ...
- 2021-06-27:给定一个正数数组arr,代表若干人的体重。再给定一个正数limit,表示所有船共同拥有的载重量。每艘船最多坐两人,且不能超过载重,想让所有的人同时过河,并且用最好的分配方法让船尽
2021-06-27:给定一个正数数组arr,代表若干人的体重.再给定一个正数limit,表示所有船共同拥有的载重量.每艘船最多坐两人,且不能超过载重,想让所有的人同时过河,并且用最好的分配方法让船尽 ...
- 2021-08-24:合并石头的最低成本。有 N 堆石头排成一排,第 i 堆中有 stones[i] 块石头。每次移动(move)需要将连续的 K 堆石头合并为一堆,而这个移动的成本为这 K 堆石头的
2021-08-24:合并石头的最低成本.有 N 堆石头排成一排,第 i 堆中有 stones[i] 块石头.每次移动(move)需要将连续的 K 堆石头合并为一堆,而这个移动的成本为这 K 堆石头的 ...
- Ubuntu下串口工具 PicoCOM 的使用和时间戳显示
PICOCOM Ubuntu下的串口软件, 除了 CuteCOM, screen, MiniCOM 以外, 还有一个和 MiniCOM 很像的 PicoCOM. 最近在调试 CH340C 串口的过程中 ...
- Mybatis查询
查询 查询的数据为单条实体类 使用实体类进行接受即可,或者使用list,map接口均可.后面两者比较浪费 使用实体类接受 mapper接口: User selectUserById(int useri ...
- 小程序打印饼图报错VM6541:1 thirdScriptError Converting circular structure to JSON;
今日使用小程序开发饼图页面,由于一个页面需要打印多个饼图,所以需要将echarts对象进行缓存,于是出现了如下js报错问题 一. 错误代码 VM6541:1 thirdScriptError Conv ...
- 让优惠再续一年!SHPC 老客专享
最近云筏君经常收到自家小伙伴发来的关于产品活动的关心慰问,掐指一算,哦,原来是一年一度大家喜闻乐见的剁手节(学名"双十一")马上要来了! 大家都知道,云筏家的产品向来主打高性价比, ...