「这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战」
今天说一下Redis 列表类型的底层实现双向链表。
链表是一个比较常见的数据结构,C 语言本身不支持链表类型,但是列表类型需要用到,所以Redis自己实现了一个双向链表。
链表定义
链表节点的定义
typedef struct listNode{
// 前置节点
struct listNode *prev;
// 后置节点
struct listNode *next;
// 节点的值
void *value;
}listNode
链表结构List 定义
typedef struct list {
// 表头结点
listNode *head;
// 表尾节点
listNode *tail;
// 链表所包含的节点数量
unsigned long len;
// 其他函数
...
}list;
整体结构图
注意 表头结点的 prev 指针和表尾结点的 next 指针都指向 null, 所以 Redis 的链表是一个无环链表。
双向链表的优缺点
优点
-
由于链表节点中定义了当前节点的前置节点和后置节点,所以在查询时复杂度为O(1)。
-
由于在List中定义了链表包含的节点,所以查询链表长度的复杂度为O(1),对用的命令为 LLEN。
-
由于在List中定义了链表的表头节点和表尾节点,对链表的表头和表尾进行插入的复杂度都为O(1),查询表头和表尾数据同样复杂度为O(1),对应的命令为 LPUSH、 RPUSH、 LPOP、 RPOP 等。
-
链表中的节点由
prev和next两个指针相连,所以可以不在一块连续的内存中,更合理的利用空间,但这也增加了寻址的时间。 -
在操作过程中,删除、修改、增加数据,虽然在查询时,复杂度比较高,但是进行这些操作的时候却比较简单,不会影响其他的元素。
缺点
由于链表的结构导致,在进行范围查询和等值查询时,复杂度为O(N),因此在执行LRANGE、listIndex等命令的时候,如果链表很长,由于复杂度比较高,可能会造成阻塞。
结尾
其实Redis的List类型是由两种底层数据结构实现的,另外一种是压缩列表,听名字就能够联想到,这种结构可以更节省空间。下一篇接着说压缩列表的实现。