redis基本数据结构-sorted set

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"
输出:100

ZREM key member [member ...] #删除元素
ZREM leaderboard "player_10087"

ZRANK key member #获取指定元素的排名
ZRANK leaderboard "player_10086"

ZRANGEBYSCORE key min max [WITHSCORES] #按照分数范围查询
ZRANGEBYSCORE leaderboard 50 150 WITHSCORES

ZINCRBY Key score member #给元素member增加score分数
ZINCRBY leaderboard 40 "player_10086"

2. 常见的业务场景介绍

2.1. 排行榜系统

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

package main

import (
    "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 []string
    for _, 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 main

import (
    "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 []string
    for _, 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.mfbz.cn/a/881928.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

科研入门学习

学习视频链接 为什么要读论文 读哪些论文 论文的分类 论文质量 如何找论文 根据领域大牛的名字进行搜索查看高水平论文引用的论文,高水平论文引用的论文很大程度也是高水平的论文 如何整理论文 如何读论文 读论文的困境 不同人群阅读差异 读论文的方式 论文的结构…

Day.js时间插件的安装引用与常用方法大全

🚀 个人简介:某大型国企资深软件研发工程师,信息系统项目管理师、CSDN优质创作者、阿里云专家博主,华为云云享专家,分享前端后端相关技术与工作常见问题~ 💟 作 者:码喽的自我修养&#x1f9…

BLE 设备丢包理解

前言 个人邮箱:zhangyixu02gmail.com在学习 BLE 过程中,总能听到 “丢包” 一词,但是我查阅资料又发现,有大佬说,ATT所有命令都是“必达”的,不存在所谓的“丢包”。而且我发现,在宣传 BLE 产品…

SpringBoot 整合 Caffeine 实现本地缓存

目录 1、Caffeine 简介1.1、Caffeine 简介1.2、对比 Guava cache 的性能主要优化项1.3、常见的缓存淘汰算法1.4、SpringBoot 集成 Caffeine 两种方式 2、SpringBoot 集成 Caffeine 方式一2.1、缓存加载策略2.1.1、手动加载2.1.2、自动加载【Loading Cache】2.1.3、异步加载【As…

Jboss 低版本JMX Console未授权

漏洞描述 此漏洞主要是由于JBoss中/jmx-console/HtmlAdaptor路径对外开放,并且没有任何身份验证机制,导致攻击者可以进⼊到 jmx控制台,并在其中执⾏任何功能 影响范围 Jboss4.x以下 环境搭建 cd vulhub-master/jboss/CVE-2017-7504 doc…

Java笔试面试题AI答之单元测试JUnit(7)

文章目录 37. 请列举一些JUnit扩展 ?1. 参数化测试2. 条件测试执行3. 临时目录4. 时间测试5. 重复测试6. 前置/后置条件7. Mockito8. Spring Test9. JUnit Vintage10. Testcontainers11. 自定义注解和扩展12. 测试监听器(TestListener 和 RunListener&am…

WIFI路由器的套杆天线简谈

❝本次推文简单介绍下WIFI路由器的套杆天线。 路由器天线 路由器在这个万物互联的时代,想必大家对其都不陌生。随着科技的发展,常用的路由器上的天线也越来越多,那么问题来了:天线越多,信号越好吗?路由器…

智谱清影 - CogVideoX-2b-部署与使用

🍑个人主页:Jupiter. 🚀 所属专栏:Linux从入门到进阶 欢迎大家点赞收藏评论😊 目录 体验地址:[丹摩DAMODEL官网](https://www.damodel.com/console/overview) CogVideoX 简介本篇将详细介绍使用丹摩服务器部…

Codeforces Round 974 (Div. 3)

比赛地址 : Dashboard - Codeforces Round 974 (Div. 3) - Codeforceshttps://codeforces.com/contest/2014 A 模拟 #include<bits/stdc.h> #define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); using namespace std;#define endl \n typedef long long …

Qt 模型视图(一):概述

文章目录 Qt 模型视图(一):概述1、模型/视图结构基本原理2、模型3、视图4、代理5、简单实例 Qt 模型视图(一):概述 ​ 模型/视图结构是一种将数据存储和界面展示分离的编程方法。模型存储数据&#xff0c;视图组件显示模型中的数据&#xff0c;在视图组件里修改的数据会被自动…

MySQL练手题--体育馆的人流量(困难)

一、准备工作 Create table If Not Exists Stadium (id int, visit_date DATE NULL, people int); Truncate table Stadium; insert into Stadium (id, visit_date, people) values (1, 2017-01-01, 10); insert into Stadium (id, visit_date, people) values (2, 2017-01-02…

C++调用C# DLL之踩坑记录

C是非托管代码&#xff0c;C#则是托管代码&#xff0c;无法直接调用 CLR的介绍见CLR简介 MSDN提到了两种非托管-托管的交互技术&#xff1a;CLR Interop和COM Interop 后者要将C# 类库注册为COM组件&#xff0c;本文只探讨CLR&#xff0c;要通过C CLR写中间层代码 方式一&…

软件测试技术之 GPU 单元测试是什么!

1 背景 测试是开发的一个非常重要的方面&#xff0c;可以在很大程度上决定一个应用程序的命运。良好的测试可以在早期捕获导致应用程序崩溃的问题&#xff0c;但较差的测试往往总是导致故障和停机。 单元测试用于测试各个代码组件&#xff0c;并确保代码按照预期的方式工作。单…

Cesium 绘制可编辑点

Cesium Point点 实现可编辑的pointEntity 实体 文章目录 Cesium Point点前言一、使用步骤二、使用方法二、具体实现1. 开始绘制2.绘制事件监听三、 完整代码前言 支持 鼠标按下 拖动修改点,释放修改完成。 一、使用步骤 1、点击 按钮 开始 绘制,单击地图 绘制完成 2、编辑…

java(3)数组的定义与使用

目录 1.前言 2.正文 2.1数组的概念 2.2数组的创建与初始化 2.2.1数组的创建 2.2.1数组的静态初始化 2.2.2数组的动态初始化 2.3数组是引用类型 2.3.1引用类型与基本类型区别 2.3.2认识NULL 2.4二维数组 2.5数组的基本运用 2.5.1数组的遍历 2.5.2数组转字符串 2.…

C#学习笔记(三)Visual Studio安装与使用

博主刚开始接触C#&#xff0c;本系列为学习记录&#xff0c;如有错误欢迎各位大佬指正&#xff01;期待互相交流&#xff01; 上一篇文章中安装了Visual Studio Code来编写调试C#程序&#xff0c;但是博主的目标是编写带窗口的应用程序&#xff0c;了解之后发现需要安装Visual …

基于SpringBoot+定时任务实现地图上绘制车辆实时运动轨迹图

目录 1. 项目结构 2. Maven依赖配置 (pom.xml) 3. 实现后端服务 4. 配置文件 (application.properties) 5. 启动项目 6. 访问页面 实现基于北斗卫星的车辆定位和轨迹图的Maven工程&#xff08;使用模拟数据&#xff09;&#xff0c;我们将使用以下技术&#xff1a; Spri…

局部凸空间及其在算子空间中的应用之四——归纳极限空间2

局部凸空间及其在算子空间中的应用之四——归纳极限空间2 前言一、归纳极限拓扑中极限的含义总结 数学的真理是绝对的&#xff0c;它超越了时间和空间。——约翰冯诺伊曼 前言 在上一篇文章中&#xff0c;我们讨论了归纳极限拓扑的概念和与连续线性算子有关的一个重要结论。认…

【Qt | QAction】Qt 的 QAction 类介绍

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

OSG开发笔记(三十):OSG加载动力学仿真K模型文件以及测试Demo

​ 若该文为原创文章&#xff0c;未经允许不得转载 本文章博客地址&#xff1a;https://blog.csdn.net/qq21497936/article/details/142340138 各位读者&#xff0c;知识无穷而人力有穷&#xff0c;要么改需求&#xff0c;要么找专业人士&#xff0c;要么自己研究 长沙红胖子Q…