redis基本数据结构-sorted set

news/2024/12/21 20:59:22/

1. sorted set的简单介绍

参考链接:https://mp.weixin.qq.com/s/srkd73bS2n3mjIADLVg72A
Redis的Sorted Set(有序集合)是一种数据结构,它是一个不重复的字符串集合,每个元素都有一个对应的分数(score),可以根据分数对元素进行排序。Sorted Set的特点是能够在O(log(N))的时间复杂度内进行插入和删除操作,同时可以通过分数快速检索和排序元素
具备以下特性:
唯一性:每个元素在集合中是唯一的,但可以有相同的分数。
排序:元素根据分数进行排序,分数相同的元素按字典序排序。
范围查询:支持通过分数或排名进行范围查询。
高效操作:对元素的插入、删除和查找操作均为O(log(N))。
在Redis中,数据结构的底层实现是非常关键的。对于你提到的Set和Sorted Set,它们的底层实现是不同的。

1.1. 底层结构介绍

Set
Redis的Set(无序集合)底层使用的是哈希表(Hash Table)。具体来说,Redis在实现Set时,使用了一个哈希表来存储集合中的元素。因为哈希表具有O(1)的时间复杂度来进行插入、删除和查找操作,所以Set在这些操作上非常高效。
Sorted Set
Redis的Sorted Set(有序集合)则是一个更复杂的数据结构,底层实现结合了两种结构:

  1. 哈希表:用于存储元素和其分数之间的映射关系。
  2. 跳表(Skip List):用于维护元素的有序性,以便能够高效地进行范围查询和排名操作。跳表是一种可以在O(log(N))时间复杂度内进行插入、删除和查找操作的数据结构
    总结
  • Set:底层实现是哈希表。
  • Sorted Set:底层实现是哈希表结合跳表。
    这种设计使得Sorted Set能够在保证元素唯一性的同时,同时高效地支持按分数排序和范围查询等操作。这样,Redis可以在需要高效检索和排序的业务场景中,提供良好的性能表现。

1.2. 常用命令

# 将元素member添加到有序集合key中,如果元素已存在,则更新其分数为score。
ZADD Key score member
# 比如向排行榜leaderboard新添加三名玩家player_xxx, 分数如下所示:
ZADD leaderboard 100 "player_10086"
ZADD leaderboard 200 "player_10087"
ZADD leaderboard 150 "player_10088"# 返回[start,stop]范围内的集合成员,后面的选项可以决定分数也返回。
ZRANGE key start stop [WITHSCORES]
# 比如返回排在前两位的玩家
ZRANGE leaderboard 0 1 WITHSCORES
#结果输出
player_10086 100 player_10087 150# 如果先按分数高的在前面,也就是返回分数前两名的玩家,可以使用
ZRERANGE:该命令与ZRANGE一样格式,只不过它是倒序; ZSCORE Key member # 获取指定成员的分数
ZSCORE leadergroup "player_10086"
输出:100ZREM key member [member ...] #删除元素
ZREM leaderboard "player_10087"ZRANK key member #获取指定元素的排名
ZRANK leaderboard "player_10086"ZRANGEBYSCORE key min max [WITHSCORES] #按照分数范围查询
ZRANGEBYSCORE leaderboard 50 150 WITHSCORESZINCRBY Key score member #给元素member增加score分数
ZINCRBY leaderboard 40 "player_10086"

2. 常见的业务场景介绍

2.1. 排行榜系统

场景
排行榜系统:Sorted Set类型非常适合实现排行榜系统,如游戏得分排行榜、文章热度排行榜等。在一个在线游戏中,玩家的得分需要实时更新并显示在排行榜上。使用Sorted Set可以方便地根据得分高低进行排序。
优势
实时排序:根据玩家的得分自动排序,无需额外的排序操作。
动态更新:可以快速地添加新玩家或更新现有玩家的得分。
范围查询:方便地查询排行榜的前N名玩家。
解决方案
使用Redis Sorted Set来存储和管理游戏玩家的得分排行榜。
在这里插入图片描述
代码实现

