redis基本数据结构-string

news/2024/9/18 9:21:08/ 标签: redis, 数据结构, go

文章目录

    • 1. redis的string数据结构
    • 2. 常见的业务场景
      • 2.1 缓存功能
        • 案例讲解
          • 背景
          • 优势
          • 解决方案
          • 代码实现
      • 2.2 计数器
        • 案例讲解
          • 背景
          • 优势
          • 解决方案
          • 代码实现
      • 2.3 分布式锁
        • 案例讲解
          • 背景
          • 优势
          • 解决方案
          • 代码实现
      • 2.4 限流
        • 案例讲解
          • 背景
          • 优势
          • 解决方案
          • 代码实现
      • 2.5 共享session
        • 案例讲解
          • 背景
          • 优势
          • 解决方案
          • 代码实现

redisstring_1">1. redis的string数据结构

参考链接:https://mp.weixin.qq.com/s/srkd73bS2n3mjIADLVg72A
redis 的 string 数据结构redis 中最基本的数据类型,它可以存储任何形式的数据,最大可以存储 512MB 的字符串。string 类型可以存储文本、数字和二进制数据等。
具有以下特性:

  1. 简单性:String 是最简单的类型,可以用来存储简单的键值对。
  2. 多样性:可以存储任意类型的数据,包括文本、数字、甚至是序列化后的对象。
  3. 原子性:对 String 的操作是原子性的,即在并发情况下操作串的安全性。

以下是一些与 String 相关的常用 Redis 命令:

  • SET key value:设置指定 key 的值。
  • GET key:获取指定 key 的值。
  • DEL key:删除指定 key。
  • EXISTS key:检查指定 key 是否存在。
  • INCR key:将 key 的值加 1。
  • DECR key:将 key 的值减 1。
  • MSET key1 value1 key2 value2 …:同时设置多个 key-value 对。
  • MGET key1 key2 …:同时获取多个 key 的值。
  • SETEX key seconds value:设置 key 的值,同时设置过期时间(以秒为单位)。
xxxxxx:6379> SET person:1 '{"name": "Alice", "age": 30, "city": "New York"}'
OKxxxxxx:6379> get person:1
"{\"name\": \"Alice\", \"age\": 30, \"city\": \"New York\"}"xxxxxx:6379> exists person:1
(integer) 1xxxxxx:6379> exists person:2
(integer) 0xxxxxx:6379> set art:122 0
OKxxxxxx:6379> incr art:122
(integer) 1xxxxxx:6379> get art:122
"1"xxxxxx:6379> decr art:122
(integer) 0xxxxxx:6379> get art:122
"0"xxxxxx:6379> mget person:1 art:122
1) "{\"name\": \"Alice\", \"age\": 30, \"city\": \"New York\"}"
2) "0"xxxxxx:6379> SETEX config:site 600 '{"theme": "dark", "language": "en"}'
OKxxxxxx:6379> mget person:1 art:122 config:site
1) "{\"name\": \"Alice\", \"age\": 30, \"city\": \"New York\"}"
2) "0"
3) "{\"theme\": \"dark\", \"language\": \"en\"}"

2. 常见的业务场景

2.1 缓存功能

string类型常用于缓存经常访问的数据,如数据库查询结果、网页内容等,以提高访问速度和降低数据库的压力。一般多是用于读多写少的场景。比如,商品的价格,描述等信息,用户的资料等信息。

案例讲解
背景

在商品系统中,商品的详细信息如描述、价格、库存等数据通常不会频繁变动,但会被频繁查询。每次用户访问商品详情时,都直接从数据库查询这些信息会导致不必要的数据库负载。

优势
  1. 快速数据访问:Redis作为内存数据库,提供极速的读写能力,大幅降低数据访问延迟,提升用户体验。
  2. 减轻数据库压力:缓存频繁访问的静态数据,显著减少数据库查询,从而保护数据库资源,延长数据库寿命。
  3. 高并发支持:Redis设计用于高并发环境,能够处理大量用户同时访问,保证系统在流量高峰时的稳定性。
  4. 灵活的缓存策略:易于实现缓存数据的更新和失效,结合适当的缓存过期和数据同步机制,确保数据的实时性和一致性。
解决方案

使用Redis String类型来缓存商品的静态信息。当商品信息更新时,相应的缓存也更新或失效。
在这里插入图片描述

