redis基本数据结构-sorted set

news2024/9/20 11:01:36

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.coloradmin.cn/o/2139956.html

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

相关文章

模板方法模式:设计模式中的骨架法则

模板方法模式(Template Method Pattern)是一种行为设计模式,它定义了一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 一,模板方法模式的…

C# HttpListener 实现的HTTP Sever浏览器文件下载

1. 前端页面请求 编写简单的test.html 文件&#xff0c;body体值配置a标签&#xff0c;其href 属性设置为文件下载请求的http接口要求的参数序列。 <!DOCTYPE html><html> <head><meta name"viewport" content"widthdevice-width" …

行业分析---自动驾驶行业的发展

1 背景 进入21世纪以来&#xff0c;自动驾驶行业有着飞速的发展&#xff0c;L2级别的自动驾驶技术也逐渐落地量产到寻常百姓家。不管是起步比较早的特斯拉&#xff0c;还是2015年以后国内的公司&#xff0c;都在逐渐发展自动驾驶技术&#xff0c;并量产给用户使用。 自动驾驶最…

COMDEL电源维修CLX2500康戴尔射频电源维修

美国COMDEL射频电源维修常见型号包括&#xff1a;CLX2750&#xff1b;CLX2500&#xff1b;CLX-600H&#xff1b;CX600AS&#xff1b;CX-5000S&#xff1b;CX-3500S&#xff1b;CX-2500S&#xff1b;CV500&#xff1b;CDX2000等。 Comdel成立于1966年&#xff0c;总部设在马萨诸…

Linux环境基础开发工具使用(gcc/g++与makefile)

1.Linux编译器-gcc/g使用 1. 背景知识 接下来的操作&#xff0c;我以gcc为例&#xff0c;因为两者选项都是通用的&#xff0c;所以也就相当于间接学习了 1.预处理&#xff08;进行宏替换) 2.编译&#xff08;生成汇编) 3.汇编&#xff08;生成机器可识别代码&#xff09;…

革新骨科金属螺丝:TPMS结构助力骨再生研究新突破AbMole

在骨科植入物领域&#xff0c;金属螺丝因其出色的机械强度和生物相容性&#xff0c;一直是骨折固定不可或缺的工具。然而&#xff0c;传统实心设计的金属螺丝常常面临应力遮挡和术后松动的问题&#xff0c;严重影响其长期固定效果。近期&#xff0c;一项由吉林大学第二医院骨科…

第T1周:Tensorflow实现mnist手写数字识别

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 目标&#xff1a; 具体实现&#xff1a; &#xff08;一&#xff09;环境&#xff1a; 语言环境&#xff1a;Python 3.10 编 译 器: PyCharm 框架&#xff1a…

Mixtral 8x7B:开源稀疏混合专家模型的新里程碑

人工智能咨询培训老师叶梓 转载标明出处 随着大模型规模的增大&#xff0c;计算成本和资源消耗也相应增加&#xff0c;这限制了它们的应用范围和效率。本论文介绍了一种新的稀疏混合专家模型&#xff08;SMoE&#xff09;——Mixtral 8x7B&#xff0c;它在保持较小计算成本的同…

【C++】c++ 11

目录 前言 列表初始化 std::initializer_list 右值引用和移动拷贝 左值和右值 左值引用和右值引用的区别 万能引用&#xff08;引用折叠&#xff09; 完美转发 默认成员函数控制 列表初始化 在C98中&#xff0c;标准允许使用花括号{}对数组或者结构体元素进行统一的列…

Gartner 成熟度曲线报告解读(一)| 2024中国IT基础设施使用趋势、影响中国IT使用的4大因素

近些年&#xff0c;面对数字化转型、信息化发展、政策监管与地缘政治等外部因素&#xff0c;以及降本增效的内部需求&#xff0c;不少中国企业在制定 IT 基础设施发展策略时遇到多重挑战。为帮助国内企业用户优化基础设施战略&#xff0c;Gartner 近日发布《中国 IT 基础设施技…

【HCIA-Datacom】华为VRP系统

