Redis的值有5种数据结构,不同数据结构的使用场景是什么?

文章目录

  • 字符串
    • 缓存
    • 计数
    • 共享Session
    • 限速
  • 哈希
    • 缓存
  • 列表
    • 消息队列
    • 文章列表
    • 队列
    • 有限集合
  • 集合
    • 标签
    • 抽奖
    • 社交需求
  • 有序集合
    • 排行榜系统

字符串

缓存

(1)使用原生字符类型缓存
优点:简单直观,每个属性都支持更新操作
缺点:占用过多的键、内存占用量较大,同时用户内聚性比较差。
(2)使用序列化字符串类型缓存
优点:简化编程,如果合理地使用序列化可以提高内存的利用效率。
缺点:序列化和反序列化有一定的开销,同时每次更新属性都需要把全部数据取出来,反序列化,更新后在序列化存到Redis中。

计数

许多应用都会使用Redis作为计数的基础工具。它可以实现快速计数、查询缓存等功能,同时数据可以异步落到其它数据源。比如视频播放系统使用Redis作为视频播放计数的基础组件,用户每播放一次视频,相应的视频播放数就会自增1。

long incrVideoCounter(long id){key = "video:playCount:" + id;return redis.incr(key);
}

图解mysql专栏:count( * )这么慢,我该怎么办?

Redis计数和数据库同步的问题:

以InnoDB为存储引擎的数据库,由于MVCC机制,count(*)计数时需要一行一行的读取,判断是否对当前事务可见, 然后计数加1或者不加。所以当数据量很大时,执行count(*)的效率很慢。

假设某个网站有这样一个页面,要显示数据库中某张操作记录表的总数,同时还要显示最新的100条记录。由于count(*)的效率很慢,我们可以用Redis来计数,如果这张操作记录表插入了一行,Redis计数就加1,删除了一行,Redis计数减1。

我们用一张表模拟两个事务的操作过程,事务B负责这个页面的业务查询,即获取Redis计数和操作表中最新的100条记录,事务A向表中插入一条记录,Redis计数加1

由于多线程中,线程的执行顺序是不确定的,如果事务B的代码在t2时刻执行,则查询到底100条记录里面有最新的插入记录,但redis计数却没变。

时刻事务A事务B
t0
t1向操作表中插入一条记录
t2读取Redis计数
获取表中最新的100条记录
t3Redis记录加1

如果事务A变化一下添加记录的执行过程,先去Redis计数加1,再向操作表中执行插入操作。事务B在t2时刻执行,此时该页面显示的情况是,Redis的计数变了,但是查询到的100行记录里面没有最新的改变

时刻事务A事务B
t0
t1Redis记录加1
t2读取Redis计数
获取表中最新的100条记录
t3向操作表中插入一条记录

以上两种情况,对于事物B来说,查计数值和“最近的100条记录”看到的结构,逻辑上是不一致的。

共享Session

查看我写的另一篇博客
什么是Redis共享Session?

限速

很多应用出了安全考虑,会在每次进行登录的时候,让用户输入手机验证码,从而确定是否是用户本人。但是为了短信验证接口不被频繁访问,会限制用户每分钟获取验证码的频率,例如一分钟不能超过5次。

此功能可以通过Redis来实现:

phoneNum = "173xxxxxxxx";
key = "shortMsg:limit:" + phoneNum;
// 将这个key的生命周期设置为60s,也就是1分钟
// key初始时的次数是1
isExists = redis.set(key,1,"EX 60","NX");
if(isExists != null || redis.incr(key) <= 5){// 第一次创建这个键// 或者// 不是第一次创建键,但 key 对应的次数小于等于5// 通过
}else{// 限速
}

哈希

缓存

优点:简单直观,如果合理可以减少内存空间的使用。
缺点:要控制哈希在ziplist和hashtable两种内部编码的转换,hashtable会消耗更多内存。

列表

消息队列