代码实现
go">package mainimport ("context""encoding/json""github.com/go-redis/redis/v8"
)var (rdb *redis.Client   // 为了表现代码的完整性,这里省略了初始化代码ctx context.Context // 为了表现代码的完整性,这里省略了初始化代码
)// generateProductCacheKey generates the cache key for the product.
func generateProductCacheKey(productID string) string {return "product:" + productID
}// cacheProductInfo caches the product information in Redis.
func cacheProductInfo(productID string, productInfo map[string]interface{}) {cacheKey := generateProductCacheKey(productID)// 序列化商品信息为JSON格式productJSON, _ := json.Marshal(productInfo)// 将序列化后的商品信息存储到Redis===> set product:apple {"name":"apple","price":100, "description":"a nice apple"}rdb.Set(ctx, cacheKey, string(productJSON), 0) // 0表示永不过期,实际使用时可以设置过期时间
}// getProductInfoFromCache gets the product information from Redis.
func getProductInfoFromCache(productID string) (map[string]interface{}, error) {cacheKey := generateProductCacheKey(productID)// 从Redis获取商品信息productJSON, err := rdb.Get(ctx, cacheKey).Result() // get product:appleif err != nil {return nil, err}if len(productJSON) == 0 {// 未在缓存中找到商品信息, 需要从数据库中获取, 同时更新缓存return nil, nil}// 反序列化JSON格式的商品信息var productInfo map[string]interface{}err = json.Unmarshal([]byte(productJSON), &productInfo)if err != nil {return nil, err}return productInfo, nil
}func updateProductInfoAndCache(productID string, newProductInfo map[string]interface{}) {// 更新数据库中的商品信息// 更新Redis缓存中的商品信息cacheProductInfo(productID, newProductInfo)
}

在日常实践中,感觉这里还是会有一点小问题:

  1. 如果是在商品信息插入,对于一个新商品可能缓存肯定是不存在的,如果某一个商城大量上新新产品,这个时候如果流量大量进来,是否会造成缓存击穿的情况?因此对于这种插入情况多基本没更新的时候,上面的流程可能有点问题。
  2. 在用户查询商品信息的时候,如果存在大量缓存失败的情况,也会导致数据库崩溃的情况,所以在日常实践中,如果在redis中查询不到,那可以直接返回。因此在写入的时候需要双写;且要异步数据同步。定义一个定时任务,每隔一段时间将数据库的信息增量同步到redis,一天全量同步一次商品信息插入的时候双写
go">package mainimport ("context""encoding/json""fmt""github.com/go-redis/redis/v8"
)var (rdb *redis.Client   // 为了表现代码的完整性,这里省略了初始化代码ctx context.Context // 为了表现代码的完整性,这里省略了初始化代码
)// generateProductCacheKey generates the cache key for the product.
func generateProductCacheKey(productID string) string {return "product:" + productID
}// cacheProductInfo caches the product information in Redis.
func cacheProductInfo(productID string, productInfo map[string]interface{}) {cacheKey := generateProductCacheKey(productID)// 序列化商品信息为JSON格式productJSON, _ := json.Marshal(productInfo)// 将序列化后的商品信息存储到Redis===> set product:apple {"name":"apple","price":100, "description":"a nice apple"}rdb.Set(ctx, cacheKey, string(productJSON), 0) // 0表示永不过期,实际使用时可以设置过期时间
}// getProductInfoFromCache gets the product information from Redis.
func getProductInfoFromCache(productID string) (map[string]interface{}, error) {cacheKey := generateProductCacheKey(productID)// 从Redis获取商品信息productJSON, err := rdb.Get(ctx, cacheKey).Result() // get product:appleif err != nil {return nil, err}// 反序列化JSON格式的商品信息var productInfo map[string]interface{}err = json.Unmarshal([]byte(productJSON), &productInfo)if err != nil {return nil, err}return productInfo, nil
}func updateProductInfoAndCache(productID string, newProductInfo map[string]interface{}) {// 三个步骤如果有任何一个步骤失败,都需要回滚// 更新数据库中的商品信息// 更新Redis缓存中的商品信息cacheProductInfo(productID, newProductInfo)// 查一遍缓存,确保缓存中的商品信息已经更新info, err := getProductInfoFromCache(productID)if err != nil {// 处理错误}// 处理infofmt.Printf("product info: %v\n", info)
}// 补充一个定时任务,可以使用crontab, 方便管理也可以使用airflow等工具