| &#x1f449;个人主页&#xff1a;Reuuse 希望各位多多支持&#xff01;❀ | &#x1f449;往期博客&#xff1a;网络参考模型 | 最后如果对你们有帮助的话希望有一个大大的赞&#xff01; | ⭐你们的支持是我最大的动力&#xff01;⭐ | 目录 1. 华为VRP系统概述VRP概念设备…

Docker-compose:管理多个容器

Docker-Compose 是 Docker 公司推出的一个开源工具软件&#xff0c;可以管理多个 Docker 容器组成一个应用。用户需要定义一个 YAML 格式的配置文件 docker-compose.yml&#xff0c;写好多个容器之间的调用关系。然后&#xff0c;只要一个命令&#xff0c;就能同时启动/关闭这些…

七、垃圾收集器ParNewCMS与底层三色标记算法详解

文章目录 垃圾收集算法分代收集理论标记-复制算法标记-清除算法标记-整理算法 垃圾收集器1.1 Serial收集器(-XX:UseSerialGC -XX:UseSerialOldGC)1.2 Parallel Scavenge收集器(-XX:UseParallelGC(年轻代),-XX:UseParallelOldGC(老年代))1.3 ParNew收集器(-XX:UseParNewGC)1.4 C…

POSIX信号量以及利用POSIX信号量实现基于循环队列的高效生产者消费者模型

&#x1f351;个人主页&#xff1a;Jupiter. &#x1f680; 所属专栏&#xff1a;Linux从入门到进阶 欢迎大家点赞收藏评论&#x1f60a; 目录 &#x1f341;POSIX信号量 &#x1f341;信号量的相关接口介绍*初始化信号量**销毁信号量**等待信号量**发布信号量* &#x1f341;&…

YOLOv9 简介

YOLO v9 是目前表现最佳的目标检测器之一&#xff0c;被视为现有 YOLO 变体&#xff08;如 YOLO v5、YOLOX 和 YOLO v8&#xff09;的改进版本。 YOLOv9 在实时目标检测领域取得了重大进展&#xff0c;引入了诸如可编程梯度信息&#xff08;PGI&#xff09;和通用高效层聚合网…

后端开发刷题 | 打家劫舍

描述 你是一个经验丰富的小偷&#xff0c;准备偷沿街的一排房间&#xff0c;每个房间都存有一定的现金&#xff0c;为了防止被发现&#xff0c;你不能偷相邻的两家&#xff0c;即&#xff0c;如果偷了第一家&#xff0c;就不能再偷第二家&#xff1b;如果偷了第二家&#xff0…

Dina靶机详解

靶机下载 https://www.vulnhub.com/entry/dina-101,200/ 靶机配置 默认是桥接模式&#xff0c;切换为NAT模式后重启靶机 主机发现 arp-scan -l 端口扫描 nmap -sV -A -T4 192.168.229.157 发现80端口开启&#xff0c;访问 访问网站 目录扫描 python dirsearch.py -u http…

1.2 交换技术

欢迎大家订阅【计算机网络】学习专栏&#xff0c;开启你的计算机网络学习之旅&#xff01; 文章目录 前言一、电路交换1. 定义与原理2. 工作过程3. 优点与局限 二、分组交换1. 定义与原理2. 工作过程3. 优点与局限 三、报文交换1. 定义与原理2. 工作过程3. 优点与局限 四、比较…

改进RRT*的路径规划算法

一、RRT算法 RRT 算法是一种基于随机采样的快速搜索算法。该算法的主要思想是通过随机采样来创建一个快速探索的树&#xff0c;从而生长出一条从起点到终点的路径。如图为随机树的生长过程。 初始化。首先&#xff0c;初始化起始点和目标点位置&#xff0c;并将起点作为根节点…

printf()函数的全面介绍及用法——简单易懂

printf&#xff08;&#xff09;函数介绍 目录 printf&#xff08;&#xff09;函数介绍 一&#xff1a;头文件 二&#xff1a;格式控制字符串 1.格式字符。 2.转义字符。 3.普通字符。 三&#xff1a;格式字符输出示例 1. %c-----------输出字符 2. %s-----------输…