Redis zset 共享对象

前言

本文介绍 Redis 中 skiplist 编码的 zset 对象是如何共享对象的。

skiplist 编码的 zset 对象为了同时支持高效的点查询和范围查询,内部使用了跳表和哈希表。倘若将每个插入的元素都拷贝两份,分别插入跳表和哈希表,将浪费大量的内存,那 Redis 是怎么让二者共享对象的呢?

介绍的源码基于 Redis 5.0.8 版本,不同版本采用的方式可能不同,同时会删除一些不影响理解的部分。

前置知识

对象的表示

Redis 中的每个对象都由一个 redisObject 结构表示,该结构中和保存数据有关的三个属性分别是 type、encoding 和 ptr。

// redisObject 用来保存各种对象
typedef struct redisObject {unsigned type:4;        // 数据类型unsigned encoding:4;    // 编码类型unsigned lru:LRU_BITS; int refcount;           // 引用计数void *ptr;              // 指向值的指针
} robj;

字符串对象

// sdshdr64 len 和 alloc 用的是 uint64_t
struct sdshdr64 {uint64_t len;uint64_t alloc;char buf[];
};

Redis 中的字符串对象由 SDS 表示,它的主要属性有以下三个:

  • len 属性记录已使用长度
  • alloc 属性记录分配空间长度
  • buf 属性是一个 char 类型的数组,记录实际的数据

zset 对象

// zset 有序集合,里面包含哈希表和跳表
typedef struct zset {dict *dict;zskiplist *zsl;
} zset;

client

client 中记录了我们输入的命令的参数。

typedef struct client {int argc;               // 当前命令的参数数量robj **argv;            // 当前命令的参数
} client;

问题探索

下面就顺着源码一步步寻找 zset 是怎么共享对象的。

当我们在 Redis 客户端输入如下命令时:

127.0.0.1:6379> ZADD study 1 mem1 2 mem2 3 mem3

会调用 zaddGenericCommand 函数。该函数会有如下一段向 zset 插入数据的代码:

for (j = 0; j < elements; j++) {double newscore;score = scores[j];int retflags = flags;ele = c->argv[scoreidx+1+j*2]->ptr;int retval = zsetAdd(zobj, score, ele, &retflags, &newscore);
}

以上面的命令为例:

  • scores 为 double 类型的数组,记录所有的 score,即 1 2 3
  • scoreidx 为 2,指示实际的 score score-element pair 的开始位置
  • elements 为 3,即 score 和 score-element pair 的数量
  • argv 为 robj * 类型的数组,记录了所有参数对应的字符串对象

关键是 ele 是什么,ele 的类型是 sds,sds 类型就是 char * 类型的指针。当 j 为 0 时,scoreidx+1+j*2 为 3,c->argv[3] 为从 mem1 构建的 SDS 所属的 redisObject 对象,c->argv[3]->ptr 是指向 SDS 底层数组的指针,所以 ele 就是指向 SDS 底层数组的指针。

在 zsetAdd 函数中,会用如下代码将数据插入跳表和哈希表:

ele = sdsdup(ele);
znode = zslInsert(zs->zsl,score,ele);
serverAssert(dictAdd(zs->dict,ele,&znode->score) == DICT_OK);

首先复制了一份新的 SDS 对象,然后将它插入到跳表和哈希表中。ele 是一个 char * 类型的指针,因此插入跳表和哈希表中的 key 是一个指针,这个指针指向同一份数据,这两种结构通过指针来共享相同的成员。同理,score 在跳表中是一个 double 类型的浮点数,在哈希表中是保存这个浮点数的指针。

这里为什么需要复制一份,不太理解,以下是个人猜测。

假如不复制,直接使用 c->argv 里面保存的 redisObject 内的 SDS 对象。redisObject 中还有一些我们并不需要的变量,若是字符串比较短,redisObject 和 SDS 内存是一起申请的,不能归还其中一个,就白白浪费一些空间来保存并不需要的数据;若是字符串比较长,redisObject 和 SDS 内存是分开申请的,这样可以做到只归还 redisObject 的内存,SDS 的内存交给 zset 归还,但这样的话处理上比较繁琐,需要加一些额外的逻辑来判断,同样耗费时间。

参考资料

  • Redis 5.0.8
  • 《Redis 设计与实现》
  • 极客时间:Redis 源码剖析与实战

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/51121.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity 骨骼动画(Skinned Mesh Renderer): 角色动画的高级渲染

在Unity中&#xff0c;骨骼动画(Skinned Mesh Renderer)是一种用于高级角色动画渲染的组件。它允许开发者将复杂的3D模型和动画应用到游戏角色上&#xff0c;实现逼真的视觉效果。本文将探讨Skinned Mesh Renderer的基本概念、使用方法以及如何优化性能。 Skinned Mesh Render…

Docker容器的数据管理

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 我们在使用Docker的过程中&#xff0c;往往需要能查看容器内应用产生的数据&#xff0c;或者需要把容器内的数据进行备份&#x…

信创终端操作系统上vmware的命令行操作

原文链接&#xff1a;信创终端操作系统上vmware的命令行操作 Hello&#xff0c;大家好啊&#xff01;今天给大家带来一篇关于在信创终端操作系统上使用命令行操作VMware的文章。通过命令行管理VMware虚拟机可以提高效率&#xff0c;特别是在需要批量操作或自动化管理时。本文将…

Python脚本批量将md文件转化成pdf

自己学编程时做了很多笔记&#xff0c;如今累积起来已经有几十个了&#xff0c;有很多图片链&#xff0c;怕哪天图床垮了图片就找不到了&#xff0c;于是就想把当时的 md 文件都转成 pdf 。 看了点文章&#xff0c;就开始实践&#xff1a; 1.下载 pandoc 和 CTex 2.ChatGpt …

