当前位置: 首页 > news >正文

redis有序集合写入和求交集的速度

背景

团队小伙伴做了一个需求。大概的需求是有很多的图片作品,图片作品有一些类别,每个人进入到每个类别的作品业,根据权重优先查看权重最高的的作品,权重大概是基于每个人对该作品的浏览计算,浏览过的作品放在最后展示。小伙伴是基于redis的有序集合的方式实现的,主要用到写入和求交集这两个操作。我们这个系统的作品数量不算多,目前只有5000左右,用户也只有5000左右,这么小的数据量用这种方式实现,我原本觉得是没什么问题的。但是,实际测试下来,就很离谱,不知道他的程序里哪里导致的异常耗时。出于好奇,我写了一段测试代码,测试一下不同数量级的数据,写入和求交集的时长。

测试程序

package mainimport ("context""fmt""log""math/rand""time""github.com/redis/go-redis/v9"
)// BatchZAdd function to batch insert elements into a Redis sorted set
func BatchZAdd(ctx context.Context, rdb *redis.Client, key string, members []redis.Z) error {// Pipeline to batch the ZADD commandspipe := rdb.Pipeline()if err := pipe.ZAdd(ctx, key, members...).Err(); err != nil {return fmt.Errorf("failed to add member to sorted set: %v", err)}// Execute the pipeline_, err := pipe.Exec(ctx)if err != nil {return fmt.Errorf("failed to execute pipeline: %v", err)}return nil
}func main() {// Create a new Redis clientrdb := redis.NewClient(&redis.Options{Addr:     "10.10.37.100:6379", // Replace with your Redis server addressPassword: "dreame@2020",DB:       11,})// Define the context for the Redis operationctx := context.Background()startTime := time.Now()// Define the key for the sorted setkey := "mySortedSet|test1"// Prepare the members to be added to the sorted setmembers := make([]redis.Z, 1000000)rand.Seed(time.Now().UnixNano())for i := 0; i < len(members); i++ {// Generate a random score and member for each element in the setrandomNumber := rand.Intn(50)members[i] = redis.Z{Score: float64(randomNumber), Member: fmt.Sprintf("member%d", i)}}// Call the BatchZAdd function to batch insert the membersif err := BatchZAdd(ctx, rdb, key, members); err != nil {log.Fatalf("failed to batch insert members into sorted set: %v", err)}fmt.Println("mySortedSet|test1写入序列执行时间:", time.Since(startTime).Seconds(), "s")// 写入第二个序列startTime = time.Now()key2 := "mySortedSet|test2"members2 := make([]redis.Z, 1000000)for i := 0; i < len(members2); i++ {// Generate a random score and member for each element in the setrandomNumber := rand.Intn(50)members2[i] = redis.Z{Score: float64(randomNumber), Member: fmt.Sprintf("member%d", i)}}// Call the BatchZAdd function to batch insert the membersif err := BatchZAdd(ctx, rdb, key2, members2); err != nil {log.Fatalf("failed to batch insert members into sorted set: %v", err)}fmt.Println("mySortedSet|test2写入序列执行时间:", time.Since(startTime).Seconds(), "s")log.Println("Members successfully added to the sorted set")destinationKey := "mySortedSet|destination"cmd := rdb.ZInterStore(ctx, destinationKey, &redis.ZStore{Keys:      []string{key, key2},Aggregate: "MIN",})if cmd.Err() != nil {log.Fatalf("failed to create intersection of sorted sets: %v", cmd.Err())}startTime = time.Now()nums := int64(0)var err1 errortimeout := time.After(10 * time.Second) // 设置超时为5秒ticker := time.NewTicker(10 * time.Millisecond)defer ticker.Stop()for {select {case <-timeout:fmt.Println("Timeout reached, exiting loop.", time.Now())returncase <-ticker.C:cmd = rdb.ZCard(ctx, destinationKey)nums, err1 = cmd.Result()if err1 != nil {fmt.Println("Error getting ZCard:", err1)return // 或者其他错误处理逻辑}fmt.Println("*********************nums:*************", nums)if nums != 0 {fmt.Println("执行时间:", time.Since(startTime).Seconds(), "s")log.Println("Members successfully ZInterStore the sorted set")return // 退出循环}}}
}

 

这种写法和测试程序中的方法相比,100万一下的数据,时间稍微长了一点。但是,测试程序中的方法在100万的数据写入时就会报错了,但是,图中的方法不会报错。

写入时长(单位:s)求交集时长
5千0.020.014
1万0.030.026
10万0.190.012
100万5.220.015
1000万

 

