Redis中ZSet的具体使用

 更新时间:2022年07月18日 09:40:23   作者:北极星小王子  
本文主要介绍了Redis中ZSet的具体使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

一、题目

ZSet能用在哪些场景?跳表查找的过程,时间复杂度

二、ZSet 简单使用

举个例子,fruit-price 是一个有序集合键,这个有序集合以水果名为成员,水果价钱为分值,保存了 130 款水果的价钱:

redis>ZADD fruit-price 5 "banana"
redis>ZADD fruit-price 6.5 "cherry"
redis>ZADD fruit-price 8 "apple"


redis> ZRANGE fruit-price 0 2 WITHSCORES
1) "banana"
2) "5"
3) "cherry"
4) 6.5
5) "apple"
6) "8"

redis> ZCARD fruit-price
(integer)130

三、ZSet 结构

ZSet 结构即支持单个元素查询,又支持范围查询,是如何实现的呢?

Redis 中有两种数据结构来支持 ZSet 的功能,一个是字典 dict ,一个是 zskipList; 字典保存着从 member 到 score 的映射,跳跃表按 score 从小到大保存所有集合元素

先看下 ZSet 在代码中的定义:

typedef struct zset {
   dict *dict;
   zskiplist *zsl;
} zset;

dict 各种编程语言中都有实现。可以保证 O(1) 的时间复杂度; 我们继续看 zskiplist 的定义:

typedef struct zskiplist {
   struct zskiplistNode *header, *tail;
   unsigned long length;
   int level;
} zskiplist;

zskiplist 是 Redis 对 skiplist 做了变种,skiplist 就是我们常说的跳表;

四、跳跃表

跳跃表的特点

  • 由许多层结构组成,每层都是一个有序链表
  • 最底层的链表包含所有元素
  • 如果一个元素出现在 level i 层的链表中,则它在 level i 之下的链表中也都会出现
  • 每个节点包含两个指针,一个指向同一链表中的下一个元素,一个指向下面一层的元素

查找过程

  • 跳表的查找会从顶层链表的头部元素开始遍历该链表,直到找到元素大于或等于目标元素的节点,如果当前元素正好等于目标,那么就直接返回它;
  • 如果当前元素大于目标或到达链表尾部,则移动到前一个节点的位置,然后垂直下降到下一层;
  • 正因为 Skiplist 的搜索过程会不断地从一层跳跃到下一层的,所以被称为跳跃表;

举例说明

假设链接包含 1-10,共 10 个元素。我们要找到第 9 个,需要从 header 遍历,共 9 次才能找到:

在这里插入图片描述

一次只能比较一个数,最坏的情况下时间复杂度是 O(n),如果我们一次可以比较 2 个元素就好了:

在这里插入图片描述

一次查找 2 个的话,我们只找了 5 次就找到了。所以就有了类似下面的结构,在链表上增加一层减少了元素个数的 “链表”:

在这里插入图片描述

如果增加两层 “链表”,只查找 3 次就可以找到:

在这里插入图片描述

即便是我们找元素 8,也只需要遍历 1 -> 4 -> 7 -> 8,共 4 次查询;

这样查找过程就非常类似于一个二分查找,使得查找的时间复杂度可以降低到 O(log n)

ZskipList 插入过程:

在这里插入图片描述

从上面 Skiplist 的创建和插入过程可以看出,每一个节点的层数(level)是随机出来的,而且新插入一个节点不会影响其它节点的层数。 因此,插入操作只需要修改插入节点前后的指针,而不需要对很多节点都进行调整。这就降低了插入操作的复杂度

Redis 初始化的时候,只判断存储的元素长度是否大于 64 个字节。大于 64 个字节选择 Zkiplist,否则 Ziplist。当执行增删改查的方法,根据是 ziplist 还是 zkiplist 选择不同的实现。只需要记住 zset,在两种情况下使用 ziplist:

保存的元素个数不足 128 个;单个元素的大小超过 64 byte;

ziplist 编码的有序集合使用紧挨在一起的压缩列表节点来保存,第一个节点保存 member,第二个保存 score。ziplist 内的集合元素按 score 从小到大排序,score 较小的排在表头位置

