Redis各类数据结构应用场景总结

news2024/11/19 9:26:28

Redis各类数据结构应用场景总结

  • 引言
  • String
    • 应用场景
  • List
    • 应用场景
  • Hash
    • 应用场景
  • Set
    • 应用场景
  • ZSet
    • 应用场景
  • 小结


引言

实际面试过程中更多看重的是对Redis相关数据结构的活学活用,同时也可能会引申出Redis相关底层数据结构原理的实现,笔者最近面试过程中对这块内容有点生疏,所以本文也是为了笔者个人查漏补缺所写。

文章内容很短,大家可放心食用,同时欢迎各位在评论区进行补充。

在线Redis命令练习


String

当value是字符串类型时,根据字符串格式的不同,可以分为三类:

  • string : 普通字符串
  • int : 整数类型 , 可以做自增,自减操作
  • float : 浮点类型 , 可以做自增,自减操作

但是不论是何种格式,底层都是采用字节数组形式存储,只不过是编码方式不同,如下图所示:
在这里插入图片描述
常见命令:

set k1 v1 [NX | EX expireTime ]

get k1

mset k1 v1 k2 v2

mget k1 k2

incr count  (val类型必须是int类型)

incrby count 2 

incrbyfloat floatCount 1.5 

setnx k3 v3

setex k4 v4 expireTime

# string 中每个位的操作
SETBIT key offset value   # 将指定位置设置为0或1
GETBIT key offset        # 获取指定位置的bit值   
BITCOUNT key [start end]  # 统计bitmap中值为1的bit位数量
BITOP operation destkey key [key ...] # 对指定的多个 key 进行位操作,并将结果保存在 destkey 中。操作可以是 AND、OR、XOR、NOT

应用场景

String数据结构的应用场景如下图所示,大家可以右键在新标签页中打开浏览:
请添加图片描述
这里针对String类型作为bitmap的应用场景进行特殊说明:

  • 统计当前在线用户数量
# setbit onlineUsers userId 1

> setbit onlineUsers 1 1
0
> setbit onlineUsers 2 1
0
> setbit onlineUsers 10 1
0
> setbit onlineUsers 25 1
0
> bitcount onlineUsers
4
  • 计算连续2天都在线的用户
> setbit online_one 1 1
0
> setbit online_two 1 1
0
> setbit online_two 2 1
0
> setbit online_two 3 1
0
> bitop and both_two_online online_one online_two
1
> bitcount both_two_online
1
  • 签到
# 签到    sign:userId:yyyy-MM
> setbit sign:1:2023-08 27 1
0
> setbit sign:1:2023-08 28 1
0
> setbit sign:1:2023-08 29 1
0
> bitcount sign:1:2023-08
3
  • 连续签到
    • 获取当前key对应的bitmap, 从后往前定位到bitmap中出现的第一个1,从该位置开始依次与1进行与运算,如果结果不为0,则累加计数器,然后右移一位,然后再与1进行与运算,重复以上过程直到与运算结果为0
    • 此时计数器的结果即为当前用户连续签到的天数

List

当val类型为List时,其可能存在以下三种编码情况:

  • Redis 3.2 版本之前 ,采用 ZipList 和 LinkedList 来实现 List ,当元素数量小于 512 并且 元素大小小于 64 字节时采用 ZipList 编码 , 超过则采用 LinkedList 编码 。
  • Redis 3.2 版本之后 , Redis 统一采用 QuickList 来实现 List

常见命令:

LPUSH key element ... # 向列表左侧插入一个或多个元素
LPOP key  # 移除并返回列表左侧第一个元素,没有则返回nil
RPUSH key element ... # 向列表右侧插入一个或多个元素
RPOP key  # 移除并返回列表右侧的第一个元素
LRANGE key start end # 返回该范围内的所有元素
BLPOP , BRPOP  # 没有元素时超时阻塞等待 , 而不是直接返回nil

redis提供的List列表类型其实可以用来替换我们常见的三种数据结构:

  • 栈 : 出入口在同一边
  • 队列 : 出入口不在同一边
  • 阻塞队列 : 增加超时阻塞等待

应用场景

  • 消息队列

    • 基于List的阻塞模式实现阻塞队列,利用阻塞队列来实现一个简易的生产者-消费者模型
      • 优点: 可利用Redis的持久化机制,确保数据一定程度上可靠性。 消息按照投递时间顺序在队列中排序。
      • 缺点: 无法避免消息丢失,只支持单消费者
    • Pub/Sub 可以用来实现发布订阅模式 ,支持多生产,多消费 ;缺点就是不支持数据持久化,无法避免消息丢失,消息堆积有上限,超出时数据丢失。
    • Stream流实现消息队列
  • 用户消息时间线 – timeline

    • list中的元素按照插入的先后顺序排序