如图所示,Redis的lpush+brpop命令组合即可实现阻塞队列,生产者使用lpush命令从列表左侧插入元素,多个消费者使用brpop命令阻塞式地抢列表尾部的元素。
在这里插入图片描述

文章列表

每个用户有属于自己文章列表,现需要分页展示文章列表。此时可以考虑使用列表,因为列表是索引有序的,可以支持按照索引范围获取元素。
(1)每篇文章用哈希类型存储,例如每篇文章有一些属性:title、time、author、content、isbn、money等

hmset acticle:1 title xx time 1476536196 content xxxx ...
...
hmset acticle:k title xx time 1476536196 content xxxx ...

使用类似这样的命令构造k篇文章的数据
在这里插入图片描述

(2)向用户添加文字列表

lpush user:1:acticles article:1 article:3 ...
...
lpush user:n:acticles article:2 article:4

每个用户有自己的一个文章列表
在这里插入图片描述
(3)分页获取用户文章列表,例如通过下面的伪代码获取用户id=1的前10篇文章:

articles = lrange user:1:articles 0 9
for (article : articles){hgetall {article}
}

在这里插入图片描述
使用列表类型保存或获取文章列表会存在两个问题:

  1. 如果每次分页获取的文章数目较多,需要执行多次hgetall操作,此时可以考虑使用Pipeline批量获取。或者考虑将文章数据序列化为字符串类型,使用mget来获取。
  2. 分页获取文章列表时,lrange命令在列表的两端性能比较好,但如果列表较大,lrange获取列表中间元素性能会变差,此时可以考虑将列表做二级拆分。或者使用Redis quciklist内部编码实现。

lpush + lpop = Stack

队列

lpush + rpop = Queue

有限集合

lpush + ltrim = Capped Collection

集合

标签

一个用户可能对娱乐、体育比较感兴趣,另一个用户可能对历史、新闻比较感兴趣,这些兴趣点就是标签。有了标签之后我们可以得到:

  • 喜欢同一个标签的人(站在标签角度)
  • 两个或多个用户共同喜欢的标签(站在用户角度)

这些数据对于用户体验,以及增强用户黏度比较重要。

例如,一个电子商务的网站会对不同标签的用户做不同类型的推荐,比如对数码产品感兴趣的人,在各个页面或通过邮件的形式给他们推荐最新的数码产品,通常会为网站带来更多的收益。

下面使用集合类型实现标签的若干功能
(1)给用户添加标签

SADD user:1:tags tag1 tag2 tag3
SADD user:2:tags tag2 tag4 tag5
SADD user:3:tags tag1 tag3 tag5
SADD user:4:tags tag2 tag3 tag4
SADD user:5:tags tag1 tag4 tag5
SADD user:6:tags tag3 tag4 tag5

在redis种可以使用lua脚本一次性执行这命令:

EVAL "
redis.call('SADD', 'user:1:tags', 'tag1', 'tag2', 'tag3')
redis.call('SADD', 'user:2:tags', 'tag2', 'tag4', 'tag5')
redis.call('SADD', 'user:3:tags', 'tag1', 'tag3', 'tag5')
redis.call('SADD', 'user:4:tags', 'tag2', 'tag3', 'tag4')
redis.call('SADD', 'user:5:tags', 'tag1', 'tag4', 'tag5')
redis.call('SADD', 'user:6:tags', 'tag3', 'tag4', 'tag5')
" 0

(2) 给标签添加用户

SADD tag1:users user:1 user:3 user:5
SADD tag2:users user:1 user:2 user:4
SADD tag3:users user:1 user:3 user:4
SADD tag4:users user:2 user:4 user:5 user:6
SADD tag5:users user:2 user:3 user:5 user:6

在redis种可以使用lua脚本一次性执行这命令:

EVAL "
redis.call('SADD', 'tag1:users', 'user:1', 'user:3', 'user:5')
redis.call('SADD', 'tag2:users', 'user:1', 'user:2', 'user:4')
redis.call('SADD', 'tag3:users', 'user:1', 'user:3', 'user:4')
redis.call('SADD', 'tag4:users', 'user:2', 'user:4', 'user:5', 'user:6')
redis.call('SADD', 'tag5:users', 'user:2', 'user:3', 'user:5', 'user:6')
" 0

