热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Redis源码解析3

EverythingisObject数据结构在Redis中,用robj结构表示一切数据对象,可以把它看作一种元数据(MetaData)各种不同的结构化数据,通过该对象进行封装、传递、变换、编码,而该对象本身却十分简单其类型定义如下:1typedefstructredisObject{un

Everything is Object 数据结构 在Redis中,用 robj 结构表示一切数据对象,可以把它看作一种元数据(MetaData) 各种不同的结构化数据,通过该对象进行封装、传递、变换、编码,而该对象本身却十分简单 其类型定义如下: 1 typedef struct redisObject { un

Everything is Object

数据结构

在Redis中,用 robj 结构表示一切数据对象,可以把它看作一种元数据(MetaData)
各种不同的结构化数据,通过该对象进行封装、传递、变换、编码,而该对象本身却十分简单

其类型定义如下:

1 typedef struct redisObject { unsigned storage:unsigned encoding:unsigned lru:refcount; } robj;

type字段表示数据类型,有以下几种定义:
REDIS_STRING // 字符串
REDIS_LIST // 链表
REDIS_SET // 集合
REDIS_ZSET // 有序集合
REDIS_HASH // HASH结构(注意,此处不同于传统意义上的哈希表(如stl::hash_map),这里的hash仅有字段散列的语义)
REDIS_VMPOINTER // VM指针(表示数据处于VM管理之下)

encoding字段表示数据编码方式,有以下几种定义:
REDIS_ENCODING_RAW // 原始编码,就是一个原始字符串
REDIS_ENCODING_INT // INT型编码,会将数字类型的字符串编码成该格式
REDIS_ENCODING_HT // 哈希表编码,源代码中以dict结构来管理
REDIS_ENCODING_ZIPMAP // 精简编码的hash结构,更省内存
REDIS_ENCODING_LINKEDLIST // 双向链表
REDIS_ENCODING_ZIPLIST // 精简编码的链表,更省内存
REDIS_ENCODING_INTSET // 精简编码的集合,更省内存
REDIS_ENCODING_SKIPLIST //

refcount字段是对象引用计数,每多一次引用,该计数加1;每减少一次引用,该计数减1,若减至0,则释放该对象内存

操作流程

Redis源代码中,有大量的robj指针在函数间传递
下面,以redis处理“set”命令为例,对 robj的操作进行讲解

1. 在 ProcessInlineBuffer、ProcessMultiBulkBUffer中,对命令缓存进行解析
通常是以空格为分隔符进行分离,每一个字符块都编码为一个独立的robj对象
对象存储在redisClient结构中的argc数组中,提供给后续函数使用
InlineBuffer:对应于Redis单行命令
MultiBulkBuffer:对应于多行命令

2. ProcessCommand对命令进行解析,然后进行函数分发
处理流程进入具体的命令处理函数

3. setCommand是对应于“set”命令的处理函数
该函数非常简单,网站空间,主要起到一个命令接入作用
它会对obj-val进行一次尝试性的编码转换,在本例中,会尝试将val对象转换为一个INT型的对象
转换完成后,进入内部共享函数setGenericCommand处理流程

4. setGenericCommand进行实际的“set”操作逻辑处理,即:
将kv键值对,加入到该连接对应的命名空间中(即一个dict结构)
对应于该dict结构,插入操作的具体语义由一个全局性的 dictType 进行定义:

1 dictType commandTableDictType = { NULL, NULL, dictSdsKeyCompare, dictSdsDestructor, dictRedisObjectDestructor };

根据这个操作定义,执行完比后
obj-key会复制一份原始字符串,将指针加入dict中(复制操作在dbAdd函数中实现)
obj-val直接将指针加入dict中,同时将该对象的refcount加1(加引用操作在setKey函数中实现)

5. 整个处理流程结束后,释放redisClient中的argc对象数组
在本例中,导致的结果是:
obj-key引用计数减1,最终值为0,导致对象删除
obj-val引用计数减1,最终值为1,该对象继续存在于全局key的dict表中

继续上一条“set”命令,针对更进一步的命令,对redis的对象编码转换作个初步了解
在下图中,主要注意 appendCommand中,由于该命令需要改变 obj-val对象的值,从而导致obj-val从INT编码状态解码到RAW编码状态。

解码时生成了临时对象 obj-decoded
临时对象与obj-append合并,将合并后的值赋给obj-val

引用计数 php中的变量

通过引用计数管理对象的生存期,是个好主意。通过上面两张图,也可以初步看出,redis如何通过引用计数来管理对象的生死
在很多动态类型的语言中,也有类似的做法
比如在PHP中,用以下结构表示变量:

1 struct _zval_struct { zend_uint refcount; zend_uchar type; zend_uchar is_ref; }; 7 typedef struct _zval_struct zval;

其中,value是一个 “zvalue_value” 类型的指针
zvalue_value是一个union类型的结构,将不同类型的数据整合进了同一个结构里

1 typedef union _zvalue_value { dval; { 5 char *val; 6 int len; HashTable *ht; zend_object_value obj; } zvalue_value;

可以看到,PHP中的变量有两种引用计数

1. refcount:直接引用计数,赋值时计数增加
2. is_ref:间接引用计数,赋引用时计数增加

之所以有这两个值,是因为PHP中的变量有引用的概念,香港虚拟主机,并且涉及到几个重要原则:

1. 赋值零拷贝:变量赋值时,不会复制一份新的变量,而是直接对zval结构加引用,且引用计数加在refcount变量上
但是,自定义类对象的赋值,和普通变量不同,默认是赋引用,导致计数加在is_ref上
我认为这是PHP语言设计上很混乱的一个地方

2. 写时复制:变量改变会引发变量分离,导致复制一份新变量

推荐阅读
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • Redis底层数据结构之压缩列表的介绍及实现原理
    本文介绍了Redis底层数据结构之压缩列表的概念、实现原理以及使用场景。压缩列表是Redis为了节约内存而开发的一种顺序数据结构,由特殊编码的连续内存块组成。文章详细解释了压缩列表的构成和各个属性的含义,以及如何通过指针来计算表尾节点的地址。压缩列表适用于列表键和哈希键中只包含少量小整数值和短字符串的情况。通过使用压缩列表,可以有效减少内存占用,提升Redis的性能。 ... [详细]
  • Redis API
    安装启动最简启动命令行输入验证动态参数启动配置文件启动常用配置通用命令keysbdsize计算key的总数exists判断是否存在delkeyvalue删除指定的keyvalue成 ... [详细]
  • 本文描述了作者第一次参加比赛的经历和感受。作者是小学六年级时参加比赛的唯一选手,感到有些紧张。在比赛期间,作者与学长学姐一起用餐,在比赛题目中遇到了一些困难,但最终成功解决。作者还尝试了一款游戏,在回程的路上感到晕车。最终,作者以110分的成绩取得了省一会的资格,并坚定了继续学习的决心。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • PHP图片截取方法及应用实例
    本文介绍了使用PHP动态切割JPEG图片的方法,并提供了应用实例,包括截取视频图、提取文章内容中的图片地址、裁切图片等问题。详细介绍了相关的PHP函数和参数的使用,以及图片切割的具体步骤。同时,还提供了一些注意事项和优化建议。通过本文的学习,读者可以掌握PHP图片截取的技巧,实现自己的需求。 ... [详细]
  • 关羽败走麦城时路过马超封地 马超为何没有出手救人
    对当年关羽败走麦城,恰好路过马超的封地,为啥马超不救他?很感兴趣的小伙伴们,趣历史小编带来详细的文章供大家参考。说到英雄好汉,便要提到一本名著了,没错,那就是《三国演义》。书中虽 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • PHP设置MySQL字符集的方法及使用mysqli_set_charset函数
    本文介绍了PHP设置MySQL字符集的方法,详细介绍了使用mysqli_set_charset函数来规定与数据库服务器进行数据传送时要使用的字符集。通过示例代码演示了如何设置默认客户端字符集。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • yum安装_Redis —yum安装全过程
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Redis—yum安装全过程相关的知识,希望对你有一定的参考价值。访问https://redi ... [详细]
author-avatar
莫梓智
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有