redis 基础数据结构 之 链表

本文探讨了Redis中的链表数据结构,重点解释了链表节点value字段使用void *类型的原因,以及如何通过dup、free和match三个函数处理不同类型的value。通过分析redisServer中的list *Client和list *slowlog,展示了Redis链表的灵活性和实用性。同时,文章提醒读者注意free函数可能引发的内存泄漏问题。
摘要由CSDN通过智能技术生成

给新观众老爷的开场

大家好,我是弟弟!
最近读了一遍 黄健宏大佬的 <<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

  1. 当一个redis客户端连接上redis服务器后,会创建一个redisClient,并且该对象的指针被加入到了 redisServer->Clients
  2. 当一个命令被执行完毕,如果命令执行慢,将慢日志写入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;
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值