为什么采用跳跃表

  • 跳表就是这样的一种数据结构,结点是跳过一部分的,从而加快了查询的速度。类似于 HashMap 中,Java 8 中当哈希冲突个数大于 7 个的时候,转换为红黑树;
  • 跳表跟红黑树两者的算法复杂度差不多,为什么 Redis 要使用跳表而不使用红黑树呢?跳表相对于红黑树,代码简单;
  • 如果我们要查询一个区间里面的值,用平衡树实现可能会麻烦。删除一段区间时,如果是平衡树,就会相当困难,毕竟涉及到树的平衡问题,而跳表则没有这种烦恼;

五、场景案例

1、信息统计

假设我们有某个班级所有学生的语文成绩,想统计、查询区间范围、查询单个学生成绩、满足高性能读取这些需求, Redis 的 zset 结构无疑是最好的选择。Redis 提供了丰富的 API。示例

ZADD yuwen 90 s01 89 s03 99 s02 74 s04 97 s05

以 yuwen 为 key 分别存储了 s01 到 s06 共计 6 名学生的分数,我们可以查询任一学生的成绩:

ZSCORE yuwen s03

可以按照排序返回指定区间内的所有元素

ZRANGE yuwen 1 2 withscores

可以访问指定分数区间内的所有元素

ZRANGEBYSCORE yuwen 90 100 withscores

可以统计指定区间内的个数

ZCOUNT yuwen 80 90

2、排行榜

  • 经常浏览技术社区的话,应该对 “1小时最热门” 这类榜单不陌生。如何实现呢?如果记录在数据库中,不太容易对实时统计数据做区分;
  • 我们以当前小时的时间戳作为 zset 的 key,把贴子 ID 作为 member ,点击数评论数等作为 score,当 score 发生变化时更新 score;
  • 利用 ZREVRANGE 或者 ZRANGE 查到对应数量的记录;

到此这篇关于Redis中ZSet的具体使用的文章就介绍到这了,更多相关Redis ZSet使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 你了解Redis事务吗

    你了解Redis事务吗

    说到事务,大家会立刻想到Mysql的事务,所谓的事务就是对数据进行一系列的操作,要么都执行成功,要么都执行失败,下面就介绍一下Redis如何实现事务,感兴趣的可以了解一下
    2022-08-08
  • Redis分布式锁及安全问题解决

    Redis分布式锁及安全问题解决

    在分布式环境中,遇到抢购等访问共享资源的场景时,需要我们有一种锁机制去解决并发问题,本文主要介绍了Redis分布式锁及安全问题解决,具有一定的参考价值,感兴趣的可以了解一下
    2024-03-03
  • 详解Redis命令和键_动力节点Java学院整理

    详解Redis命令和键_动力节点Java学院整理

    Redis命令用于在redis服务器上执行某些操作,下面通过本文给大家分享Redis命令和键,需要的的朋友参考下吧
    2017-08-08
  • 浅析redis cluster介绍与gossip协议

    浅析redis cluster介绍与gossip协议

    这篇文章主要介绍了redis cluster介绍与gossip协议,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-09-09
  • 使用高斯Redis实现二级索引的方法

    使用高斯Redis实现二级索引的方法

    本文介绍了如何通过高斯Redis搭建二级索引,二级索引在电商、图(hexastore)、游戏等领域具有广泛的应用场景,高斯redis现网亦有很多类似应用,需要的朋友跟随小编一起看看吧
    2022-07-07
  • 面试常问:如何保证Redis缓存和数据库的数据一致性

    面试常问:如何保证Redis缓存和数据库的数据一致性

    在实际开发过程中,缓存的使用频率是非常高的,只要使用缓存和数据库存储,就难免会出现双写时数据一致性的问题,那我们又该如何解决呢
    2021-09-09
  • Redisson分布式锁之加解锁详解

    Redisson分布式锁之加解锁详解

    这篇文章主要为大家介绍了Redisson分布式锁加解锁的详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-03-03
  • 一篇文章揭秘Redis的磁盘持久化机制

    一篇文章揭秘Redis的磁盘持久化机制

    这篇文章主要给大家介绍了如何通过一篇文章揭秘Redis的磁盘持久化机制的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Redis具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-09-09
  • Redis的配置、启动、操作和关闭方法

    Redis的配置、启动、操作和关闭方法

    今天小编就为大家分享一篇Redis的配置、启动、操作和关闭方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-05-05
  • Redis教程(十三):管线详解

    Redis教程(十三):管线详解

    这篇文章主要介绍了Redis教程(十三):管线详解,本文讲解了请求应答协议和RTT、管线(pipelining)、Benchmark等内容,需要的朋友可以参考下
    2015-05-05

最新评论