2.2 计数器

利用INCR和DECR命令,String类型可以作为计数器使用,适用于统计如网页访问量、商品库存数量等 。

案例讲解
背景

对于文章的浏览量的统计,每篇博客文章都有一个唯一的标识符(例如,文章ID)。每次文章被访问时,文章ID对应的浏览次数在Redis中递增。可以定期将浏览次数同步到数据库,用于历史数据分析。在这里插入图片描述

优势
  • 实时性:能够实时更新和获取文章的浏览次数。
  • 高性能:Redis的原子操作保证了高并发场景下的计数准确性。
解决方案

通过Redis实现对博客文章浏览次数的原子性递增和检索,以优化数据库访问并实时更新文章的浏览统计信息。

代码实现
go">package mainimport ("context""errors""fmt""github.com/go-redis/redis/v8""log""strconv"
)var (rdb *redis.Client   // 为了表现代码的完整性,这里省略了初始化代码ctx context.Context // 为了表现代码的完整性,这里省略了初始化代码
)// recordArticleView 记录文章浏览次数
func recordArticleView(articleID string) {res, err := rdb.Incr(ctx, articleID).Result()if err != nil {// 如果发生错误,记录错误日志log.Printf("Error incrementing view count for article %s: %v", articleID, err)return}// 可选:记录浏览次数到日志或进行其他业务处理fmt.Printf("Article %s has been viewed %d times\n", articleID, res)
}// getArticleViewCount 从Redis获取文章的浏览次数
func getArticleViewCount(articleID string) (int, error) {// 从Redis获取文章的浏览次数viewCount, err := rdb.Get(ctx, articleID).Result()if err != nil {if errors.Is(err, redis.Nil) {// 如果文章ID在Redis中不存在,可以认为浏览次数为0return 0, nil} else {// 如果发生错误,记录错误日志log.Printf("Error getting view count for article %s: %v", articleID, err)return 0, err}}// 将浏览次数从字符串转换为整数count, err := strconv.Atoi(viewCount)if err != nil {log.Printf("Error converting view count to integer for article %s: %v", articleID, err)return 0, err}return count, nil
}// renderArticlePage 渲染文章页面,并显示浏览次数
func renderArticlePage(articleID string) {// 在渲染文章页面之前,记录浏览次数recordArticleView(articleID)// 获取文章浏览次数viewCount, err := getArticleViewCount(articleID)if err != nil {// 处理错误,例如设置浏览次数为0或跳过错误viewCount = 0}log.Printf("Rendering article %s with view count %d\n", articleID, viewCount)
}

2.3 分布式锁

分布式锁:通过SETNX命令(仅当键不存在时设置值),String类型可以实现分布式锁,保证在分布式系统中的互斥访问 。

案例讲解
背景

在分布式系统中,如电商的秒杀活动或库存管理,需要确保同一时间只有一个进程或线程可以修改共享资源,以避免数据不一致的问题。
在这里插入图片描述

优势
  1. 互斥性:确保同一时间只有一个进程可以访问共享资源,防止数据竞争和冲突。
  2. 高可用性:分布式锁能够在节点故障或网络分区的情况下仍能正常工作,具备自动故障转移和恢复的能力。
  3. 可重入性:支持同一个进程或线程多次获取同一个锁,避免死锁的发生。
  4. 性能开销:相比于其他分布式协调服务,基于Redis的分布式锁实现简单且性能开销较小。
解决方案

使用Redis的SETNX命令实现分布式锁的获取和释放,通过Lua脚本确保释放锁时的原子性,并在执行业务逻辑前尝试获取锁,业务逻辑执行完毕后确保释放锁,从而保证在分布式系统中对共享资源的安全访问。

代码实现
go">package mainimport ("context""github.com/go-redis/redis/v8""log""strconv""time"
)var (rdb *redis.Client   // 为了表现代码的完整性,这里省略了初始化代码ctx context.Context // 为了表现代码的完整性,这里省略了初始化代码
)// 尝试获取分布式锁
func tryGetDistributedLock(lockKey string, val string, expireTime int) bool {// 使用SET命令结合NX和PX参数尝试获取锁// NX表示如果key不存在则可以设置成功// PX指定锁的超时时间(毫秒)// 这里的val是一个随机值,用于在释放锁时验证锁是否属于当前进程result, err := rdb.SetNX(ctx, lockKey, val, time.Duration(expireTime)*time.Millisecond).Result()if err != nil {// 记录错误,例如:日志记录log.Printf("Error trying to get distributed lock for key %s: %v", lockKey, err)return false}// 如果result为1,则表示获取锁成功,result为0表示锁已被其他进程持有return result
}// 释放分布式锁, 这里的val是一个随机值,用于在释放锁时验证锁是否属于当前进程
func releaseDistributedLock(lockKey string, val string) {// 使用Lua脚本来确保释放锁的操作是原子性的script := `if redis.call("get", KEYS[1]) == ARGV[1] thenreturn redis.call("del", KEYS[1])elsereturn 0end`// 执行Lua脚本result, err := rdb.Eval(ctx, script, []string{lockKey}, val).Result()if err != nil {// 记录错误log.Printf("Error releasing distributed lock for key %s: %v", lockKey, err)}// 如果result为1,则表示锁被成功释放,如果为0,则表示锁可能已经释放或不属于当前进程if result == int64(0) {log.Printf("Failed to release the lock, it might have been released by others or expired")}
}// 执行业务逻辑,使用分布式锁来保证业务逻辑的原子性
func executeBusinessLogic(lockKey string) {val := generateRandomValue()                    // 生成一个随机值,作为锁的值if tryGetDistributedLock(lockKey, val, 30000) { // 尝试获取锁,30秒超时defer releaseDistributedLock(lockKey, val) // 无论业务逻辑是否成功执行,都释放锁// 执行具体的业务逻辑// ...} else {// 未能获取锁,处理重试逻辑或返回错误// ...}
}
// generateRandomValue 生成一个随机值作为锁的唯一标识
func generateRandomValue() string {return strconv.FormatInt(time.Now().UnixNano(), 10)
}

