Redis/链表
Redis中链表的应用非常广泛,比如列表键的底层实现之一就是链表。
当一个列表键包含了数量比较多的元素,或者列表中包含的元素都是比较长的字符串时,Redis就会使用链表作为列表键的底层实现
为什么列表包含的元素是不够长的字符串时,Redis不使用链表?
这是因为链表需要保存指针变量用来指向其他元素,如果包含的元素是较短的字符串,那么可能在整个链表节点中的空间占比不够高,导致存储密度较低,会有大量空间浪费在存储指针上
Redis中使用到链表的其他功能:
- 发布与订阅
- 慢查询
- 监视器
- 保存多个客户端的状态信息
- 构建客户端输出缓冲区(output buffer)
链表及其节点的实现
每个链表节点都是使用的在adlist.h
文件下的listNode
结构体
typedef struct listNode {
// 前置节点
struct listNode* prev;
// 后置节点
struct listNode* next;
// 节点的值
void *value;
}listNode;
可以看到多个listNode
能够通过prev
和next
指针组成双端链表
实际上,Redis使用的并非是listNode
结构体来持有链表,而是adlist.h
下的list
结构体来持有链表,这方便了有关的操作
typedef struct list {
// 表头节点
listNode *head;
// 表尾节点
listNode *tail;
// 链表包含的节点数量
unsigned long len;
// 节点值复制函数
void (*dup)(void *ptr);
// 节点值释放函数
void (*free)(void *ptr);
// 节点值对比函数
void (*match)(void *ptr, void *key);
} list;
Redis链表特性总结:
- 双端链表,设有
prev
和next
指针 - 无环,表头的
prev
和表尾next
指针都是指向NULL - 带表头和表尾
- 有维护链表长度的变量
- 多态。链表节点使用
void*
指针来保存节点值,并且可以通过dup
、free
、match
三个属性为节点值设置类型特定的函数