(3)删除用户下标签

srem user:1:tags tag1 tag2

(4)删除标签下的用户

srem tag1:users user:1
srem tag2:users user:1

(1)和(2)尽量放在一个事务中执行。
(3)和(4)尽量放在一个事务中执行。

(5)获取喜欢同一个标签的人

 smembers tag1:users

在这里插入图片描述
(6)获取user:1与user:2共同喜欢的标签

sinter user:1:tags user:2:tags

在这里插入图片描述
在这里插入图片描述

抽奖

spop/srandmember = Random item

社交需求

sadd + sinter = Social Graph

有序集合

排行榜系统

比如视频网站需要对用户上传的视频做排行榜,榜单的维度可能是多方面的,可能按照以下几个维度排名:

  • 时间
  • 播放数量
  • 获得的赞数

以获得的赞数为例,记录每天用户上传的视频的排行榜。

(1)视频添加用户赞数
例如用户mike上传了一个视频,并获得了3个赞,可以使用有序集合的zadd和zincrby功能:

zadd user:ranking:2024_03_12 3 mike

如果之后再获得一个赞,就可以使用zincrby:

zincrby user:ranking:2024_03_12 1 mike

(2)将用户移除榜单
由于各种原因(如用户注销、用户作弊)需要将用户删除,此时需要将用户从榜单中删除掉,可以使用zrem。例如删除榜单中的tom用户

zrem user:ranking:2024_03_12 tom

(3)显示榜单Top10
为了模拟这个功能,我用lua脚本创建了20条模拟数据:

EVAL "
redis.call('ZADD', 'user:ranking:2024_03_12', 3, 'mike')
redis.call('ZADD', 'user:ranking:2024_03_12', 5, 'jack')
redis.call('ZADD', 'user:ranking:2024_03_12', 7, 'tom')
redis.call('ZADD', 'user:ranking:2024_03_12', 9, 'curt')
redis.call('ZADD', 'user:ranking:2024_03_12', 11, 'dexter')
redis.call('ZADD', 'user:ranking:2024_03_12', 13, 'bert')
redis.call('ZADD', 'user:ranking:2024_03_12', 15, 'christian')
redis.call('ZADD', 'user:ranking:2024_03_12', 17, 'cecil')
redis.call('ZADD', 'user:ranking:2024_03_12', 19, 'charles')
redis.call('ZADD', 'user:ranking:2024_03_12', 21, 'bill')
redis.call('ZADD', 'user:ranking:2024_03_12', 23, 'cathy')
redis.call('ZADD', 'user:ranking:2024_03_12', 25, 'crystal')
redis.call('ZADD', 'user:ranking:2024_03_12', 27, 'elaine')
redis.call('ZADD', 'user:ranking:2024_03_12', 29, 'ellie')
redis.call('ZADD', 'user:ranking:2024_03_12', 31, 'hortensia')
redis.call('ZADD', 'user:ranking:2024_03_12', 33, 'kit')
redis.call('ZADD', 'user:ranking:2024_03_12', 35, 'lori')
redis.call('ZADD', 'user:ranking:2024_03_12', 37, 'marian')
redis.call('ZADD', 'user:ranking:2024_03_12', 39, 'lesley')
redis.call('ZADD', 'user:ranking:2024_03_12', 41, 'thirza')
" 0

根据或赞数排序,取前或赞数量前10个用户,可以使用zrevrange命令,从高到地返回成员:

 zrevrange user:ranking:2024_03_12 0 9

在这里插入图片描述
(4)展示用户信息、用户分数以及用户排名

假设用户信息保存再哈希类型中,用户分数和用户排名可以用zscore和zrank两个命令:

hgetall user:info:tom
zscore user:ranking:2024_03_12 mike
zrank user:ranking:2024_03_12 mike

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

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

相关文章