2.4 限流

限流:使用EXPIRE命令,结合INCR操作,可以实现API的限流功能,防止系统被过度访问

案例讲解
背景

一个在线视频平台提供了一个API,用于获取视频的元数据。在高流量事件(如新电影发布)期间,这个API可能会收到大量并发请求,这可能导致后端服务压力过大,甚至崩溃。
在这里插入图片描述

优势
  1. 稳定性保障:通过限流,可以防止系统在高负载下崩溃,确保核心服务的稳定性。
  2. 服务公平性:限流可以保证不同用户和客户端在高并发环境下公平地使用服务。
  3. 防止滥用:限制API的调用频率,可以防止恶意用户或爬虫对服务进行滥用。
解决方案
  1. 请求计数:每次API请求时,使用INCR命令对特定的key进行递增操作。
  2. 设置过期时间:使用EXPIRE命令为计数key设置一个过期时间,过期时间取决于限流的时间窗口(例如1秒)。
  3. 检查请求频率:如果请求计数超过设定的阈值(例如每秒100次),则拒绝新的请求或进行排队。
代码实现
go">package mainimport ("context""github.com/go-redis/redis/v8""log""time"
)var (rdb *redis.Client   // 为了表现代码的完整性,这里省略了初始化代码ctx context.Context // 为了表现代码的完整性,这里省略了初始化代码
)// 伪代码:API限流器
func rateLimiter(apiKey string, threshold int, timeWindow int) bool {currentCount, err := rdb.Incr(ctx, apiKey).Result()if err != nil {log.Printf("Error incrementing API key %s: %v", apiKey, err)return false}// 如果当前计数超过阈值,则拒绝请求if int(currentCount) > threshold {return false}// 重置计数器的过期时间_, err = rdb.Expire(ctx, apiKey, time.Duration(timeWindow)).Result()if err != nil {log.Printf("Error resetting expire time for API key %s: %v", apiKey, err)return false}return true
}// 在API处理函数中调用限流器
func handleAPIRequest(apiKey string) {if rateLimiter(apiKey, 100, 1) { // 限流阈值设为100,时间窗口为1秒// 处理API请求} else {// 限流,返回错误或提示信息}
}

2.5 共享session

在多服务器的Web应用中,用户在不同的服务器上请求时能够保持登录状态,实现会话共享。

案例讲解
背景

考虑一个大型电商平台,它使用多个服务器来处理用户请求以提高可用性和伸缩性。当用户登录后,其会话信息(session)需要在所有服务器间共享,以确保无论用户请求到达哪个服务器,都能识别其登录状态。

优势
  1. 用户体验:用户在任何服务器上都能保持登录状态,无需重复登录。
  2. 系统可靠性:集中管理session减少了因服务器故障导致用户登录状态丢失的风险。
  3. 伸缩性:易于扩展系统以支持更多服务器,session管理不受影响。
解决方案

使用Redis的String类型来集中存储和管理用户session信息。

  • 存储Session:当用户登录成功后,将用户的唯一标识(如session ID)和用户信息序列化后存储在Redis中。
  • 验证Session:每次用户请求时,通过请求中的session ID从Redis获取session信息,验证用户状态。
  • 更新Session:用户活动时,更新Redis中存储的session信息,以保持其活跃状态。
  • 过期策略:设置session信息在Redis中的过期时间,当用户长时间不活动时自动使session失效。
代码实现
go">package mainimport ("context""encoding/json""github.com/go-redis/redis/v8""strconv""time"
)var (rdb *redis.Client   // 为了表现代码的完整性,这里省略了初始化代码ctx context.Context // 为了表现代码的完整性,这里省略了初始化代码
)// 伪代码:用户登录并存储session
func userLogin(username string, password string) (string, error) {// 验证用户名和密码// 创建session IDsessionID := generateSessionID()// 序列化用户信息userInfo := map[string]string{"username": username}serializedInfo, err := json.Marshal(userInfo)if err != nil {// 处理错误return "", err}// 存储session信息到Redis,设置过期时间err = rdb.Set(ctx, sessionID, string(serializedInfo), time.Duration(30)*time.Minute).Err()if err != nil {// 处理错误return "", err}return sessionID, nil
}// 伪代码:从请求中获取并验证session
func validateSession(sessionID string) (map[string]string, error) {// 从Redis获取session信息serializedInfo, err := rdb.Get(ctx, sessionID).Result()if err != nil {// 处理错误或session不存在return nil, err}// 反序列化用户信息var userInfo map[string]stringerr = json.Unmarshal([]byte(serializedInfo), &userInfo)if err != nil {// 处理错误return nil, err}return userInfo, nil
}// 伪代码:生成新的session ID
func generateSessionID() string {return strconv.FormatInt(time.Now().UnixNano(), 36)
}

