redis数据结构之 链表
给新观众老爷的开场
大家好,我是弟弟!
最近读了一遍 黄健宏大佬的 <<Redis 设计与实现>>,对Redis 3.0版本有了一些认识,该书作者有一版添加了注释的 redis 3.0源码。
网上说Redis代码写得很好,为了加深印象和学习redis大佬的代码写作艺术,了解工作中使用的redis 命令背后的源码逻辑,便有了从redis命令角度学习redis源码的想法。
(全文提到的redis服务器,都指在 mac os 上启动的一个默认配置的单机redis服务器)
redis的基础数据结构之 链表
我以为的链表是这样的👇
typedef struct listNode {
// 前置节点
struct listNode *prev;
// 后置节点
struct listNode *next;
// value的类型取决于具体的场景
int value;
} listNode;
redis中的链表是这样的👇
/*
* 双端链表结构
*/
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;
/*
* 双端链表节点
*/
typedef struct listNode {
// 前置节点
struct listNode *prev;
// 后置节点
struct listNode *next;
// 节点的值
void *value;
} listNode;
/*
* 双端链表迭代器
*/
typedef struct listIter {
// 当前迭代到的节点
listNode *next;
// 迭代的方向
int direction;
} listIter;
观众老爷:“代码看着费劲,来,开始你的表演”
弟弟 :“ 😅 ”
Q1: 为什么redis链表节点的value字段是void *类型
个人理解,这个体现 redis链表的高度抽象(对,又是抽象)。
链表就做链表的事情,把你们一个一个节点链接起来,有前/后指针就完事。
至于值,想放啥放啥。当然了,对于链表节点里的值怎么解读,跟链表没关系。
谁放的值谁负责解读 🙃️
这样一个链表里就能放任意数据类型了,让我们在源码里找一找证据。
redisServer里的 list *Client与 list *slowlog
- 当一个redis客户端连接上redis服务器后,会创建一个redisClient,并且该对象的指针被加入到了 redisServer->Clients
- 当一个命令被执行完毕,如果命令执行慢,将慢日志写入redisServer->slowlog里
redisServer->Clients 与 redisServer->slowlog 都是list *类型,源码如下👇
struct redisServer
{
...
// 一个链表,保存了所有客户端状态结构
list *clients; /* List of active clients */
...
// 保存了所有慢查询日志的链表
list *slowlog; /* SLOWLOG list of commands */
...
};
往一个list尾部里添加一个元素函数是
list *listAddNodeTail(list *list, void *value)
可以看到 value的类型是void *
而在redisServer.Clients链表中加入redisClient时,传入的value类型是redisClient *
/*
* 创建一个新客户端
*/
redisClient *createClient(int fd)
{
// 分配空间
redisClient *c = zmalloc(sizeof(redisClient));
...
// 如果不是伪客户端,那么添加到服务器的客户端链表中
if (fd != -1)
listAddNodeTail(server.clients, c);
...
// 返回客户端
return c;
}
list *listAddNodeTail(list *list, void *value)
{
listNode *node;
// 为新节点分配内存
if ((node = zmalloc(sizeof(*node))) == NULL)
return NULL;