php redis 源码分析,Redis源码解析3

Redis 使用 robj 结构表示所有数据对象,作为元数据进行数据封装、传递和编码。对象包含类型、编码方式和引用计数等信息。在处理如 'set' 命令时,涉及对象的编码转换和引用计数管理。例如,当值从 INT 编码转换为 RAW 编码时,会解码生成临时对象,然后合并到目标对象中,更新引用计数。Redis 的这种机制确保了高效的数据操作和内存管理。
摘要由CSDN通过智能技术生成

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

Everything is Object

数据结构

在Redis中,用 robj 结构表示一切数据对象,可以把它看作一种元数据(MetaData)

各种不同的结构化数据,通过该对象进行封装、传递、变换、编码,而该对象本身却十分简单

其类型定义如下:

1 typedef structredisObject {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的操作进行讲解

c6348cc1bf7e16bcb402a79bba205522.png

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

5e98c86f5278c7361ac0011e3db7414a.png

引用计数

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

intlen;HashTable *ht; zend_object_value obj; } zvalue_value;

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

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

2. is_ref:间接引用计数,赋引用时计数增加

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

1. 赋值零拷贝:变量赋值时,不会复制一份新的变量,而是直接对zval结构加引用,且引用计数加在refcount变量上

但是,自定义类对象的赋值,和普通变量不同,默认是赋引用,导致计数加在is_ref上

我认为这是PHP语言设计上很混乱的一个地方

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

本文原创发布php中文网,转载请注明出处,感谢您的尊重!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值