Hash

当val类型为Hash时,可能存在以下几种编码情况:

  • 数据量比较小时,默认采用ZipList编码,ZipList中相邻的两个entry分别保存field和value
  • 数据量比较大时,采用Dict实现,触发条件有两个:
    • ZipList中的元素数量超过了默认的512个
    • ZipList中的任意entry大小超过了默认规定的64字节大小

应用场景

String 和 Hash 数据结构都可以用来存储对象信息,对于String数据类型来说,通常存储JSON序列化后的对象信息,当需要频繁修改对象某个属性时,这种方式就不太合适了。

Hash类型可以针对某个属性单独修改,没有序列化,也无需修改整个对象,如果某个对象内部属性需要频繁修改,如: 商品价格,销量,关注数,评价数等,可以考虑hash类型。如果需要单独读取对象内部某几个属性,也可以考虑Hash类型。


Set

当val类型为Set时,其底层编码有以下两种情况:

  • 当存储的所有数据都是整数时,并且元素数量不超过set-max-intset-entries时,Set会采用IntSet编码
  • 大部分情况下,Set都是采用Dict实现,其中使用Dict中的Key来存储元素,val统一为nil

Redis中的Set数据结构具备什么特点:

  • 无序性
  • 唯一性
  • 支持求交集,并集和差集

常用命令:

sadd key member ... 
srem key member ... # 删除
scard key   # 返回set中元素的个数
sismember key member # 判断元素是否存在当前set中
smembers # 获取set中所有元素
sinter k1 k2 # 求k1和k2关联的两个set集合的交集
sdiff k1 k2 # 求差集
sunion k1 k2 # 求并集

set 和 zset 是面试重点,这两个数据结构大家要尤其注意其应用场景。


应用场景

  • 点赞 — 但是每个人只能点赞一次 , 同时记录哪些人点了赞
# 点赞此篇文章
> sadd like:t0001 u0001
(integer) 1
> sadd like:t0001 u0001
(integer) 0
> sadd like:t0001 u0002
(integer) 1
> sadd like:t0001 u0003
(integer) 1
# 返回当前文章点赞总数
> scard like:t0001
3
# 用户u0002取消点赞
> srem like:t0001 u0002
1
> scard like:t0001
2
# 判断用户u0002是否点赞了当前文章
> sismember like:t0001 u0002
(integer) 0
# 获取点赞当前文章的所有用户
> smembers like:t0001
1) "u0003"
2) "u0001"
  • 存放商品标签
# 为当前物品添加标签信息
> sadd tags:i5001 物美价廉
(integer) 1
> sadd tags:i5001 4K高清
(integer) 1
# 返回当前物品具备的所有标签信息
> smembers tags:i5001
1) "4K高清"
2) "物美价廉"
  • 商品筛选
# 为每个商品添加标签
> sadd 苹果手机 价格昂贵
(integer) 1
> sadd 苹果手机 流畅好用
(integer) 1

> sadd 小米手机 价格实惠
(integer) 1
> sadd 小米手机 流畅好用
(integer) 1

> sadd 华为手机 价格适中
(integer) 1
> sadd 华为手机 流畅好用
(integer) 1

# 获取三者的共同点
> sinter 苹果手机 小米手机 华为手机
1) "流畅好用"
# 获取苹果手机相较于小米手机的不同点
> sdiff 苹果手机 小米手机
1) "价格昂贵"
# 获取小米手机相较于苹果手机的不同点
> sdiff 小米手机 苹果手机
1) "价格实惠"
# 获取苹果手机和小米手机的共同点
> sunion 苹果手机 小米手机
1) "价格实惠"
2) "流畅好用"
3) "价格昂贵"
  • 共同关注
# 用户1关注了用户2,3,4
> sadd 1:follow 2
(integer) 1
> sadd 1:follow 3
(integer) 1
> sadd 1:follow 4
(integer) 1
# 用户2关注了用户3,1
> sadd 2:follow 3
(integer) 1
> sadd 2:follow 1
(integer) 1
# 用户2的粉丝有用户1
> sadd 2:fans 1
(integer) 1
# 用户1的粉丝有用户2
> sadd 1:fans 2
(integer) 1

