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

news2024/11/18 3:45:31

背景

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

测试程序

package main

import (
	"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 commands
	pipe := 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 client
	rdb := redis.NewClient(&redis.Options{
		Addr:     "10.10.37.100:6379", // Replace with your Redis server address
		Password: "dreame@2020",
		DB:       11,
	})

	// Define the context for the Redis operation
	ctx := context.Background()

	startTime := time.Now()
	// Define the key for the sorted set
	key := "mySortedSet|test1"
	// Prepare the members to be added to the sorted set
	members := 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 set
		randomNumber := rand.Intn(50)
		members[i] = redis.Z{Score: float64(randomNumber), Member: fmt.Sprintf("member%d", i)}
	}

	// Call the BatchZAdd function to batch insert the members
	if 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 set
		randomNumber := rand.Intn(50)
		members2[i] = redis.Z{Score: float64(randomNumber), Member: fmt.Sprintf("member%d", i)}
	}

	// Call the BatchZAdd function to batch insert the members
	if 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 error
	timeout := 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())
			return
		case <-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.WaitGroup
	batchSize := len(members) / numGoroutines

	for i := 0; i < numGoroutines; i++ {
		start := i * batchSize
		end := start + batchSize
		if 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万的数据,并发写入时会报超时错误,按照我的理解,并发写入其实并不能提升写入效率。

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

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

相关文章

IDEA 通义灵码 插件使用体验

目录 前言 主要功能 演示代码 解释代码 生成单元测试 生成代码注释 生成优化建议 代码片段补全 总结 前言 自从 AI 技术开始大规模应用&#xff0c;老板就想让下面的牛马借助 AI 工具来提高编码效率&#xff0c;由于团队都没有在实际编码中深度使用过 AI 工具&#x…

消息中间件有哪些常见类型

消息中间件根据其设计理念和用途&#xff0c;可以大致分为以下几种常见类型&#xff1a; 点对点消息队列&#xff08;Point-to-Point Messaging Queues&#xff09;&#xff1a; 在这种模型中&#xff0c;消息被发送到特定的队列中&#xff0c;消费者从队列中取出并处理消息。队…

【LeetCode每日一题】——LCR 078.合并 K 个升序链表

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目注意】六【题目示例】七【题目提示】八【解题思路】九【时间频度】十【代码实现】十一【提交结果】 一【题目类别】 优先队列 二【题目难度】 困难 三【题目编号】 LCR 078.合并 K 个升序链表 …

Linux入门——“Linux基本指令”上

在刚开始学习Linux时&#xff0c;首先需要掌握一些基本指令&#xff0c;以便我们能更好地使用Linux操作系统&#xff0c;以下指令在Ubuntu 22上执行。以下内容不过多介绍选项内容。 1.ls指令 ls指令用来查看当前目录下的文件&#xff0c;显示的信息是很有限的一般只显示文件名&…

二进制部署ETCD单机版

文章目录 一、签发etcd证书二、搭建etcd单机版三、测试ETCD服务 一、签发etcd证书 注意&#xff1a;在操作签发证书操作时一定要检查服务器时间、时区是否一致&#xff0c;会导致证书不可用&#xff01;&#xff01; 1、创建etcd目录 mkdir /etc/etcd/{ssl,data} -p2、安装签…

【vue-media-upload】一个好用的上传图片的组件,注意事项

一、问题 media 的saved 数组中的图片使用的是location 相对路径&#xff0c;但是我的业务需要直接根据图片链接展示图片&#xff0c;而且用的也不是location 相关源代码 <div v-for"(image, index) in savedMedia" :key"index" class"mu-image-…

基于SpringBoot的社区宠物管理与推荐系统的设计与实现

文未可获取一份本项目的java源码和数据库参考。 1.课题的基本内容&#xff0c;可能遇到的困难&#xff0c;提出解决问题的方法和措施 2.1课题的基本内容 本课题主要研究基于SpringBoot的社区宠物管理与推荐系统的设计与实现。用户注册登录系统前端后可以可以实现对宠物信息的…

【读书笔记-《30天自制操作系统》-20】Day21

本篇的内容主要是操作系统的保护&#xff0c;涉及到x86 CPU的一些机制&#xff0c;以及操作系统的异常处理。 1. 字符显示API问题解决 首先来解决一下上一篇内容中字符串显示API没有生效的问题。 void hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ec…

为什么H.266未能普及?EasyCVR视频编码技术如何填补市场空白

H.266&#xff0c;也被称为Versatile Video Coding&#xff08;VVC&#xff09;&#xff0c;是近年来由MPEG&#xff08;Moving Picture Experts Group&#xff09;和ITU&#xff08;International Telecommunication Union&#xff09;联合开发并发布的新一代国际视频编码标准…

[每周一更]-(第114期):介绍GitLab不同角色对应的权限

文章目录 GitLab 角色及其权限项目级别角色组级别角色 使用场景示例 工作中一直使用Gitlab搭建了公司内网的代码管理工具&#xff0c;但是不同的用户会分配相应的权限&#xff0c;来管理不同用户及角色的权限信息&#xff0c;我们来介绍下角色的信息&#xff0c;方便我们管理公…

演示:基于WPF的自绘的中国地铁轨道控件

一、目的&#xff1a;演示一个基于WPF的自绘的中国地铁轨道控件 二、效果演示 北京地铁 成都地铁 上海地铁 深圳地铁 南京地铁 长春地铁 哈尔滨地铁 武汉地铁 厦门地铁 香港地铁 三、功能 支持平移、缩放等操作 鼠标悬停显示线路信息和站点信息 按表格显示&#xff0c;按纸张…

传知代码-融合经典与创新的图像分类新途径

代码以及视频讲解 本文所涉及所有资源均在传知代码平台可获取 概述 在当前的深度学习领域&#xff0c;构建兼具高性能与灵活性的卷积神经网络&#xff08;CNN&#xff09;已成为计算机视觉研究的核心课题。本文介绍了一种全新的卷积神经网络架构&#xff0c;该网络巧妙地结合…

MacOS Sonoma(14.x) 大写模式或中文输入法下的英文模式,光标下方永远会出现的CapsLock箭头Icon的去除办法

如图&#xff0c;MacOS Sonoma(14.x) 大写模式或中文输入法下的英文模式下&#xff0c;光标下方永远会出现一个CapsLock箭头Icon。此Icon挡住视野&#xff0c;还容易误触导致切换大小写状态&#xff0c;带来的收益远远小于带来的困扰。 解决办法 打开终端&#xff0c;输入以下…

Go协程及并发锁应用指南

概念 协程&#xff08;Goroutine&#xff09;是Go语言独有的并发体&#xff0c;是一种轻量级的线程&#xff0c;也被称为用户态线程。相对于传统的多线程编程&#xff0c;协程的优点在于更加轻量级&#xff0c;占用系统资源更少&#xff0c;切换上下文的速度更快&#xff0c;不…

Vue:使用v-model绑定的textarea在光标处插入指定文本

一、问题描述 使用v-model绑定的textarea如果需要改变其内容&#xff0c;一般只要改变v-model对应的变量即可&#xff0c;但如果需要在textarea的当前光标位置插入指定文本&#xff0c;那就需要操作DOM了。于是我们写了一段js&#xff1a; const insertTextAtCursor (text) …

聊天组件 Vue3-beautiful-chat

前言 最近很多公司都在搞大模型&#xff0c;类似于 chatgpt 的功能&#xff1b;而 chatgpt 的界面其实就是个对话框。今天就介绍一个不错的对话框组件 Vue3-beautiful-chat 项目框架 vite vue3 TS Vue3-beautiful-chat 使用流程 1、引用三方件 npm install Vue3-beaut…

【大模型专栏—进阶篇】语言模型创新大总结——“三派纷争”

大模型专栏介绍 &#x1f60a;你好&#xff0c;我是小航&#xff0c;一个正在变秃、变强的文艺倾年。 &#x1f514;本文为大模型专栏子篇&#xff0c;大模型专栏将持续更新&#xff0c;主要讲解大模型从入门到实战打怪升级。如有兴趣&#xff0c;欢迎您的阅读。 &#x1f4…

ChatGPT对话训练数据采集渠道有哪些

ChatGPT是人工智能技术驱动的自然语言处理工具&#xff0c;它可以生成逼真的自然语言回复&#xff0c;被广泛应用于聊天机器人、智能助理等领域。ChatGPT本身需要依赖大量的训练对话数据和算法运行&#xff0c;其所依赖的对话数据&#xff0c;需要专业的数据采集标注处理流程才…

20 递归算法精髓解析:基准、性质、案例(阶乘、斐波拉契、猴子吃桃、汉诺塔等)、与循环的对比

目录 1 概述 2 递归的基本组成部分 2.1 基准情况 2.2 递归步骤 2.3 案例&#xff1a;循环实现阶乘的计算 2.4 案例&#xff1a;递归函数实现阶乘的计算 3 递归的性质 3.1 自我调用 3.2 栈的使用 3.3 问题分解 3.4 性能考虑 3.5 案例&#xff1a;递归的回溯 4 综合…

WPF DataGrid 列表中,DataGrid.Columns 列根据不同的值显示不同内容

需求&#xff1a;在WPF DataGrid 控件中&#xff0c;有以下列&#xff0c;绑定了一个LogType&#xff0c;值分别是0,1,2&#xff0c;根据不同的值&#xff0c;显示不同的内容以及背景 <DataGrid ItemsSource"{Binding EventLog}"><DataGrid.Columns><…