http://www.ppmy.cn/news/1525629.html

相关文章

HarmonyOS开发之路由跳转

文章目录 一、路由跳转模式与实例1.router.pushUrl2.router.replaceUrl3.router.back 一、路由跳转模式与实例 跳转模式 有点类似于vue的路由跳转 router.pushUrl 保留路由栈,保留当前的页面;router.replaceUrl 销毁当前页面,跳转一个新的页…

Go语言现代web开发08 if和switch分支语句

if语句 If is the most common conditional statement in programming languages. If the result of the condition caculation is positive(true), the code inside if statement will be executed. In the next example, value a will be incremented if it is less than 10…

opencv学习:信用卡卡号识别

该代码用于从信用卡图像中自动识别和提取数字信息。该系统将识别信用卡类型,并输出信用卡上的数字序列。 1.创建命令行参数 数字模板 信用卡 # 创建命令行参数解析器 ap argparse.ArgumentParser() # 添加命令行参数 -i/--image,指定输入图像路径 ap.…

饿了么基于Flink+Paimon+StarRocks的实时湖仓探索

摘要:本文整理自饿了么大数据架构师、Apache Flink Contributor 王沛斌老师在8月3日 Streaming Lakehouse Meetup Online(Paimon x StarRocks,共话实时湖仓架构)上的分享。主要分为以下三个内容: 饿了么实时数仓演进之…

python-游戏自动化(一)(实战-自动刷视频点赞)

前提准备 什么是游戏自动化? 游戏自动化是指通过对游戏的界面结构的解析或界面图像的处理与识别,再模拟人工对软件进行的各种操作,从而实现自动化,达到解放双手,节约时间,提高效率的目标。 在本教程中&am…

房产销售系统开发:SpringBoot技术要点

摘 要 随着科学技术的飞速发展,各行各业都在努力与现代先进技术接轨,通过科技手段提高自身的优势;对于房产销售系统当然也不能排除在外,随着网络技术的不断成熟,带动了房产销售系统,它彻底改变了过去传统的…