# 求用户1和用户2共同关注的人
> sinter 1:follow 2:follow
1) "3"

# 求用户1可能认识的人 -- 需要从返回结果中移除用户1自身
> sdiff 2:follow 1:follow
1) "1"

# 求用户2可能认识的人 -- 需要从返回结果中移除用户2自身
> sdiff 1:follow 2:follow
1) "2"
2) "4"

ZSet

当val类型为ZSet时,其具备以下特性:

  • 在set的基础上,需要给每个member增加一个socre , 然后zset有序集合内部按照socre对member进行排序
  • member的唯一性保持不变
  • 可以查询member对应的socre

ZSet结构可能存在两种编码类型:

  • 当元素数量不多时,采用ziplist编码
    • 元素数量小于zset_max_ziplist_entries , 默认为128
    • 每个元素都小于zset_max_ziplist_value 字节,默认为64
  • 否则采用SkipList + Dict 编码,利用SkipList的有序性实现排序和范围查找 , 利用Dict实现快速定位和去重

Redis提供的ZSet功能很类似Java中的TreeMap:

  • ZSet基于单独为每个key指定的score进行升序排序 , 而TreeMap默认基于key进行升序排列,当然我们可以自定义排序规则
  • ZSet 底层基于SkipList 进行排序 , TreeMap 基于红黑树进行排序
  • ZSet 只存储Key ,没有Val ,TreeMap 作为 map 既存储 Key 也存储 Val

常见命令:

zadd key score member 
zrem key member 
zscore key member # 获取每个member的分数
zcard key # 获取当前zset集合中的元素个数
zcount key min max # 统计score在指定范围内的所有元素个数
zincrby key incrment member # 为zset中某个member的socre增加指定大小
zrange key min max # 按照score排序后,根据索引获取指定范围内的元素
zrangebyscore key min max # 按照score排序后,根据score获取指定分数段内的元素
zdiff,zinter,zunion # 求差集,交集,并集

所有排名默认升序,如果要降序则在命令后添加REV即可。


应用场景

  • 排行榜
# 记录2023-8-24的热点信息排行榜 ,  依次添加每个信息 , 记录每个新闻的热点数
> zincrby hotNews:2023-8-24 30000 福岛核废水
30000.0
> zincrby hotNews:2023-8-24 25000 日本3天已连发5次地震
25000.0
> zincrby hotNews:2023-8-24 25000 ShowMaker谈LPL中单
25000.0
> zincrby hotNews:2023-8-24 23000 大脑在替熬夜负重前行
23000.0
> zincrby hotNews:2023-8-24 22000 俄罗斯海岸现恐怖怪鱼
22000.0

# 按照默认升序返回所有热点信息
> zrange hotNews:2023-8-24 0 -1
1) "俄罗斯海岸现恐怖怪鱼"
2) "大脑在替熬夜负重前行"
3) "ShowMaker谈LPL中单"
4) "日本3天已连发5次地震"
5) "福岛核废水"

# 按照降序返回
> zrevrange hotNews:2023-8-24 0 -1
1) "福岛核废水"
2) "日本3天已连发5次地震"
3) "ShowMaker谈LPL中单"
4) "大脑在替熬夜负重前行"
5) "俄罗斯海岸现恐怖怪鱼"

# 获取排名前三的热点信息 -- 降序返回并携带score热度值
> zrevrange hotNews:2023-8-24 0 2 withscores
1) "福岛核废水"
2) 30000.0
3) "日本3天已连发5次地震"
4) 25000.0
5) "ShowMaker谈LPL中单"
6) 25000.0
  • 设计一个游戏排行榜,有三个关键信息: 用户id,score ,time ; 要求排行榜先按照用户得分降序排列,再分数相同的情况下,再按照time升序排列;
    • key 如何设计
    • member 如何设计
    • 要求: 返回排名前三的用户 id,score 和 time

这是一道真实的面试题,笔者当时一时绕进去了,没给出正确答案,此处给出我自己的理解:

  • member的得分如何设计 : score左移32位,然后或上32的时间戳 — 这里的时间戳需要翻转一下

这里出于简单,就不翻转了,排序规则改为按照得分降序,分数相同的情况下,再按照time降序排列

在这里插入图片描述

  • key如何设计: 假设这里求的是排行日榜 —> rank:2023-8-24
  • member如何设计: 存放用户id即可

下面给出返回排名前三的用户 id,score 和 time 的具体命令实现:

