前言

  大体学习完t_string.c的代码,我们正式进入下一个文件的学习,这一次我们学习的是t_list.c文件,从文件名我们可以知道这是一个关于列表的相关命令的源代码。
  在学习列表命令之前,这里要介绍一下,在Redis中列表的底层数据结构由压缩表和链表实现,所以在今天的学习中会发现大量的底层数据结构判断,根据不同的数据结构使用不同的方法。不过这里要注意的是,这里是基于3.0版本的源代码,3.2版本之后发生了变化,3.2版本之后使用了快速表来实现。

1 pushGenericCommand

1.1 方法说明

  这是一个lpush、rpush命令的基础方法,两个命令都会调用这个方法。

1.2 方法源代码

void pushGenericCommand(redisClient *c, int where) {int j, waiting = 0, pushed = 0;//获取列表对象robj *lobj = lookupKeyWrite(c->db,c->argv[1]);//如果对象的类型不为列表,报类型错误if (lobj && lobj->type != REDIS_LIST) {addReply(c,shared.wrongtypeerr);return;}//遍历推入的元素for (j = 2; j < c->argc; j++) {c->argv[j] = tryObjectEncoding(c->argv[j]);//如果列表不存在,则创建一个压缩表对象if (!lobj) {lobj = createZiplistObject();dbAdd(c->db,c->argv[1],lobj);}//调用列表推入元素方法listTypePush(lobj,c->argv[j],where);pushed++;}//返回列表的元素总个数addReplyLongLong(c, waiting + (lobj ? listTypeLength(lobj) : 0));//如果推入了元素//标记键被修改,并发送通知事件if (pushed) {//判断事件类型char *event = (where == REDIS_HEAD) ? "lpush" : "rpush";//标记键被修改 signalModifiedKey(c->db,c->argv[1]);//通知事件notifyKeyspaceEvent(REDIS_NOTIFY_LIST,event,c->argv[1],c->db->id);}//改变状态值增加server.dirty += pushed;
}

1.3 代码理解

  可以看到pushGenericCommand总共做了以下几件事情

  1. 获取键对象
  2. 判断键对象的类型是否为列表类型
  3. 遍历推入的元素,一个个的推入列表中
  4. 如果列表不存在,则创建一个压缩表对象,可以看到列表默认会使用压缩表作为底层数据结构。
  5. 调用listTypePush将元素推入列表中。
  6. 返回列表的总数量
  7. 如果推入了元素,则标记键已被修改,并且触发通知事件。
  8. 记录更改状态值。

  这里个人有一点不太明白的是,不知道为什么这里判断列表对象不存在的逻辑要写在循环里,而不是写在外面,可能作者有其他的考量。

2 listTypePush

2.1 方法说明

  这个方法可以说是实现push命令的核心方法了,根据列表的底层数据结构调用不同的方法来推入元素。

2.2 方法源代码

void listTypePush(robj *subject, robj *value, int where) {/* Check if we need to convert the ziplist *///检查该值是否会引起数据结构变化listTypeTryConversion(subject,value);//检查列表的长度是否超过等于压缩表最大值//如果是的话,需要转变数据结构为链表if (subject->encoding == REDIS_ENCODING_ZIPLIST &&ziplistLen(subject->ptr) >= server.list_max_ziplist_entries)listTypeConvert(subject,REDIS_ENCODING_LINKEDLIST);//如果数据结构是压缩表//则调用压缩表相关方法推入元素if (subject->encoding == REDIS_ENCODING_ZIPLIST) {int pos = (where == REDIS_HEAD) ? ZIPLIST_HEAD : ZIPLIST_TAIL;value = getDecodedObject(value);subject->ptr = ziplistPush(subject->ptr,value->ptr,sdslen(value->ptr),pos);decrRefCount(value);}//如果数据结构是链表 //则调用链表相关方法推入元素else if (subject->encoding == REDIS_ENCODING_LINKEDLIST) {if (where == REDIS_HEAD) {listAddNodeHead(subject->ptr,value);} else {listAddNodeTail(subject->ptr,value);}incrRefCount(value);} else {redisPanic("Unknown list encoding");}
}

2.3 代码理解

  这个方法做了以下几件事情

  1. 判断当前值的类型,是否会引起底层数据结构变化。
  2. 判断当前压缩列表的元素个数是否超过了配置,如果超过了配置则要转变底层数据结构为链表。
  3. 如果当前数据结构为压缩表,则调用压缩表相关方法推入元素。
  4. 如果当前数据结构为链表,则调用链表相关方法推入元素。

3 lpushCommand

3.1 命令说明

  向列表的头部插入一个元素,成功返回列表元素的个数

3.2 命令实践

3.3 命令源代码

void lpushCommand(redisClient *c) {pushGenericCommand(c,REDIS_HEAD);
}

3.4 代码理解

  可以看到lpushCommand没有过多其他的代码,直接调用了pushGenericCommand这个方法,并传入了一个参数REDIS_HEAD表示是从头部推入元素,这个参数在redis.h文件里有定义。

#define REDIS_HEAD 0
#define REDIS_TAIL 1

4 rpushCommand

4.1 命令说明

  向列表的尾部插入一个元素,成功返回列表元素的个数

4.2 命令实践

4.3 命令源代码

void rpushCommand(redisClient *c) {pushGenericCommand(c,REDIS_TAIL);
}

4.4 代码理解

  可以看到rpushCommand也是直接调用了pushGenericCommand这个方法,并传入了一个参数REDIS_TAIL表示是从尾部推入元素。

5 总结