RDMA应用场景及效果

GPU Direct 参考:网络架构如何支持超万卡的大规模 AI 训练?| AICon_芯片与网络_InfoQ精选文章 GPU 网络的情况已经发生了很大变化。每个 GPU 都有自己的内部互联,例如 NVIDIA 的 A100 或 H800,它们内部的 NVLink 互联可以达到 6…

【网络安全】空字节绕过:URL回调+XSS+SQL绕WAF

未经许可,不得转载。 文章目录 空字节URL回调XSSSQL空字节 \0,也称为null字节,是一个值为零的特殊字符。在编程中,通常用来表示字符串的结束。攻击者可以利用null字节注入来绕过一些验证或过滤机制。 以下三个漏洞,空字节功不可没。 URL回调 密码重置功能,发起请求后…

如何找到UI5 Tooling-UI5命令

文章目录 UI5 Tooling第一步:首先找找到UI5 的官网如下:第二步:找到get started, 学习UI5 Demo第三步:开发环境--搭建安装UI5 命令行界面Global installation to have the command availableAdditional local install …

学习平台|基于java的移动学习平台系统小程序(源码+数据库+文档)

学习平台|学习平台系统|在线学习平台系统小程序 目录 基于java的移动学习平台系统小程序 一、前言 二、系统设计 三、系统功能设计 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取: 博主介绍:✌️大厂码…

基于大数据的科研热点分析与挖掘系统

温馨提示:文末有 CSDN 平台官方提供的学长 QQ 名片 :) 1. 项目简介 科研活动的快速发展产生了大量的学术文献,如何从这些文献中提炼出有价值的科研热点和趋势成为了一个重要的问题。本项目旨在开发一个基于大数据的科研热点分析可视化系统,采…

综合型医院适合什么样的数据摆渡方式,才能服务与安全兼顾?

综合型医院,是提供全面医疗服务的综合型医院。综合型医院的服务对象广泛,包括儿童、成人、老年人等各年龄段的人群,以及患有各种疾病的患者。它们通过提供全面的医疗服务,保障人民群众的健康需求,是医疗卫生事业的重要…

ComfyUI安装节点过程中被降低了版本的软件包重新安装

最近在安装2个没怎么及时更新节点时,安装节点依赖性过程中,将原高版本的软件包,给降到了低版本,解决的办法就是:1、再次删除软件包,2、指定版本号重新安装回高版本软件包。

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

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

【运维】自动化运维工具,使用 Ansible 进行开发环境配置管理(本地/远程,brew/scoop/yum,docker/packer/openstack)

【运维】自动化运维工具,使用 Ansible 进行开发环境配置管理(本地/远程,brew/scoop/yum,docker/packer/openstack) 文章目录 1、什么是 Ansible,如何安装2、使用 ansible 自动配置本地开发环境(…

前端页面加载由模糊到清晰的实现方案

要实现图片加载时由模糊逐渐变得清晰的效果,可以使用 CSS 和 JavaScript 的结合。这里的思路是:先让图片在加载时模糊显示,等图片完全加载完后,再去掉模糊效果。 1. 使用 CSS 实现模糊效果 我们可以使用 filter: blur() 来为图片…

[数据集][目标检测]车油口挡板开关闭合检测数据集VOC+YOLO格式138张2类别

数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):138 标注数量(xml文件个数):138 标注数量(txt文件个数):138 标注类别…

从C语言过渡到C++

📔个人主页📚:秋邱-CSDN博客☀️专属专栏✨:C 🏅往期回顾🏆:单链表实现:从理论到代码-CSDN博客🌟其他专栏🌟:C语言_秋邱的博客-CSDN博客 目录 ​…

Day 72

作业 #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QLineEdit> #include <QLabel> #include <QIcon> #include <QPushButton> #include <QMovie> #include <QPainter> #include <QWidget> …

性能测试 —— linux服务器搭建JMeter+Grafana+Influxdb监控可视化平台!

前言 在当前激烈的市场竞争中&#xff0c;创新和效率成为企业发展的核心要素之一。在这种背景下&#xff0c;如何保证产品和服务的稳定性、可靠性以及高效性就显得尤为重要。 而在软件开发过程中&#xff0c;性能测试是一项不可或缺的环节&#xff0c;它可以有效的评估一个系…