# 排行榜添加条目信息
# 用户1得分80,时间戳为1
> zadd rank:2023-8-24 343597383681 1
(integer) 1
# 用户2得分75,时间戳为1
> zadd rank:2023-8-24 322122547201 2
(integer) 1
# 用户3得分90,时间戳为1
> zadd rank:2023-8-24 386547056641 3
(integer) 1
# 用户4得分75,时间戳为2
> zadd rank:2023-8-24 322122547202 4
(integer) 1

# 按照默认升序返回整个排行榜信息
> zrange rank:2023-8-24 0 -1
1) "2" # 用户2得分75,时间戳为1
2) "4" # 用户4得分75,时间戳为2
3) "1" # 用户1得分80,时间戳为1
4) "3" # 用户3得分90,时间戳为1

# 返回排名前三的用户信息
> zrevrange rank:2023-8-24 0 2 withscores
1) "3"
2) 386547056641.0
3) "1"
4) 343597383681.0
5) "4"
6) 322122547202.0

用户id可以从返回的member中直接获取到,而当前用户得分和time可以通过位运算获取到:

# score + 时间戳(32bit)
386547056641 & (2的31次方 - 1) = 8589934591 --> 获取时间戳结果为1
386547056641 >> 32 = 90

在这里插入图片描述


  • 优先级队列
    • 我们可以利用zset按照score排序的这个特性,将score设置为任务的优先级,将其插入zset集合中,这样zset就变成了优先级队列

在这里插入图片描述
我们可以利用优先级队列实现延迟队列,只需要将优先级定义为任务执行的时间戳即可,然后应用线程不断循环,直到发现队列头部第一个任务到期了,则从队列移除并执行任务。


小结

关于Redis数据结构,大家需要重点关注String,Set和ZSet的应用,特别是Set和ZSet,绝对是面试场景题的重要考点。

本文只列举了部分笔者目前所能想到的内容,各位大佬如果有补充,可以在评论区留言。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/936937.html

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

相关文章

【ARP欺骗】嗅探流量、限速、断网操作

【ARP欺骗】 什么是ARP什么是ARP欺骗ARP欺骗实现ARP断网限制网速嗅探流量 什么是ARP ARP(Address Resolution Protocol,地址解析协议)是一个TCP/IP协议,用于根据IP地址获取物理地址。在计算机网络中,当一个主机需要发…

查看windows当前占用的所有端口、根据ipt终止任务进程、OS、operatingSystem

文章目录 查询端口查询指定端口根据进程pid查询进程名称查看所有进程名称根据pid终止任务进程根据进程名称终止任务 查询端口 netstat -ano查询指定端口 netstat -ano | findstr "80"根据进程pid查询进程名称 tasklist | findstr "660"查看所有进程名称 ta…

从哈希表到红黑树:探讨 epoll 是如何管理事件的?

揭开pkill的秘密:在Linux中杀死进程的完整指南 一、引言二、 传统事件管理的局限性三、epoll 概述3.1、epoll 的基本概念和工作原理3.2、epoll 在 Linux 内核中的实现方式 四、哈希表在事件管理中的挑战五、 红黑树在 epoll 中的应用六、epoll 中的事件注册与触发七…

TCP 和 UDP 的区别、TCP 是如何保证可靠传输的?

先来介绍一些osi七层模型 分为应用层、表示层、会话层、运输层、网络层、链路层、物理层。 应用层(数据):确定进程之间通信的性质以及满足用户需要以及提供网络和用户应用,为应用程序提供服务,DNS,HTTP,HTTPS&#xf…

strcpy函数

目录 函数介绍: 函数声明: 具体使用: 情况一: 情况二: 注意事项: 1.源字符必须以 \0结束: 2.目标空间必须足够大,以确保能放源字符串: 3.目标空间必须可变&…

阿里2面:你们部署多少节点?1000W并发,当如何部署?

说在前面 在40岁老架构师 尼恩的读者交流群(50)中,最近有小伙伴拿到了一线互联网企业如阿里、网易、有赞、希音、百度、网易、滴滴的面试资格,遇到一几个很重要的面试题: 1000W并发,需部署多少个节点?如何觉得部署多少…

<八> objectARX开发:动态拖动Jig创建自定义实体

1、介绍 接上一篇文章,在某些情况下,CAD中的实体对象初始参数并不是固定的,我们需要通过jig动态拖动方式来绘制自定义实体,下面就用一个简单的例子来介绍一下自定义实体动态绘制。   实体形状:包括实体夹点和文字夹点拖动实现。 2、效果 3、源码 static void RYMyGrou…