如何在本地搭建集成大语言模型Llama 2的聊天机器人并实现无公网IP远程访问

文章目录 1. 拉取相关的Docker镜像2. 运行Ollama 镜像3. 运行Chatbot Ollama镜像4. 本地访问5. 群晖安装Cpolar6. 配置公网地址7. 公网访问8. 固定公网地址 随着ChatGPT 和open Sora 的热度剧增,大语言模型时代,开启了AI新篇章,大语言模型的应用非常广泛&#xff0c;包括聊天机…

JAVAEE之Cookie/Session

1.Cookie HTTP 协议自身是属于 "无状态" 协议. "无状态" 的含义指的是: 默认情况下 HTTP 协议的客户端和服务器之间的这次通信, 和下次通信之间没有直接的联系. 但是实际开发中, 我们很多时候是需要知道请求之间的关联关系的. 例如登陆网站成功后, 第二…

自定义树形筛选选择组件

先上效果图 思路&#xff1a;刚开始最上面我用了el-input&#xff0c;选择框里面内容用了el-inputel-tree使用&#xff0c;但后面发现最上面那个可以输入&#xff0c;那岂不是可以不需要下拉就可以使用&#xff0c;岂不是违背了写这个组件的初衷&#xff0c;所以后面改成div自定…

基于springboot+vue实现的小区物业管理系统

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 技术选型 【后端】&#xff1a;Java 【框架】&#xff1a;spring…

yolov9文献阅读记录

本文记录了yolov9文献的阅读过程&#xff0c;对主要内容进行摘选翻译&#xff0c;帮助理解原理和应用&#xff0c;包括摘要、主要贡献、网络结构、主要模块&#xff0c;问题描述和试验对比等内容。 文献摘要前言摘选主要贡献相关工作可逆性结构辅助监督 问题描述信息瓶颈原理可…

Linux 恶意软件“Migo”针对 Redis 进行加密劫持攻击

安全研究人员遇到了一种新的加密劫持活动&#xff0c;该活动使用一种名为 Migo 的新恶意软件&#xff0c;该恶意软件针对 Linux 主机上的 Redis 服务器。在 Cado Security 研究人员注意到在野外利用 Redis 系统的新命令后&#xff0c;该活动曝光了。 初始访问 根据 Cado secu…

传统模型用腻了?GCN图卷积神经网络一键实现西储大学轴承故障诊断!发文新思路!

​ 声明&#xff1a;文章是从本人公众号中复制而来&#xff0c;因此&#xff0c;想最新最快了解各类智能优化算法及其改进的朋友&#xff0c;可关注我的公众号&#xff1a;强盛机器学习&#xff0c;不定期会有很多免费代码分享~ 目录 数据介绍与故障诊断讲解 1.数据…

实时获取 Pacific Time Zone (太平洋时区) 时间

实时获取 Pacific Time Zone [太平洋时区] 时间 1. Google -> Pacific Time2. Pacific Time - exact time nowReferences 1. Google -> Pacific Time 2. Pacific Time - exact time now https://time.is/zh/PT References [1] Yongqiang Cheng, https://yongqiang.blog…

原理图设计的通用规范

原理图各页内容依次为&#xff1a;封面、目录、电源、时钟、CPU、存储器、逻辑、背板&#xff08;母板&#xff09;接口等。 原理图上所有的文字方向应该统一&#xff0c;文字的上方应该朝向原理图的上方&#xff08;正放文字&#xff09;或左方&#xff08;侧放文字&#xff…

文件操作详解(二)

目录 一.文件的顺序读写1.顺序读写函数&#xff08;适合于所有的流&#xff09;1.1 fgetc(读字符)1.2 fputc(写字符)1.3 fgets(读字符串)1.4 fput(写字符串)1.5 fscanf(格式化地读)1.6 fprintf(格式化地写) 2.顺序读写函数&#xff08;只适用于文件流&#xff09;2.1 fread(二进…

蓝桥杯嵌入式学习笔记(9):RTC程序设计