package mainimport ("context""fmt""github.com/go-redis/redis/v8""log"
)var ctx = context.Background()// Redis 客户端初始化
var rdb = redis.NewClient(&redis.Options{Addr:     "",  // Redis 服务器地址Password: "", // 密码DB:       0,                  // 使用默认 DB
})// 更新玩家得分
func updatePlayerScore(playerID string, score float64) error {sortedSetKey := "playerScores"// 添加或更新玩家得分_, err := rdb.ZAdd(ctx, sortedSetKey, &redis.Z{Score: score, Member: playerID}).Result()return err
}// 获取排行榜
func getLeaderboard(start int, stop int) ([]string, error) {sortedSetKey := "playerScores"// 获取排行榜数据leaderboard, err := rdb.ZRangeWithScores(ctx, sortedSetKey, int64(start), int64(stop)).Result()if err != nil {return nil, err}var result []stringfor _, entry := range leaderboard {result = append(result, fmt.Sprintf("%s: %.2f", entry.Member.(string), entry.Score))}return result, nil
}// 获取前N名玩家
func getTopNPlayers(n int) ([]string, error) {return getLeaderboard(0, n-1) // 获取前N名,stop需要是n-1
}// 清理测试数据
func clearTestKeys() error {sortedSetKey := "playerScores"_, err := rdb.Del(ctx, sortedSetKey).Result()return err
}func main() {// 更新玩家得分示例if err := updatePlayerScore("player1", 100); err != nil {log.Fatalf("Error updating player score: %v", err)}if err := updatePlayerScore("player2", 200); err != nil {log.Fatalf("Error updating player score: %v", err)}if err := updatePlayerScore("player3", 150); err != nil {log.Fatalf("Error updating player score: %v", err)}// 获取前2名玩家的排行榜topPlayers, err := getTopNPlayers(2)if err != nil {log.Fatalf("Error getting top players: %v", err)}fmt.Println("Top 2 Players:")for _, player := range topPlayers {fmt.Println(player)}// 清理测试数据if err := clearTestKeys(); err != nil {log.Fatalf("Error clearing test keys: %v", err)}fmt.Println("Test keys cleared.")
}

在这里插入图片描述

2.2. 实时数据获取

场景
实时数据统计:Sorted Set可以用于实时数据统计,如网站的访问量统计、商品的销量统计等。在一个电商平台中,需要统计商品的销量,并根据销量对商品进行排序展示。
优势
自动排序:根据销量自动对商品进行排序。
灵活统计:可以按时间段统计销量,如每日、每周等。
解决方案
使用Redis Sorted Set来实现商品的销量统计和排序。
在这里插入图片描述
代码实现

package mainimport ("context""fmt""github.com/go-redis/redis/v8""log""time"
)var ctx = context.Background()// Redis 客户端初始化
var rdb = redis.NewClient(&redis.Options{Addr:     "",  // Redis 服务器地址Password: "", // 密码DB:       0,                  // 使用默认 DB
})// 更新商品销量
func updateProductSales(productID string, sales int64) {today := time.Now().Format("2006-01-02")sortedSetKey := "productSales:" + today// 增加商品销量rdb.ZIncrBy(ctx, sortedSetKey, float64(sales), productID)
}// 获取商品销量排行
func getProductSalesRanking(date string) ([]string, error) {sortedSetKey := "productSales:" + date// 获取销量排行数据ranking, err := rdb.ZRevRangeWithScores(ctx, sortedSetKey, 0, -1).Result() // 按销量从高到低排序if err != nil {return nil, err}var result []stringfor _, entry := range ranking {result = append(result, fmt.Sprintf("%s: %d", entry.Member.(string), int(entry.Score)))}return result, nil
}// 获取某个时间段的商品销量(如每日、每周)
func getSalesByPeriod(productID string, startDate string, endDate string) (int64, error) {totalSales := int64(0)start, _ := time.Parse("2006-01-02", startDate)end, _ := time.Parse("2006-01-02", endDate)for d := start; !d.After(end); d = d.AddDate(0, 0, 1) {dateStr := d.Format("2006-01-02")sales, err := rdb.ZScore(ctx, "productSales:"+dateStr, productID).Result()if err == nil {totalSales += int64(sales)} else if err != redis.Nil {return 0, err // 其他错误}}return totalSales, nil
}func main() {// 示例:更新产品销量updateProductSales("product1", 10)updateProductSales("product2", 20)updateProductSales("product1", 5)// 示例:获取今日的产品销量排行today := time.Now().Format("2006-01-02")ranking, err := getProductSalesRanking(today)if err != nil {log.Fatalf("Error getting sales ranking: %v", err)}fmt.Println("Today's product sales ranking:")for _, entry := range ranking {fmt.Println(entry)}// 示例:获取某段时间内某个产品的总销量totalSales, err := getSalesByPeriod("product1", "2023-10-01", "2023-10-07")if err != nil {log.Fatalf("Error getting sales by period: %v", err)}fmt.Printf("Total sales for product1 from 2023-10-01 to 2023-10-07: %d\n", totalSales)// 清理测试数据rdb.Del(ctx, "productSales:"+today)// 如果需要清理特定日期的销量数据,可以在这里添加更多的 DEL 语句// rdb.Del(ctx, "productSales:2023-10-01")// rdb.Del(ctx, "productSales:2023-10-02")// 根据需求添加更多日期
}

在这里插入图片描述
注意事项:

  • Sorted Set中的分数可以是浮点数,这使得它可以用于更精确的排序需求。
  • 元素的分数可以动态更新,但应注意更新操作的性能影响。
  • 使用Sorted Set进行范围查询时,应注意合理设计分数的分配策略,以避免性能瓶颈。
  • 在设计排行榜或其他需要排序的功能时,应考虑数据的时效性和更新频率,选择合适的数据结构和索引策略。

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

相关文章

Mybatis中Like模糊查询三种处理方式

目录 Mybatis中Like模糊查询三种处理方式 1.通过单引号拼接${} 1)mapper接口 2)Mapper.xml 3)测试代码 4) 测试结果 2.通过concat()函数拼接(个人推荐使用这种) 1)mapper接口 2)Mapper.xml 3)测试代码 4) 测…