软考:中级软件设计师:无线网,网络接入技术,ipv6

软考:中级软件设计师:无线网 提示:系列被面试官问的问题,我自己当时不会,所以下来自己复盘一下,认真学习和总结,以应对未来更多的可能性 关于互联网大厂的笔试面试,都是需要细心准备的 &#x…

退出屏保前玩一把游戏吧!webBrowser中网页如何调用.NET方法

本文主要以 HackerScreenSaver 新功能的开发经历介绍 webBrowser中网页如何调用.NET方法的过程。 1. 背景 之前开源了一款名为 HackerScreenSaver 的 Windows 屏保程序。该程序具有模拟黑客炫酷界面的特点,用户可以将自定义的网页作为锁屏界面。不久前,…

1. import pandas as pd 导入库

【目录】 文章目录 1. import pandas as pd 导入库1. pandas库的概念2. 导入pandas库2.1 常规导入2.2 别名导入 3. 别名的作用4. 课堂练习 【正文】 1. import pandas as pd 导入库 【学习时间】 10分钟 1. pandas库的概念 pandas:熊猫panda的复数, …

基于体素形态学测量分析(VBM)的工具包比较及其在年龄预测中的应用

摘要 基于体素的形态学测量分析(VBM)通常用于灰质体积(GMV)的局部量化。目前存在多种实现VBM的方法。然而,如何比较这些方法及其在应用中的效用(例如对年龄效应的估计)仍不清楚。这会使研究人员疑惑他们应该在其项目中使用哪种VBM工具包。本研究以用户为中心&#…

【字节跳动青训营】后端笔记整理-4 | Go框架三件套之GORM的使用

**本人是第六届字节跳动青训营(后端组)的成员。本文由博主本人整理自该营的日常学习实践,首发于稀土掘金。 我的go开发环境: *本地IDE:GoLand 2023.1.2 *go:1.20.6 *MySQL:8.0 本文介绍Go框架三…

百万级数据导入导出

导入 引入easyexcel <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.3.2</version></dependency>总结有三种导出方式&#xff1a; 单行读取&#xff0c;单行导入。这种方案最简单…

涛然自得周刊(第06期):韩版苏东坡的突围

作者&#xff1a;何一涛 日期&#xff1a;2023 年 8 月 27 日 涛然自得周刊主要精选作者阅读过的书影音内容&#xff0c;不定期发布。历史周刊内容可以看这里。 电影 兹山鱼谱 讲述丁若铨因政治事件被贬黜到了遥远的黑山岛。来到岛上后&#xff0c;丁被大自然环境疗愈&#…

自定义温度显示控件(四) — 终结篇

概述 详细讲述自定义温度控件的实现 详细 前言 在之前的文章中&#xff0c;已经讲到了自定义温度显示控件一步步进化的历程&#xff0c;大家有兴趣的话可参考以下文章&#xff1a; 自定义温度显示控件(一) 自定义温度显示控件(二) 自定义温度显示控件(三) 今天讲温度实现效…

CPU深度解析

操作系统课程 计算机组成 ALU:计算单元(运算器)PC:pc寄存器存执行指令Registers:寄存器存数据MMU:控制器程序的构成:指令+数据 总线:一个程序读入内存,全是由0和1构成,从内存读取到cpu计算,需要通过总线。一段01数据段是指令还是数据是通过来源总线区分的。总线分…

农村农产品信息展示网站的设计与实现(论文+源码)_kaic

摘 要 随着软件技术的迅速发展,农产品信息展示的平台越来越多,传统的农产品显示方法将被计算机图形技术取代。这种网站技术主要把农产品的描述、农产品价格、农产品图片等内容&#xff0c;通过计算机网络的开发技术&#xff0c;在互联网上进行展示&#xff0c;然后通过计算机网…

9 串口通信(三)

9.4 USART串口数据包 HEX数据包 1&#xff09;固定包长&#xff0c;含包头包尾 例如陀螺仪的数据&#xff0c;需要XYZ坐标一起打包 2&#xff09;可变包长&#xff0c;含包头包尾 如果定义的包头包尾刚刚好也是数据&#xff0c;这样容易混淆&#xff0c;解决的办法&#x…

java-Optional 类详解

目录 前言 Optional的构造方法 Optional的相关方法介绍 isPresent用法&#xff1a; get用法&#xff1a; filter用法&#xff1a; orElse用法&#xff1a; orElseGet用法 orElseThrow用法 map用法 flatMap用法&#xff1a; 前言 Optional 类是java8的新特性&#xff0…