Redis中ZSet的具体使用

目录
  • 一、题目
  • 二、ZSet 简单使用
  • 三、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使用内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • redis zset实现滑动窗口限流的代码

    目录 限流 rediszset特性 滑动窗口算法 java代码实现 补充:RediszSet实现滑动窗口对短信进行防刷限流 前言 示例代码 限流 需求背景:同一用户1分钟内登录失败次数超过3次,页面添加验证码登录验证,也即是限流的思想. 常见的限流算法:固定窗口计数器:滑动窗口计数器:漏桶:令牌桶.本篇选择的滑动窗口计数器 redis zset特性 Redis 有序集合(sorted set)和集合(set)一样也是 string 类型元素的集合,且不允许重复的成员.不同的是每个元素都会关联一个

  • 基于Redis zSet实现滑动窗口对短信进行防刷限流的问题

    前言 主要针对目前线上短信被脚本恶意盗刷的情况,用Redis实现滑动窗口限流 public void checkCurrentWindowValue(String telNum) { String windowKey = CommonConstant.getNnSmsWindowKey(telNum); //获取当前时间戳 long currentTime = System.currentTimeMillis(); //1小时,默认只能发5次,参数smsWindowMax做成可配置项,配置到Na

  • Redis基本数据类型Zset有序集合常用操作

    目录 Redis数据类型Zset有序集合 一.zadd 二.zrange 三.zrevrange 四.zrangebyscore 五. zrem 六.zcard 七.zcount Redis数据类型Zset有序集合 有序集合和集合一样也是 string 类型元素的集合,且不允许重复的成员. 不同的是有序集合每个元素都会关联一个 double 类型的分数.redis 正是通过分数来为集合中的成员进行从小到大的排序. 有序集合的成员是唯一的,但分数(score)却可以重复. 集合是通过哈希表实现的,

  • Redis中ZSet的具体使用

    目录 一.题目 二.ZSet 简单使用 三.ZSet 结构 四.跳跃表 五.场景案例 一.题目 ZSet能用在哪些场景?跳表查找的过程,时间复杂度 二.ZSet 简单使用 举个例子,fruit-price 是一个有序集合键,这个有序集合以水果名为成员,水果价钱为分值,保存了 130 款水果的价钱: redis>ZADD fruit-price 5 "banana" redis>ZADD fruit-price 6.5 "cherry" redis>

  • Redis 中ZSET数据类型命令使用及对应场景总结(案例详解)

    目录 1.zadd添加元素 2.zrem 从有序集合key中删除元素 3.zscore 返回有序集合key中元素member的分值 4.zincrby 为有序集合key中元素增加分值 5.zcard获取有序集合key中元素总个数 6.zrange 正序获取分值范围内的元素 7.zrevrange 倒序获取集合元素 8.zrank获取有序集合中元素key的排名 9.zrangebyscore 获取有序集合中分数区间的元素 10.zcount 获取分值区间的元素数量 1.zadd添加元素 zadd

  • Redis中一些最常见的面试问题总结

    前言 经过长达一周的奔波和面试,电话面试,回首今天终于成功的入职了,总共面试了大概10家公司,包括阿里,京东,IBM等等,京东技术过了,学历因为非统招就被pass了,阿里面了2次电话面试就没下文了,估计是我当时最后提问题的时候减分了吧,其他的也有一些offer,不是不想去,就是了无音讯了,眼看年关将近,也由不得我挑挑拣拣了,就直接进了我现在这家公司,主要是感觉公司人不错,薪水这方面也就没有计较太多.好了,书归正文,今天小编就大家送上我精心准备的关于Redis方面的面试题,希望可以帮到还在求职路上

  • redis中数据类型命令整理

    redis是键值对的数据库,有5中主要数据类型: 字符串类型(string),散列类型(hash),列表类型(list),集合类型(set),有序集合类型(zset) 几个基本的命令: 函数 说明 keys * 获得当前数据库的所有键 
exists key [key ...] 判断键是否存在,返回个数,如果key有一样的也是叠加数 del key [key ...] 删除键,返回删除的个数 
type key 获取减值的数据类型(string,hash,list,set,zset) flush

  • redis中的数据结构和编码详解

    redis中的数据结构和编码:     背景: 1>redis在内部使用redisObject结构体来定义存储的值对象. 2>每种类型都有至少两种内部编码,Redis会根据当前值的类型和长度来决定使用哪种编码实现. 3>编码类型转换在Redis写入数据时自动完成,这个转换过程是不可逆的,转换规则只能从小内存编码向大内存编码转换.     源码: 值对象redisObject: typedef struct redisObject {                 unsigned ty

  • Python 抓取数据存储到Redis中的操作

    redis是一个key-value存储结构.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sorted set 有序集合)和hash(哈希类型),数据存储如下图分析 为了分别为ID存入多个键值对,此次仅对Hash数据进行操作,例子如下 import os,sys import requests import bs4 import redis #连接Redis r = redis.Redis(host='127

  • Redis中的String类型及使用Redis解决订单秒杀超卖问题

    本系列将和大家分享Redis分布式缓存,本章主要简单介绍下Redis中的String类型,以及如何使用Redis解决订单秒杀超卖问题. Redis中5种数据结构之String类型:key-value的缓存,支持过期,value不超过512M. Redis是单线程的,比如SetAll & AppendToValue & GetValues & GetAndSetValue & IncrementValue & IncrementValueBy等等,这些看上去像是组合命

  • SpringBoot使用Redis的zset统计在线用户信息

    统计在线用户的数量,是应用很常见的需求了.如果需要精准的统计到用户是在线,离线状态,我想只有客户端和服务器通过保持一个TCP长连接来实现.如果应用本身并非一个IM应用的话,这种方式成本极高. 现在的应用都趋向于使用心跳包来标识用户是否在线.用户登录后,每隔一段时间,往服务器推送一个消息,表示当前用户在线.服务器则可以定义一个时间差,例如:5分钟内收到过客户端心跳消息,视为在线用户. 在线用户统计的实现 基于数据库实现 最简单的办法,就是在用户表,添加一个最后心跳包的日期时间字段 last_act

  • Redis中常见的几种集群部署方案

    目录 前言 几种常用的集群方案 主从集群模式 全量同步 增量同步 举个栗子 哨兵机制 什么是哨兵机制 如何保证选主的准确性 如何选主 选举主节点的规则 哨兵进行主节点切换 切片集群 RedisCluster方案 哈希槽重新分配 避免HotKey 如何发现HotKey HotKey如何解决 避免BigKey BigKey存在问题 如何发现BigKey BigKey如何避免 BigKey如何删除 参考 前言 这里来了解一下,Redis 中常见的集群方案 几种常用的集群方案 主从集群模式 哨兵机制 切

  • Redis中有序集合的内部实现方式的详细介绍

    目录 有序集合的内部实现 以压缩列表作为内部实现 以跳跃表作为内部实现 内部实现的转换 总结 面试官:Redis中基本的数据类型有哪些? 我:Redis的基本数据类型有:字符串(string).哈希(hash).列表(list).集合(set).有序集合(zset). 面试官:有序集合的内部实现方式是什么? 我还沉浸在上一个问题的沾沾自喜中,顿时表情凝固了,手心开始冒出冷汗.“这个..没有太深入了解”,我支支吾吾的说到. 面试官:回去等消息吧. 这句话说的干净利落,然后就没有然后了.失败是成功的

随机推荐