1、列表的底层数据结构由两种实现方式,一种是压缩表,另一种是链表
2、列表的底层数据结构默认使用压缩表,达到某种条件会触发转换动作。
3、触发数据结构转换的条件有:如果值类型不符合则会转换,如果元素的个数超过配置也会转换。
4、意味着每次推入一个元素都会检查是否要转换数据结构。
5、lpush和rpush命令都是调用了pushGenericCommand这个方法,只不过通过位置参数控制推入的位置。
6、推入元素会标记列表键被修改,并且触发通知事件。
7、server.dirty 增加的是推入元素的数量。

Redis源码学习(6),t_list.c 学习(一),rpush、lpush命令实现学习相关推荐

  1. Redis源码学习(20),学习感悟

      最近学习Redis源码也有半个月的时间了,有不少收获也有不少感悟,今天来好好聊聊我学习的感悟. 1 发现问题   人非圣贤孰能无过,只要是人难免会犯错,回顾我之前的学习历程,其实是可以发现不少的问 ...

  2. redis源码学习笔记目录

    Redis源码分析(零)学习路径笔记 Redis源码分析(一)redis.c //redis-server.c Redis源码分析(二)redis-cli.c Redis源码剖析(三)--基础数据结构 ...

  3. 阅读 redis 源码,学习缓存淘汰算法 W-TinyLFU

    所有 IT 从业者都接触过缓存,一定了解基本工作原理,业界流行一句话: 缓存就是万金油,哪里有问题哪里抹一下 .那他的本质是什么呢? 上图代表从 cpu 到底层硬盘不同层次,不同模块的运行速度,上层多 ...

  4. 10年大厂程序员是如何高效学习使用redis的丨redis源码分析丨redis存储原理

    10年大厂程序员是怎么学习使用redis的 1. redis存储原理分析 2. redis源码学习分享 3. redis跳表和B+树详细对比分析 视频讲解如下,点击观看: 10年大厂程序员是如何高效学 ...

  5. 为什么C/C++程序员都要阅读Redis源码之:Redis学习事件驱动设计

    0. 为什么我说C/C++程序员都要阅读Redis源码 主要原因就是『简洁』.如果你用源码编译过Redis,你会发现十分轻快,一步到位.其他语言的开发者可能不会了解这种痛,作为C/C++程序员,如果你 ...

  6. c++ 多线程 类成员函数_为什么我说C/C++程序员都要阅读Redis源码之:通过Redis学习事件驱动设计

    0. 为什么我说C/C++程序员都要阅读Redis源码 主要原因就是『简洁』.如果你用源码编译过Redis,你会发现十分轻快,一步到位.其他语言的开发者可能不会了解这种痛,作为C/C++程序员,如果你 ...

  7. 【Redis学习笔记】2018-05-30 Redis源码学习之Ziplist、Server

    作者:施洪宝 顺风车运营研发团队 一. 压缩列表 压缩列表是Redis的关键数据结构之一.目前已经有大量的相关资料,下面几个链接都已经对Ziplist进行了详细的介绍. http://origin.r ...

  8. Redis源码分析(一)--Redis结构解析

    从今天起,本人将会展开对Redis源码的学习,Redis的代码规模比较小,非常适合学习,是一份非常不错的学习资料,数了一下大概100个文件左右的样子,用的是C语言写的.希望最终能把他啃完吧,C语言好久 ...

  9. Redis源码解析——有序整数集

    有序整数集是Redis源码中一个以大尾(big endian)形式存储,由小到大排列且无重复的整型集合.它存储的类型包括16位.32位和64位的整型数.在介绍这个库的实现前,我们还需要先熟悉下大小尾内 ...

  10. Redis源码分析:基础概念介绍与启动概述

    Redis源码分析 基于Redis-5.0.4版本,进行基础的源码分析,主要就是分析一些平常使用过程中的内容.仅作为相关内容的学习记录,有关Redis源码学习阅读比较广泛的便是<Redis设计与 ...

最新文章

  1. WPF拖拽过程中修改鼠标指针属性
  2. 秒杀系统设计架构与实现
  3. Linux中常用命令(文件与目录)
  4. js中的数组Array定义与sort方法使用示例
  5. 【报告分享】2021中国中高端人才趋势报告.pdf(附下载链接)
  6. 谈谈出入React框架踩过的坑
  7. capjoint一些生成文件的解释
  8. PowerDesigner生成PowerBuilder扩展属性~
  9. Socket通信的安全策略问题
  10. 【图像融合】基于matlab高斯金字塔+拉普拉斯金字塔彩色水下图像融合【含Matlab源码 1629期】
  11. 【Linux】Linux根据文件路径查找索引节点
  12. isPostback 的原理及作用(非常简单)
  13. 内存控制器与SDRAM【赞】
  14. Windows服务器IE浏览器无法下载文件解决方法
  15. 源码编译shc-3.8.7
  16. 2019微信公开课张小龙演讲全文
  17. Linux面试常考命令
  18. 树形数据结构——ClosureTable
  19. python--批量离线安装python包
  20. 数据挖掘算法——常用分类算法总结

热门文章

  1. 使用基于 Eclipse 插件框架的 ODA(Open Data Access)进行自定义数据驱动开发
  2. LeetCode187. 重复的DNA序列
  3. 室内外蓝牙定位巡更电子胸牌
  4. go-pitaya学习笔记(2)-chatDemo分析
  5. 第三方程序完美整合phpwind8的UC用户中心-教程加P8
  6. 图像变换,transform的使用
  7. ip virtual-reassembly:IP虚拟分片重组
  8. matlab2c使用c++实现matlab函数系列教程-inv函数
  9. STM32F103输出多路死区互补PWM波
  10. 人月神话 - 人与月