目录 前言 1. RTC介绍 2. 使用CubeMx进行源工程配置 3. 代码编程 3.1 准备工作 3.2 进行bsp_rtc.h编写 3.3 进行bsp_rtc.c编写 3.4 main.c编写 3.4.1 头文件引用 3.4.2 变量声明 3.4.3 子函数声明 3.4.4 函数实现 3.4.5 main函数编写 4. 代码实验 5. 总结 前言 因本人备赛蓝…

分布式链路追踪与云原生可观测性

分布式链路追踪系统历史 Dapper, a Large-Scale Distributed Systems Tracing Infrastructure - Google Dapper&#xff0c;大规模分布式系统的跟踪系统大规模分布式系统的跟踪系统&#xff1a;Dapper设计给我们的启示 阿里巴巴鹰眼技术解密 - 周小帆京东云分布式链路追踪在金…

WPS二次开发专题:如何获取应用签名SHA256值

作者持续关注WPS二次开发专题系列&#xff0c;持续为大家带来更多有价值的WPS开发技术细节&#xff0c;如果能够帮助到您&#xff0c;请帮忙来个一键三连&#xff0c;更多问题请联系我&#xff08;QQ:250325397&#xff09; 在申请WPS SDK授权版时候需要开发者提供应用包名和签…

Jmeter各组件超详细介绍

1、JMeter和Loadrunner的区别&#xff1f; 2、JMeter如何开发脚本的&#xff1f;强化脚本的技术&#xff1f; 代理服务器录制脚本&#xff0c;Fiddler录制脚本&#xff0c;Badboy录制脚本&#xff0c;根据API&#xff0c;手写脚本&#xff0c;根据抓包&#xff0c;手写脚本。 …

docker版Elasticsearch安装,ik分词器安装,用户名密码配置,kibana安装

1、安装es和ik分词器 创建映射目录并赋予权限&#xff1a; mkdir -p /docker_data/elasticsearch/conf mkdir -p /docker_data/elasticsearch/data mkdir -p /docker_data/elasticsearch/plugins chmod -R 777 /docker_data/elasticsearch编写配置文件&#xff1a; vi /dock…

数据结构—红黑树

红黑树介绍 红黑树&#xff08;Red Black Tree&#xff09;是一种自平衡二叉查找树。由于其自平衡的特性&#xff0c;保证了最坏情形下在 O(logn) 时间复杂度内完成查找、增加、删除等操作&#xff0c;性能表现稳定。 在 JDK 中&#xff0c;TreeMap、TreeSet 以及 JDK1.8 的 …

【C++】C++中的list

一、介绍 官方给的 list的文档介绍 简单来说就是&#xff1a; list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。list的底层是双向链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0c;在节点中…

帝国cms自适应html5成语大全/成语查询/成语接龙网站源码整站模板moretag插件带采集

(购买本专栏可免费下载栏目内所有资源不受限制,持续发布中,需要注意的是,本专栏为批量下载专用,并无法保证某款源码或者插件绝对可用,介意不要购买!购买本专栏住如有什么源码需要,可向博主私信,第二天即可发布!博主有几万资源) 帝国cms自适应html5成语大全/成语查询…

STM32-04基于HAL库(CubeMX+MDK+Proteus)中断案例(按键中断扫描)

文章目录 一、功能需求分析二、Proteus绘制电路原理图三、STMCubeMX 配置引脚及模式&#xff0c;生成代码四、MDK打开生成项目&#xff0c;编写HAL库的按键检测代码五、运行仿真程序&#xff0c;调试代码 一、功能需求分析 在完成GPIO输入输出案例之后&#xff0c;开始新的功能…

Plonky2.5:在Plonky2中验证Plonky3 proof

1. 引言 Plonky2.5为QED Protocol团队主导的项目&#xff0c;定位为&#xff1a; 在Plonky2 SNARK中验证Plonky3 STARK proof。 从而实现Plonky系列的递归证明。 开源代码实现见&#xff1a; https://github.com/QEDProtocol/plonky2.5https://github.com/Plonky3/Plonky3&a…