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.02 | 0.014 |
| 1万 | 0.03 | 0.026 |
| 10万 | 0.19 | 0.012 |
| 100万 | 5.22 | 0.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有序集合写入和求交集的速度
背景 团队小伙伴做了一个需求。大概的需求是有很多的图片作品,图片作品有一些类别,每个人进入到每个类别的作品业,根据权重优先查看权重最高的的作品,权重大概是基于每个人对该作品的浏览计算,浏览过的作品放在最后展…...
微服务之服务注册与发现:Etcd、Zookeeper、Consul 与 Nacos 比较
在微服务架构中,服务注册与发现是实现服务动态管理和负载均衡的关键。本文将对四款主流的服务注册与发现工具——Etcd、Zookeeper、Consul、Nacos进行深入对比,从功能、性能、一致性、生态集成、应用场景等多个维度展开分析,帮助您选择最适合…...
桥接模式详解和分析JDBC中的应用
🎯 设计模式专栏,持续更新中, 欢迎订阅:JAVA实现设计模式 🛠️ 希望小伙伴们一键三连,有问题私信都会回复,或者在评论区直接发言 桥接模式 文章目录 桥接模式桥接模式的四个核心组成:…...
【python - 函数】
一、交互式会话 在与 Python 的交互式会话中,你可以在提示符 >>> 后键入一些 Python 代码,Python 解释器会读取并执行你键入的各种命令。 要启动交互式会话,请在终端 (Mac/Unix/Linux) 中键入 python3 或在 Windows 中打开 Python…...
scipy中稀疏矩阵特征值问题概述
在Python的scipy库中,这三种算法——ARPACK、LOBPCG、和AMG——都是用于求解稀疏矩阵特征值问题的数值方法。它们各自有不同的特性和适用场景,以下是详细说明: 1. ARPACK (Arnoldi Package) ARPACK(Arnoldi Package)…...
浅谈线性表——队列
文章目录 一、什么是队列?二、队列底层三、自我实现一个队列3.1、链式存储3.1.1、单向链表实现队列的实现代码3.1.2、双向链表实现队列的实现代码 3.2、顺序存储3.2.1、循环队列的实现代码 一、什么是队列? 队列是只允许在一端进行插入数据操作…...
2-94 基于matlab的最佳维纳滤波器的盲解卷积算法
基于matlab的最佳维纳滤波器的盲解卷积算法。维纳滤波将地震子波转换为任意所需要的形态。维纳滤波不同于反滤波,它是在最小平方的意义上为最 佳。基于最佳纳滤波理论的滤波器算法是莱文逊(Wiener—Levinson)算法。程序提供了4种子波和4种期望输出:零延迟…...
【提示词】浅谈GPT等大模型中的Prompt
Prompt是人工智能(AI)提示词,是一种利用自然语言来指导或激发人工智能模型完成特定任务的方法。在AI语境中,Prompt是一种自然语言输入,通常指的是向模型提出的一个请求或问题,这个请求或问题的形式和内容会…...
最强AI照片说话Windows一体包下载地址,口型合成音频驱动图片,免安装,下载即用
照片数字一键整合包:点击下载 一键安装包,简单一键启动,即刻使用,秒级体验。 目前效果最好的音频驱动图片说话的软件,比sadtalker、MuseTalk更清晰,效果更好,可以作为DID heygen的开源平替。原…...
Windows下使用cmake编译OpenCV
Windows下使用cmake编译OpenCV cmake下载OpenCV下载编译OpenCV cmake下载 下载地址:https://cmake.org/download/ 下载完成,点击选择路径安装即可 OpenCV下载 下载地址:https://github.com/opencv/opencv/releases/tag/4.8.1因为我们是编译…...
设计模式---中介者模式
设计模式---中介者模式 定义与设计思路中介者模式的引入:机场控制塔中介者模式的设计框架 定义与设计思路 定义:用一个中介对象来封装一系列对象交互。中介者使各对象不需要相互引用,从而使其耦合松散,而且可以独立地改变它们之间…...
六氟化硫密度微水在线监测配套5孔M12格兰头航空插头插座
我们将为大家介绍如何使用六氟化硫密度微水在线监测配套5孔M12格兰头连接器。在本教程中,我们将向您展示简单易懂的步骤,让您轻松掌握。 所需材料: 1. 六氟化硫密度微水在线监测器 2. 5孔M12格兰头连接器 3. 电源线 4. 符合要求的电缆 5…...
linux -L4.linux 暂停和启动进程
接第3课,L3 第3课-查看进程 通过端口号,查看对应的进程 netstat -tulnp | grep :9513暂停这个进程 Kill -STOP 5376重启这个进程 Kill -CONT 5376要查看特定PID对应的端口,你可以使用netstat命令结合grep工具来过滤输出。以下是一个基于L…...
Java多线程编程-基础篇
多线程相关的概念 并发 并发是指在同一时间段内,两个或多个任务在同一个处理器上交替执行,使得在宏观上看起来像是同时进行。并发是通过快速切换任务来模拟同时执行的效果,实际上在任何一个时刻点上只有一个任务在执行。 也就是说࿰…...
【极限、数学】 NOIP 2018 提高组初赛试题 第 7 题详解(线段长度期望)
在一条长度为 1 1 1 的线段上随机取两个点,则以这两个点为端点的线段的期望长度是( )。 考虑将一个线段上平均分布有 n ( n ≥ 2 ) n(n\geq 2) n(n≥2) 个节点,其中首尾均有一个节点,那么我们就将一个线段均分为 n…...
《论网络安全体系设计》写作框架,软考高级系统架构设计师
论文真题 随着社会信息化的普及,计算机网络已经在各行各业得到了广泛的应用。目前,绝大多数业务处理几乎完全依赖计算机和网络执行,各种重要数据如政府文件、工资档案、财务账目和人事档案等均依赖计算机和网络进行存储与传输。另一方面&…...
这款开源的通用PDF处理神器,功能炸裂!
今天分享一款以PDF为中心的多功能办公学习工具箱软件,包含四大板块功能:PDF实用工具箱、Anki制卡神器、Anki最强辅助、视频笔记神器,软件功能众多且强大,熟练运用可以大幅提高办公和学习效率,绝对是您不可多得的效率神…...
RabbitMQ延迟消息——DelayExchange插件
什么是死信以及死信交换机 当一个队列中的消息满足下列情况之一时,可以成为死信: 1. 消费者使用basic.reject或 basic.nack声明消费失败,并且消息的requeue参数设置为false 2. 消息是一个过期消息,超时无人消费 3. 要投递的队列消…...
【系统规划与管理师】【案例分析】【考点】【答案篇】第5章 IT服务部署实施
【问题篇】☞【系统规划与管理师】【案例分析】【考点】【问题篇】第5章 IT服务部署实施 【移动端浏览】☞【系统规划与管理师】【案例分析】【模拟考题】章节考题汇总(第5章)(答案篇)(共24个知识点) 第5章…...
华为云服务器的数据库部署及管理
不管是终端数据上报到服务器进行存储,还是客户端的动态请求都需要用到数据库,因此这里对数据库的使用进行了一些记录,租用的是华为云的ECS弹性服务器(Ubuntu18)。下面以网页登录的账号信息Acount为例。 一、Mysql的安装…...
微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...
在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...
Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...
人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
[论文阅读]TrustRAG: Enhancing Robustness and Trustworthiness in RAG
TrustRAG: Enhancing Robustness and Trustworthiness in RAG [2501.00879] TrustRAG: Enhancing Robustness and Trustworthiness in Retrieval-Augmented Generation 代码:HuichiZhou/TrustRAG: Code for "TrustRAG: Enhancing Robustness and Trustworthin…...
Linux中《基础IO》详细介绍
目录 理解"文件"狭义理解广义理解文件操作的归类认知系统角度文件类别 回顾C文件接口打开文件写文件读文件稍作修改,实现简单cat命令 输出信息到显示器,你有哪些方法stdin & stdout & stderr打开文件的方式 系统⽂件I/O⼀种传递标志位…...