VS2022创建C C++ GTEST工程

原因 需要对带代码进行单元测试&#xff0c;选择在Visual studio 中使用GTEST 框架。 实施 创建一个常规的控制台可执行程序。然后使用NUGET安装包 安装GTEST 头文件和动态库&#xff0c;同时安装GTEST ADAPTER。 安装可能提示找不到包源&#xff0c;此时需要根据提示配置一…

如何使用API快速打造健康医疗系统?

在数字医疗市场&#xff0c;数据是人们经常谈及的一个话题。当前&#xff0c;消费者医疗和健康应用收集的数据越来越多&#xff0c;电子健康记录的实施也创造出了大量有关病人的电子信息。 API接口在智慧医院跨网、跨机构之间的业务协同和数据共享交换中得到数据共享。支撑了医…

环境搭建-Docker搭建MySQL

Docker搭建MySQL 一、前言二、Docker安装MySQL5.7安装三、建立远程连接四、Docker安装MySQL8.0 一、前言 本文使用的Docker使用Windows搭建&#xff0c;Linux版本的搭建方式一样。 Windows系统搭建Docker 二、Docker安装MySQL5.7安装 拉取MySQL指定版本的镜像 docker pull …

Redis从入门到超神-(十二)Redis监听Key的过期事件

前言 试想一个业务场景&#xff0c;订单超过30分钟未支付需要做自动关单处理,修改订单状态&#xff0c;库存回退等&#xff0c;你怎么实现&#xff1f;方案一&#xff1a;可以使用定时任务扫表&#xff0c;通过支付状态和下单时间来判断是否支付过期。但是这样的方案是非常消耗…

2846. 边权重均等查询

Powered by:NEFU AB-IN Link 文章目录 2846. 边权重均等查询题意思路代码 2846. 边权重均等查询 题意 现有一棵由 n 个节点组成的无向树&#xff0c;节点按从 0 到 n - 1 编号。给你一个整数 n 和一个长度为 n - 1 的二维整数数组 edges &#xff0c;其中 edges[i] [ui, vi…

实战:在Spring Boot中使用线程池

在处理高并发请求时&#xff0c;直接创建和销毁线程会带来巨大的开销。线程池通过重用现有的线程来减少这种开销&#xff0c;提高应用的性能和响应速度。SpringBoot提供了便捷的方式来配置和使用线程池。 在SpringBoot中使用线程池&#xff0c;可以有效提高应用的并发处理能力…

MacOS DockerDesktop配置文件daemon.json的位置

如果因为通过可视化页面修改配置错误导致客户端启动不起来&#xff0c;可以去找对应的配置文件通过 vim 修改后重启客户端 cd ~/.docker/

C#使用Clipper2进行多边形合并、相交、相减、异或的示例

Clipper2库介绍 开源库介绍&#xff1a; Clipper2在Github上的地址&#xff1a;https://github.com/AngusJohnson/Clipper2 Clipper2库对简单和复杂多边形执行交集&#xff08;Intersection&#xff09;、并集&#xff08;Union&#xff09;、差分&#xff08;Difference&…

protobuf编译之后找不到isStringEmpty方法

原因: 与mysql的jar包冲突了 解决办法&#xff1a; 在MySQL中排除proto-java <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.22</version><scope>runtime</scope>&l…

Webpack 从入门到精通

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 一、Webpack 简介 二、Webpack 的核心概念 三、Webpack 的安装与配置 安装 Node.js 安装 Webpack 初始…

戴着苹果Vision Pro,如何吃花生米

6月底苹果Vision Pro国内开售&#xff0c;我早早到官网预订了一台。选择必要的配件&#xff0c;输入视力信息&#xff0c;定制符合自己视力的蔡司镜片。确实贵。把主要配件和镜片配齐&#xff0c;要3万6&#xff0c;比Pico、META的眼镜贵一个数量级。 Vision Pro出来后&#x…

C语言之.(结构体,枚举,共用体的应用)

C语言之.(结构体&#xff0c;枚举&#xff0c;共用体的应用) 结构体&#xff0c;套联合体&#xff0c;枚举的应用例子

C语言100基础拔高题(3)

1.利用递归函数调用方式&#xff0c;将所输入的5个字符&#xff0c;以相反顺序打印出来。 解题思路&#xff1a;通过反复调用一个打印最后一个元素的函数&#xff0c;来实现此功能。源代码如下: #include<stdio.h> void oposize(char str[], int len); int main() {//利…

Springboot集成Elasticsearch High Level REST Client实现增删改查实战

获取源码&#x1f6a9; 需要完整代码资料&#xff0c;请一键三连后评论区留下邮箱&#xff0c;安排发送&#xff01;&#xff01;&#xff01;&#x1f916; 什么是High Level REST Client&#xff1f; Elasticsearch 的 High Level REST Client 是一个用于与 Elasticsearch…

ARCGIS PRO DSK GraphicsLayer创建文本要素

一、判断GraphicsLayer层【地块注记】是否存在&#xff0c;如果不存在则新建、如果存在则删除所有要素 Dim GraphicsLayer pmap.GetLayersAsFlattenedList().OfType(Of ArcGIS.Desktop.Mapping.GraphicsLayer).FirstOrDefault() 获取当前map对象中的GetLayer图层 Await Queue…

Web 安全之 OOB(Out-of-Band)攻击详解

OOB&#xff08;Out-of-Band&#xff09;攻击是指一种网络安全攻击技术&#xff0c;攻击者利用目标系统与外部资源之间的通信来获取信息或检测漏洞是否存在。这种攻击方式不会通过目标系统的直接响应来显示攻击结果&#xff0c;而是通过外部信道&#xff08;如 DNS、HTTP、SMTP…