redis源码解读(二):基础数据结构之ADLIST
2018-01-06 19:49
671 查看
近来在研读redis3.2.9的源码,虽然网上已有许多redis的源码解读文章,但大都不成系统,且纸上学来终觉浅,遂有该系列博文。部分知识点参照了黄建宏的《Redis设计与实现》。
redis 的基础数据结构主要有以下7种:
SDS(simple dynamic string):简单动态字符串
ADList(A generic doubly linked list):双向链表
dict(Hash Tables):字典
intset:整数集合
ziplist:压缩表
quicklist:快速列表(双向链表和压缩表二合一的复杂数据结构)
skiplist:跳跃链表
链表是最常用的数据结构之一了,对于其算法和ADT就不做赘述,不了解的同学可以google、百度或者参考《数据结构:C语言版》等书籍。
在简单的链表定义中,只用节点结构就已经能够满足链表的需求了,但是 redis 通过
通过这样的定义,
双向:可以灵活的访问前置或者后置节点
通过
其中
通过定义
以
所有和遍历有关的行为都收敛到了
通过实现不同的操作函数,
迭代操作由专门的迭代器实现
其他一些api就不做深入解析了,
欢迎访问我的个人博客
前言
本文探究的数据结构并不是 redis 对外暴露的5种数据结构,而是redis内部使用的基础数据结构,这些基础的数据结构 redis 不仅和 redisObj 一起构成了对外暴露的5种数据结构,还被运用于 redis 内部的各种存储和逻辑交互,支撑起了 redis 的运行。redis 的基础数据结构主要有以下7种:
SDS(simple dynamic string):简单动态字符串
ADList(A generic doubly linked list):双向链表
dict(Hash Tables):字典
intset:整数集合
ziplist:压缩表
quicklist:快速列表(双向链表和压缩表二合一的复杂数据结构)
skiplist:跳跃链表
ADList
ADList(A generic doubly linked list)是 redis 自定义的一种双向链表,广泛运用于 redisClients 、 redisServer 、发布订阅、慢查询、监视器等(注:3.0及以前还会被运用于list结构中,在3.2以后被
quicklist取代)。
链表是最常用的数据结构之一了,对于其算法和ADT就不做赘述,不了解的同学可以google、百度或者参考《数据结构:C语言版》等书籍。
链表和链表节点定义
每个链表节点都是用一个listNode表示
typedef struct listNode { // 双向节点 struct listNode *prev; struct listNode *next; void *value; // 空指针,可以被指向任何类型 } listNode;
在简单的链表定义中,只用节点结构就已经能够满足链表的需求了,但是 redis 通过
list结构体持有链表,使得链表操作更加方便、规范。
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;
dup为节点复制函数
free为节点释放函数
match为节点比较函数
通过这样的定义,
adlist有了以下优点:
双向:可以灵活的访问前置或者后置节点
list头指针和尾指针:可以方便的获取头尾节点或者从头尾遍历查找
len:使获取
list由O(N)变为O(1)
通过
void实现多态:不同的实例化链表对象可以持有不同的值,其对应的3个操作函数也可以自定义,是不是有点
interface的感觉!
链表迭代器
除了双向链表的定义外,redis 还定义了一个迭代器,用于遍历链表:typedef struct listIter { // 列表迭代器 listNode *next; int direction; // 迭代器遍历方向 } listIter;
其中
direction用于标识迭代器的遍历方向:
#define AL_START_HEAD 0 // 从头遍历 #define AL_START_TAIL 1 // 从尾遍历
通过定义
listIter,redis 在需要遍历
list时,不需要再复制各种tmp值,只需要调用
listIter的遍历函数。
以
listSearchKey为例:
listNode *listSearchKey(list *list, void *key) // list查找key { listIter iter; listNode *node; listRewind(list, &iter); // 初始化迭代器 while((node = listNext(&iter)) != NULL) { // 迭代器遍历 if (list->match) { // 如果定义了match函数 if (list->match(node->value, key)) { return node; } } else { // 直接进行值比较 if (key == node->value) { return node; } } } return NULL; }
所有和遍历有关的行为都收敛到了
listIter中,
list就专注负责存储。
总结一波
redis 的链表是双向无环的ADList持有链表的头尾节点指针,链表长度以及链表操作函数
listNode的值为void,可以保存任意类型的值
通过实现不同的操作函数,
ADlist可以保存各种不同类型的数据
迭代操作由专门的迭代器实现
其他一些api就不做深入解析了,
ADList相关的定义都在
adlist.h和
adlist.c文件中,如果感兴趣可以自行查看
欢迎访问我的个人博客
相关文章推荐
- redis源码解读(五):基础数据结构之ziplist
- redis源码解读(六):基础数据结构之quicklist
- redis源码解读(七):基础数据结构之skiplist
- redis源码解读(一):基础数据结构之SDS
- Flink内存管理源码解读之基础数据结构
- Redis基础、应用场景、数据结构及案例
- 基础数据结构之TreeMap源码分析
- redis源码剖析(基础数据结构篇)——字典(哈希)
- 【Redis源码剖析】 - Redis内置数据结构之字符串sds
- MySQL系列:innodb源码分析之基础数据结构
- nginx源码分析2———基础数据结构三(内存池)
- nginx源码分析2———基础数据结构二(链表和双向链表)
- redis 源码学习(核心数据结构剖析)
- PostgreSQL源码解读 基础结构 node
- Rocksdb源码剖析(2)--基础数据结构(组件)1
- Flink内存管理源代码解读之基础数据结构
- Redis内存管理的基石zmallc.c源码解读(附录):源码结构表
- android系统源码分析——binder基础数据结构
- 【Redis源码剖析】 - Redis内置数据结构之字符串sds
- memched1.0源码阅读(2)——基础数据结构