我们发现,求交集的时间都还好。但是,写入时长是呈线性增长,实际执行1000万数据写入,报了超时错误,也可以理解,毕竟时间呈线性增长,如果没有超时限制,应该也需要一分钟左右。因为其写入时间复杂度是O(log(N)) for each item added, where N is the number of elements in the sorted set。而求交集的时间复杂度是O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.

思考

出了上文使用管道(Pipeline)的方式,还有更优的写入方案么?最先冒出来的想法是并发写入,但是,我想到了一个问题,由于有序集合涉及到排序,并发写,是否会有锁竞争的问题?甚至排序会出现问题?我们可以实际测试一下看看结果如何。

func ConcurrentBatchZAdd(ctx context.Context, rdb *redis.Client, key string, members []redis.Z, numGoroutines int) error {var wg sync.WaitGroupbatchSize := len(members) / numGoroutinesfor i := 0; i < numGoroutines; i++ {start := i * batchSizeend := start + batchSizeif i == numGoroutines-1 {end = len(members) // 最后一个 goroutine 处理剩余的成员}wg.Add(1)go func(members []redis.Z) {defer wg.Done()pipe := rdb.Pipeline()cmd := pipe.ZAdd(ctx, key, members...)_, err := pipe.Exec(ctx)if err != nil {log.Printf("failed to execute pipeline: %v", err)}if err := cmd.Err(); err != nil {log.Printf("failed to batch insert members into sorted set: %v", err)}}(members[start:end])}wg.Wait() // 等待所有 goroutine 完成return nil
}

10万及以下的数据,似乎效率没有差异。到了100万的情况,似乎有了差异,并发写100万的时长是2.3s。1000万的数据,并发写入时会报超时错误,按照我的理解,并发写入其实并不能提升写入效率。

相关文章:

redis有序集合写入和求交集的速度

背景 团队小伙伴做了一个需求。大概的需求是有很多的图片作品&#xff0c;图片作品有一些类别&#xff0c;每个人进入到每个类别的作品业&#xff0c;根据权重优先查看权重最高的的作品&#xff0c;权重大概是基于每个人对该作品的浏览计算&#xff0c;浏览过的作品放在最后展…...

微服务之服务注册与发现:Etcd、Zookeeper、Consul 与 Nacos 比较

在微服务架构中&#xff0c;服务注册与发现是实现服务动态管理和负载均衡的关键。本文将对四款主流的服务注册与发现工具——Etcd、Zookeeper、Consul、Nacos进行深入对比&#xff0c;从功能、性能、一致性、生态集成、应用场景等多个维度展开分析&#xff0c;帮助您选择最适合…...

桥接模式详解和分析JDBC中的应用

&#x1f3af; 设计模式专栏&#xff0c;持续更新中&#xff0c; 欢迎订阅&#xff1a;JAVA实现设计模式 &#x1f6e0;️ 希望小伙伴们一键三连&#xff0c;有问题私信都会回复&#xff0c;或者在评论区直接发言 桥接模式 文章目录 桥接模式桥接模式的四个核心组成&#xff1a…...

【python - 函数】

一、交互式会话 在与 Python 的交互式会话中&#xff0c;你可以在提示符 >>> 后键入一些 Python 代码&#xff0c;Python 解释器会读取并执行你键入的各种命令。 要启动交互式会话&#xff0c;请在终端 (Mac/Unix/Linux) 中键入 python3 或在 Windows 中打开 Python…...

scipy中稀疏矩阵特征值问题概述

在Python的scipy库中&#xff0c;这三种算法——ARPACK、LOBPCG、和AMG——都是用于求解稀疏矩阵特征值问题的数值方法。它们各自有不同的特性和适用场景&#xff0c;以下是详细说明&#xff1a; 1. ARPACK (Arnoldi Package) ARPACK&#xff08;Arnoldi Package&#xff09;…...

浅谈线性表——队列

文章目录 一、什么是队列&#xff1f;二、队列底层三、自我实现一个队列3.1、链式存储3.1.1、单向链表实现队列的实现代码3.1.2、双向链表实现队列的实现代码 3.2、顺序存储3.2.1、循环队列的实现代码 一、什么是队列&#xff1f; 队列是只允许在一端进行插入数据操作&#xf…...

2-94 基于matlab的最佳维纳滤波器的盲解卷积算法

基于matlab的最佳维纳滤波器的盲解卷积算法。维纳滤波将地震子波转换为任意所需要的形态。维纳滤波不同于反滤波&#xff0c;它是在最小平方的意义上为最 佳。基于最佳纳滤波理论的滤波器算法是莱文逊(Wiener—Levinson)算法。程序提供了4种子波和4种期望输出&#xff1a;零延迟…...

【提示词】浅谈GPT等大模型中的Prompt

Prompt是人工智能&#xff08;AI&#xff09;提示词&#xff0c;是一种利用自然语言来指导或激发人工智能模型完成特定任务的方法。在AI语境中&#xff0c;Prompt是一种自然语言输入&#xff0c;通常指的是向模型提出的一个请求或问题&#xff0c;这个请求或问题的形式和内容会…...

最强AI照片说话Windows一体包下载地址,口型合成音频驱动图片,免安装,下载即用

照片数字一键整合包&#xff1a;点击下载 一键安装包&#xff0c;简单一键启动&#xff0c;即刻使用&#xff0c;秒级体验。 目前效果最好的音频驱动图片说话的软件&#xff0c;比sadtalker、MuseTalk更清晰&#xff0c;效果更好&#xff0c;可以作为DID heygen的开源平替。原…...

Windows下使用cmake编译OpenCV

Windows下使用cmake编译OpenCV cmake下载OpenCV下载编译OpenCV cmake下载 下载地址&#xff1a;https://cmake.org/download/ 下载完成&#xff0c;点击选择路径安装即可 OpenCV下载 下载地址&#xff1a;https://github.com/opencv/opencv/releases/tag/4.8.1因为我们是编译…...

设计模式---中介者模式

设计模式---中介者模式 定义与设计思路中介者模式的引入&#xff1a;机场控制塔中介者模式的设计框架 定义与设计思路 定义&#xff1a;用一个中介对象来封装一系列对象交互。中介者使各对象不需要相互引用&#xff0c;从而使其耦合松散&#xff0c;而且可以独立地改变它们之间…...

六氟化硫密度微水在线监测配套5孔M12格兰头航空插头插座

我们将为大家介绍如何使用六氟化硫密度微水在线监测配套5孔M12格兰头连接器。在本教程中&#xff0c;我们将向您展示简单易懂的步骤&#xff0c;让您轻松掌握。 所需材料&#xff1a; 1. 六氟化硫密度微水在线监测器 2. 5孔M12格兰头连接器 3. 电源线 4. 符合要求的电缆 5…...

linux -L4.linux 暂停和启动进程

接第3课&#xff0c;L3 第3课-查看进程 通过端口号&#xff0c;查看对应的进程 netstat -tulnp | grep :9513暂停这个进程 Kill -STOP 5376重启这个进程 Kill -CONT 5376要查看特定PID对应的端口&#xff0c;你可以使用netstat命令结合grep工具来过滤输出。以下是一个基于L…...

Java多线程编程-基础篇

多线程相关的概念 并发 并发是指在同一时间段内&#xff0c;两个或多个任务在同一个处理器上交替执行&#xff0c;使得在宏观上看起来像是同时进行。并发是通过快速切换任务来模拟同时执行的效果&#xff0c;实际上在任何一个时刻点上只有一个任务在执行。 也就是说&#xff0…...

【极限、数学】 NOIP 2018 提高组初赛试题 第 7 题详解(线段长度期望)

在一条长度为 1 1 1 的线段上随机取两个点&#xff0c;则以这两个点为端点的线段的期望长度是&#xff08; &#xff09;。 考虑将一个线段上平均分布有 n ( n ≥ 2 ) n(n\geq 2) n(n≥2) 个节点&#xff0c;其中首尾均有一个节点&#xff0c;那么我们就将一个线段均分为 n…...

《论网络安全体系设计》写作框架,软考高级系统架构设计师

论文真题 随着社会信息化的普及&#xff0c;计算机网络已经在各行各业得到了广泛的应用。目前&#xff0c;绝大多数业务处理几乎完全依赖计算机和网络执行&#xff0c;各种重要数据如政府文件、工资档案、财务账目和人事档案等均依赖计算机和网络进行存储与传输。另一方面&…...

这款开源的通用PDF处理神器,功能炸裂!

今天分享一款以PDF为中心的多功能办公学习工具箱软件&#xff0c;包含四大板块功能&#xff1a;PDF实用工具箱、Anki制卡神器、Anki最强辅助、视频笔记神器&#xff0c;软件功能众多且强大&#xff0c;熟练运用可以大幅提高办公和学习效率&#xff0c;绝对是您不可多得的效率神…...

RabbitMQ延迟消息——DelayExchange插件

什么是死信以及死信交换机 当一个队列中的消息满足下列情况之一时&#xff0c;可以成为死信&#xff1a; 1. 消费者使用basic.reject或 basic.nack声明消费失败&#xff0c;并且消息的requeue参数设置为false 2. 消息是一个过期消息&#xff0c;超时无人消费 3. 要投递的队列消…...

【系统规划与管理师】【案例分析】【考点】【答案篇】第5章 IT服务部署实施

【问题篇】☞【系统规划与管理师】【案例分析】【考点】【问题篇】第5章 IT服务部署实施 【移动端浏览】☞【系统规划与管理师】【案例分析】【模拟考题】章节考题汇总&#xff08;第5章&#xff09;&#xff08;答案篇&#xff09;&#xff08;共24个知识点&#xff09; 第5章…...

华为云服务器的数据库部署及管理

不管是终端数据上报到服务器进行存储&#xff0c;还是客户端的动态请求都需要用到数据库&#xff0c;因此这里对数据库的使用进行了一些记录&#xff0c;租用的是华为云的ECS弹性服务器&#xff08;Ubuntu18&#xff09;。下面以网页登录的账号信息Acount为例。 一、Mysql的安装…...

C#【必备技能篇】替换一个字节(byte)中连续几位(bit)的内容

文章目录 一、一个示例二、通用方法 一、一个示例 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace ConsoleApp1 {class Program{static void Main(string[] args){Method1();}public static…...

roboguide将tp程序转化为LS文本格式的方法

不同的软件版本可能操作不同&#xff0c;但是仍然可以参考文章中的办法。 我使用的版本如图所示&#xff1a; 1.首先&#xff0c;打开任意一个工程&#xff0c;如果没有&#xff0c;可以打开自带的示例。 如图&#xff0c;我打开了自带的示例&#xff0c;在帮助文档中可以找到…...

基于SpringBoot+Vue+MySQL的流浪猫狗宠物救助救援网站管理系统

系统展示 用户前台界面 管理员后台界面 系统背景 在当今社会&#xff0c;随着宠物数量的激增及人们关爱动物意识的提升&#xff0c;流浪猫狗问题日益严峻。为解决这一问题&#xff0c;构建一套高效、便捷的流浪猫狗宠物救助救援网站管理系统显得尤为重要。本系统基于SpringBoot…...

I/O 多路复用:`select`、`poll`、`epoll` 和 `kqueue` 的区别与示例

I/O 多路复用是指在一个线程内同时监控多个文件描述符&#xff08;File Descriptor, FD&#xff09;&#xff0c;以便高效地处理多个 I/O 事件。在 UNIX/Linux 和 BSD 系统中&#xff0c;select、poll、epoll、kqueue 都是实现 I/O 多路复用的系统调用。它们各有特点&#xff0…...

大数据之Flink(三)

9.3、转换算子 9.3.1、基本转换算子 9.3.1.1、映射map 一一映射 package transform;import bean.WaterSensor; import org.apache.flink.streaming.api.datastream.DataStreamSource; import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator; impor…...

【HCIA-Datacom】IPv4地址介绍

| | &#x1f449;个人主页&#xff1a;Reuuse 希望各位多多支持&#xff01;❀ | &#x1f449;HCIA专栏博客 | 最后如果对你们有帮助的话希望有一个大大的赞&#xff01; | ⭐你们的支持是我最大的动力&#xff01;⭐ | 目录 IPv4地址定义IPv4地址分类方式二级目录三级目录 I…...

maven父子工程多模块如何管理统一的版本号?

1.为什么要统一管理&#xff1f; maven父子工程多模块&#xff0c;每个模块还都可以独立存在&#xff0c;子模块往往通常希望和父工程保持一样的版本&#xff0c;如果每个工程单独定义版本号&#xff0c;后期变更打包也非常麻烦&#xff0c;如何维护一个全局的版本号呢&#x…...

JavaScript --函数的作用域(全局和局部)

全局作用域 全局作用域&#xff0c;就算不在一个script标签也能调用 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta nam…...

贪吃蛇项目实现(C语言)——附源码

前言 贪吃蛇是一款十分经典的游戏&#xff0c;其通过控制贪吃蛇的上下左右移动来吃食物&#xff0c;延长自己的身体&#xff0c;也会因为撞到墙体和自身而死亡。下面我们通过C语言来实现贪吃蛇。 1.技术要点 C语言枚举&#xff0c;结构体&#xff0c;链表&#xff0c;动态内…...

【C++】42道面试经典问题总结

C this指针是干什么用的&#xff1f; 假如一个类型定义了很多对象&#xff0c;类里面有很多定义的私有成员变量&#xff0c;共享一套成员方法。通过this指针这可以区分方法、变量是操作的哪个对象的。 C的new和delete&#xff0c;new[]和delete[]可以混用吗&#xff1f; 一般来…...