java数据结构的底层原理——HashMap

HashMap 是 Java 中用于存储键值对的集合类,提供了高效的插入、删除和查找操作。它的底层数据结构是哈希表。以下是 HashMap 的一些关键点和底层原理: 1. 底层数据结构 HashMap 的底层实现是一个数组加上链表(或红黑树)。每个数…

2024工业机器视觉产业现状

早在20世纪80年代美国国家标准局就预计,检测任务的80%乃至90%将由视觉测量系统来完成,该预测至今已基本变成现实。当前,以智能制造为核心的工业4.0时代背景下,新型工业化的战略部署逐步深入,伴随AI大模型技术应用的逐步…

【字幕】恋上数据结构与算法之01为什么要学习数据结构与算法

视频地址:请查看01为什么要学习数据结构与算法_哔哩哔哩_bilibili 同志们好,我是小码哥的mj李明杰。非常欢迎大家来学习链上数据结构与算法,从今天开始呢就由我来带大家一起来学习和掌握这个数据结构与算法啊。在正式学习之前我们先来看一下…

Session 和 Cookie 的数据结构示例

当然,我理解您希望在流程图中也展示相关的数据结构。我将在流程图中添加 Session 和 Cookie 的数据结构示例。为了保持图表的清晰度,我会使用注释框来展示这些数据结构。 #mermaid-svg-FXddHXWcPqZtfaqW {font-family:"trebuchet ms",verdana,…

湖仓一体-Paimon篇-简介

什么是Paimon? 2021年末,Flink官方提出打造一个全新的存储 Flink Table Store,一个 Flink 完全内置的存储。 为了让 Flink Table Store 能够有更大的发展,Flink PMC经过讨论决定将其捐赠Apache进行独立孵化。 2023 年 3 月 12 日…

Android 源码多个Launcher设置默认Launcher

目录 第一部分、android10之前 一.多个launcher 启动设置默认launcher的核心类 二 在自定义服务里面设置默认Launcher 第二部分、android10之后 一、Launcher应用内置并设置为默认Launcher 1.通过ResolverActivity.java设置为默认Launcher 改法一: 改法二&am…

Python——俄罗斯方块

俄罗斯方块游戏是一款经典的益智游戏,通常使用编程语言Python来实现。下面是一个简单的俄罗斯方块游戏的示例代码: import pygame import random# 定义颜色 BLACK (0, 0, 0) WHITE (255, 255, 255) RED (255, 0, 0) GREEN (0, 255, 0) BLUE (0, 0,…