1. Redis中常用的数据结构
Redis是一个开源的高性能键值数据库, 可以看成是一个高级的键值存储系统,提供了5种常用的数据类型:
string, hash, list, set和zset,其中用到的数据结构不尽相同,比如list用到的是链表结构,下面重点来介绍一下list的实现。
2. Redis链表结构
Redis链表结构底层是通过指针的方式来实现的,所以它的查询、插入和删除等操作都是常数时间的复杂度,相对于数组来说,因为数组是使用连续的存储空间来存储数据,所以当插入或删除元素时需要进行大量的移动操作,而链表则可以通过修改指针的方式来实现。
2.1 链表的节点
对于链表节点,Redis采用了指针域prev和next分别指向前驱节点和后继节点,以及一个用于存储数据的value域如下所示:
typedef struct listNode {
struct listNode *prev; //指向前驱节点
struct listNode *next; //指向后继节点
void *value; //节点的值
} listNode;
2.2 Redis链表的结构体
Redis链表通过一个list结构体来管理各个链表节点,该结构体中包含了指向链表头指针head和尾指针tail节点的指针域,节点数量len和值复制函数,节点释放函数等信息,如下所示:
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;
3. Redis链表的具体实现
3.1 链表的创建
3.1 链表的创建
Redis提供了创建链表的函数listCreate(),该函数创建并返回一个空链表,如下所示:
list *listCreate(void)
{
struct list *list;
if ((list = malloc(sizeof(*list))) == NULL)
return NULL;
list->head = list->tail = NULL;
list->len = 0;
list->dup = NULL;
list->free = NULL;
list->match = NULL;
return list;
}
在函数listCreate()中,首先开辟一块内存用于存储list结构体,并将该节点的head和tail节点指针初始化为NULL,即表示一个空链表,len节点数量为0,dup/free/match调用时为空。
3.2 链表的插入操作
Redis提供了从表头插入、从表尾插入、在某个节点之前插入一个新节点以及在某个节点之后插入一个新节点共4种插入链表节点的函数,下面以从表头插入为例给出Redis链表的插入操作实现:
list *listAddNodeHead(list *list, void *value)
{
struct listNode *node;
if ((node = malloc(sizeof(*node))) == NULL)
return NULL;
node->value = value;
if (list->len == 0) {
list->head = list->tail = node;
node->prev = node->next = NULL;
} else {
node->prev = NULL;
node->next = list->head;
list->head->prev = node;
list->head = node;
}
list->len++;
return list;
}
在函数listAddNodeHead()中,首先开辟一块内存用于存储listNode节点,然后将value域赋值为value所指向的节点的值,然后判断list是否为空表,如果是,则将head和tail节点都指向新节点,否则将新节点插入到表头,并调整head指针。值得一提的是,由于是从表头插入,在节点插入过程中,新节点的前驱节点为NULL,后继节点为原链表表头。
3.3 链表的删除操作
Redis提供了从表头删除、从表尾删除,删除某个值节点和按索引下标删除共4种删除链表节点的函数,下面以删除某个指定值节点为例给出Redis链表的删除操作实现:
void listDelNode(list *list, listNode *node)
{
if (node->prev)
node->prev->next = node->next;
else
list->head = node->next;
if (node->next)
node->next->prev = node->prev;
else
list->tail = node->prev;
if (list->free) list->free(node->value);
free(node);
list->len--;
}
在函数listDelNode()中,首先判断node节点的前驱节点是否存在,如果存在,将node的前驱节点的next指针指向node节点的下一个节点,否则表示node本身为表头节点,将head指向node的下一个节点;然后判断node的后继节点是否存在,如果存在,将node的后继节点的prev指针指向node节点的前驱节点,否则表示node本身为表尾节点,将tail指向node的前驱节点。最后用于释放所占用内存的释放函数进行释放,并更新len节点数量。
总结:
Redis中的list结构体底层实现使用了指针方式的链表结构,这种结构方式使得插入和删除操作可以在常数时间内进行,所以Redis的list数据类型的操作效率很高。
文章长度≈844字。