学了redis不会实战?看这篇就够了
mhr18 2024-11-18 14:38 44 浏览 0 评论
学了redis不会实战?看这篇就够了
适用人群:熟悉redis的指令,但不了解使用场景的人群。
本文会详细描述各个场景的伪代码和对应的redis指令,至于为什么只是伪代码是因为每个语言操作redis的方式不一致,但是思想是一致的,只需要根据这个思想去找对应的api即可。
String 命令实战
String的命令主要是有三大运用场景:分别是分布式锁的运用,限流操作和业务缓存。
1.业务缓存
场景复现:某个活动即将在 11月11日开展。预期数据库的访问压力陡增。
解决方案:利用Redis,提前将要被多次访问的数据放入redis,做到“缓存预热”。让用户进入我们活动页面的时候,先去搜索缓存,不直接访问数据库,做到释放数据库的压力。
对应redis中的指令
set data "data"
get data
复制代码
伪代码
void cacheData(){
//1.从数据库中获取热点数据
//2.将此类数据序列化
//3.将 (data-data的id) 作为key,序列化的data作为value,利用string的set方法,存入redis。将活动的持续时间作为TTL
}
?
void getData(id){
//1.将data和id拼接作为key,根据key聪redis中根据string的get指令,进行查询。
//2.1查询到,直接返回
//2.2没有查询到,根据id去数据库中查找,如果有,就直接返回,并存入redis中。
}
复制代码
2.分布式锁
场景复现:多个服务去抢夺资源,有并发,线程安全问题。
解决方案:利用redis的set ... nx命令,实现分布式锁的效果。
为什么它叫分布式锁呢?其实是因为多个服务都可以连接到一个redis中,相当于,水库里的水很多,但是总要从一个管道里流出一样,这个管道就相当于控制,同一时间内,只有一部分水可以流出。
对应redis中的指令
set key value NX
复制代码
当目标key不存在的时候,才允许写入这个key,如果key已经存在,这个key就写不进去
伪代码
void tryLock(id){
//1.不断抢锁直到抢到
while(true){
//1.1.把 (update+传入的id)作为key,当前线程的名称作为value,使用nx指令,设置5s的过期时间(避免因为系统原因未能释放,则导致锁无法释放,这是兜底措施)
//1.2.1.如果1.1步骤失败,说明抢锁失败,进入下一次循环,继续抢锁。
//1.2.2.如果成功,就break,结束循环
}
//2.执行业务逻辑
//3.删除 (update+传入的id)的key的值,相当于释放锁。
}
复制代码
3.限流
场景复现:高并发场景下,抢购,秒杀等,流量峰值很高,但是后端业务的资源很有限。
解决方案:假如后端资源只够1000qps,那么我们可能就得对高于这个值的qps做限流,高于它的部分可能就得做降级处理了。
对应redis中的指令
set age 25
//返回:OK
INCR age
//返回:26
复制代码
伪代码
void limit(){
//1.取到当前的时间戳
Long now = currentTimeMilles()
//2.将当前服务名+now做为key,每打过来一个请求,就把这个value利用INCR指令+1。
//3.如果当前value > 1000 qps,那么就直接返回“请求限流”
//4.如果 value < 1000 qps,那么执行请求。
}
复制代码
List 命令实战
List命令主要是有三大运用场景:消息队列,提醒功能和热点列表。
但是由于消息队列的消息丢失问题很难处理,因此我们主要讲提醒功能和热点列表。
1.提醒功能
虽然说Redis的list可以实现消息队列的效果,但是利用Redis的list实现消息队列时候,在消息弹出,但是消费者没有消费前,消费者如果宕机,此条消息就会直接丢失。所以说,除非你的场景里,消息是可以接受丢失的,例如我们要说的提醒功能,不然尽量不要用它作为消息队列。
场景复现:我是一个掘金作者,当有掘友给我点赞的时候,我需要接收到点赞信息,并且给予反馈。
解决方案:后端在接受点赞消息后,将点赞人push到点赞List中,当我(掘金作者)登录网站的时候,前端给后端请求点赞的list。
对应Redis中的指令
//从右侧push
RPUSH 集合名称 集合元素
//从左侧遍历 下标1到下标2的所有元素
LRANGE 集合名称 下标1 下标2
//从左侧截断,下标1之前的所有元素,返回下标1到下标2的所有元素
LTRIM 集合名称 下标1 下标2
复制代码
伪代码
void likeList(){
?
String listName = "like-" + articleId
//开启一个线程,模拟用户端点赞,此时有10000人
for(int i=0;i<10000;i++){
//将listName作为list的key,用户id作为value,利用rpush指令,加入list
}
//再开启一个线程,模拟掘金作者端
while(true){
//1.利用LRANGE指令,拉取前1000个人的点赞。
//2.读取,并且展示。
//3.展示完成后,利用LTRIM指令,把第一步返回的userId的size大小作为下标1,-1作为下标2,执行该指令。相当于把前1000个userIds截断掉
}
}
复制代码
2.热点列表
其实上述的提醒功能的本质就是对list里面的消息进行消费,就是消息队列里的生产者和消费者模式。
List还有一个常见的模式就是热点列表,或者是微博上的话题列表等。
对应Redis中的指令
//从右侧push
RPUSH 集合名称 集合元素
//从左侧数下标,设置该下标对应的值
LSET 集合名称 下标 值
//从左侧遍历 下标1到下标2的所有元素
LRANGE 集合名称 下标1 下标2
复制代码
伪代码
//定时任务更新热点列表
void updateHotList(){
//1.从数据库中计算得出最火热的20条数据
//2.利用lset命令,根据下标更新当前李彪数据
}
//客户端拉取热点列表
//1.根据lrange命令找到所有的热点,返回
复制代码
Hash 命令实战
hash的数据结构我们知道,适用于存储键值对比较多的集合。下面我们以两个实战来讲解,分别是用户资料缓存和购物车的存储。
1.用户资料缓存
场景复现:我们知道,在复杂的系统中,c端用户的信息是非常多的,可能有几十个字段用来描述这个用户。且该信息访问的次数也是很多。
解决方案:放弃将用户信息序列化成字符串的方式,该用Hash方式进行存储。我们用手机电话作为key,用户信息对象作为value进行存储。并且对活跃用户进redis缓存,非活跃用户留在mysql中。
对应redis中的指令
//获取key下的所有hash键值对
hgetall key
//设置key ,hash的键值对
hset key k1 v1 k2 v2 k3 v3
复制代码
伪代码
void login(mobile){
//1.获取key
String key = "login:" + mobile
//2.根据hgetall指令获取该用户的所有信息
//3.1.如果返回的信息不为空
//3.1.1.如果value里没有标识符,直接返回脱敏后的用户信息
//3.1.2.如果value里有标识符,说明这个人没有注册却一直在登录,有想让我们缓存击穿的嫌疑,直接返回“未注册”即可
//3.2.如果为空
//3.2.1.从数据库中获取登录对象
//3.2.1.1.如果数据库返回是空,说明没有注册,将该key存入redis,value给一个标识符,标识它未注册(防止缓存击穿)。
//3.2.1.2.如果数据库返回不是空,就把这个用户数据利用Hallset指令,存入redis,说明他是热点用户。返回当前用户数据
}
复制代码
什么是缓存击穿?
大量数据打入热点key,此时key失效,数据直接打入数据库,让数据库压力剧增
2.购物车实战
场景复现:相比大家都用过淘宝吧,淘宝里有购物车功能。就是当用户选好商品,会先放入购物车,可以达到一起支付的效果。
问题解决:其实这个场景有两种解决思路。第一是购物车的数据存入前端。但前端做不到在用户选择商品到购物车的时候,就做好了库存检查的此类操作,需要提交购物车的时候,后端返回。用户体验有缺失的。因此,我们可以用后端对购物车进行存储。这个需要根据场景进行衡量。
对应redis中的指令
//获取key下的所有hash键值对
hgetall key
//设置key ,hash的键值对
hset key k1 v1 k2 v2 k3 v3
//删除该key对应的hash结构里的k1 k2 k3
hdel key k1 k2 k3
复制代码
伪代码
static CART_PREFIX = "cart:"
void add(userId,productId,prodNum){
String key = CART_PREFIX + userId
//利用hset把CART_PREFIX + userId作为key,productId作为k1,prodNum作为value进行存储
}
void remove(userId,productId){
String key = CART_PREFIX + userId
//利用hdel把CART_PREFIX + userId作为key,productId作为k1进行删除
}
void submitOrder(userId){
String key = CART_PREFIX + userId
//利用hgetall进行购物车内容的获取
}
复制代码
Set命令实战
Set命令的对应实战场景主要有俩,分别是标签系统和自适应黑白名单系统。
1.标签系统
场景复现:在掘金文章发表的过程中,需要给文章贴上标签【"java","后端"】。你的个人资料里可能也有标签,比如说,你是学什么的,你的能力等,这些都是你的标签。我们可能需要根据你的个人信息里的标签,给你推荐适合你的文章。
对应redis中的指令
//将set1中添加"a"元素
SADD set1 "a"
//从set1,set2,set3中找到交集并返回
SINTER set1 set2 set3
复制代码
伪代码
productTag(){
String articleTagKey = "tag_a_" + articleId
//将这几个标签加入这个文章中
sadd(articleTagKey,"java","后端","设计模式","数据库")
String userTagKey = "tag_u_" + userId
//
sadd(userTagKey,"程序员","java");
//将用户标签集合和所有的文章标签集合,利用sinter指令做交集,如果交集数量大于一个值,就做推荐。
}
复制代码
除了掘金这种最基本的标签匹配的推荐系统之外。像微博会有共同关注的功能,底层也是基于这个思想的推荐系统。例如张三和我有类似的标签,如果他喜欢的东西,关注的东西,可能也会推荐给我,因为我们是一类人。
包括像qq里面的好友推荐功能,有的陌生好友,下方显示,和你有多少相同的好友,也基本是可以基于sinter指令来实现的。
2.自适应黑白名单系统
场景复现:某个用户异地登录,或者账户被盗,或者突然下单大量不感兴趣的商品,被风控检测。
问题解决:可以在检测到之后,把userId放入set黑名单中,利用一些安全框架,对这些黑名单做接口限制,前端也需要限制,除非他同意做人脸识别或者短信验证的安全认证,做完之后,可以从黑名单中移除。
对应redis中的指令
//将set1中添加"a"元素
SADD set1 "a"
//检查a是否在set1中
SISMEMBER set1 "a"
//将a从set1中删除
SREM set1 "a"
复制代码
伪代码
boolean addProduct(userId,productId){
//1.利用SISMEMBER指令检测是否在黑名单中,如果是就直接如人脸验证,或者短信验证
//2.如果没有就允许请求
}
void faceCheck(userId){
//1.人脸检测
//2.检测完成
//3.利用HREM指令删除黑名单
}
复制代码
Sorted Set命令实战
Sorted Set又叫Zset。主要有两种运用场景,分别是积分排名和延时消息。
1.积分排名
场景复现:比如,lol中的rank排行榜,在峡谷之巅第一名是恶魔波刚,第二名是jjking,第三名是菠萝剑姬。这样的系统如何实现呢?
问题解决:利用Sorted Set解决。
对应redis中的指令
//往myzset加入两个值。分数-玩家名的键值对
ZADD myzset 100 jjking 90 恶魔波冈
//查询目标的score值
ZSCORE myzset jjking
//批量查询目标的score值
ZSCORE myzset jjking 恶魔波刚
//增加指定元素的score值
ZINCREBY myzset 20 jjking
返回:"120"
//将myzset中140分-80分的数据带着score逆序且限制2条返回
ZRANGE myzset 140 80 BYSCORE REV WITHSCORES limit 2
复制代码
伪代码
//玩家胜利后,加分
addScore(){
String rankKey = "rank_"
//给指定玩家加分,利用ZINCREBY指令
zaddIncr(rankKey,point,playerId)
}
//取前2名
getRankList(){
//根据上述对应redis指令:ZRANGE myzset BYSCORE REV WITHSCORES limit 2
}
复制代码
2.延时消息
场景复现:有些分布式任务系统中,任务是有优先级的,系统需要触发高优先级的任务,再触发低优先级的任务。
对应redis中的指令
//往myzset加入两个值。分数-玩家名的键值对
ZADD myzset 100 jjking 90 恶魔波冈
//计数数某个范围内的值
ZCOUNT myzset 70 110
返回:2
//将最小元素pop出来,数字是pop出来的数量
ZPOPMIN myzset 1
复制代码
伪代码
//添加延时信息
addDelayMessage(){
//获取当前时间
int current = currentTimeMillis()
//在10000之后开始
int startTime = current + 10000
//利用zadd命令添加 zadd("delayMsgCenter",startTime ,"task_" + taskId);
}
?
//消费延时消息
consumeDelayMessage(){
//1.利用zcount和 0到current的范围进行搜索返回。
//2.1如果返回的数值小于0,说明没有延时消息。
//2.2如果返回的数值大于0,说明有延时消息,那就把这个延时队列里的最小值,利用ZPOPMIN的方式,弹出,消费
//2.2.1执行消费
}
作者:nika_yo_nihao
链接:https://juejin.cn/post/7225139862223405114
- 上一篇:Redis几个实战经验积累
- 下一篇:混沌工程缓存实战系列一Redis
相关推荐
- 【推荐】一个开源免费、AI 驱动的智能数据管理系统,支持多数据库
-
如果您对源码&技术感兴趣,请点赞+收藏+转发+关注,大家的支持是我分享最大的动力!!!.前言在当今数据驱动的时代,高效、智能地管理数据已成为企业和个人不可或缺的能力。为了满足这一需求,我们推出了这款开...
- Pure Storage推出统一数据管理云平台及新闪存阵列
-
PureStorage公司今日推出企业数据云(EnterpriseDataCloud),称其为组织在混合环境中存储、管理和使用数据方式的全面架构升级。该公司表示,EDC使组织能够在本地、云端和混...
- 对Java学习的10条建议(对java课程的建议)
-
不少Java的初学者一开始都是信心满满准备迎接挑战,但是经过一段时间的学习之后,多少都会碰到各种挫败,以下北风网就总结一些对于初学者非常有用的建议,希望能够给他们解决现实中的问题。Java编程的准备:...
- SQLShift 重大更新:Oracle→PostgreSQL 存储过程转换功能上线!
-
官网:https://sqlshift.cn/6月,SQLShift迎来重大版本更新!作为国内首个支持Oracle->OceanBase存储过程智能转换的工具,SQLShift在过去一...
- JDK21有没有什么稳定、简单又强势的特性?
-
佳未阿里云开发者2025年03月05日08:30浙江阿里妹导读这篇文章主要介绍了Java虚拟线程的发展及其在AJDK中的实现和优化。阅前声明:本文介绍的内容基于AJDK21.0.5[1]以及以上...
- 「松勤软件测试」网站总出现404 bug?总结8个原因,不信解决不了
-
在进行网站测试的时候,有没有碰到过网站崩溃,打不开,出现404错误等各种现象,如果你碰到了,那么恭喜你,你的网站出问题了,是什么原因导致网站出问题呢,根据松勤软件测试的总结如下:01数据库中的表空间不...
- Java面试题及答案最全总结(2025版)
-
大家好,我是Java面试陪考员最近很多小伙伴在忙着找工作,给大家整理了一份非常全面的Java面试题及答案。涉及的内容非常全面,包含:Spring、MySQL、JVM、Redis、Linux、Sprin...
- 数据库日常运维工作内容(数据库日常运维 工作内容)
-
#数据库日常运维工作包括哪些内容?#数据库日常运维工作是一个涵盖多个层面的综合性任务,以下是详细的分类和内容说明:一、数据库运维核心工作监控与告警性能监控:实时监控CPU、内存、I/O、连接数、锁等待...
- 分布式之系统底层原理(上)(底层分布式技术)
-
作者:allanpan,腾讯IEG高级后台工程师导言分布式事务是分布式系统必不可少的组成部分,基本上只要实现一个分布式系统就逃不开对分布式事务的支持。本文从分布式事务这个概念切入,尝试对分布式事务...
- oracle 死锁了怎么办?kill 进程 直接上干货
-
1、查看死锁是否存在selectusername,lockwait,status,machine,programfromv$sessionwheresidin(selectsession...
- SpringBoot 各种分页查询方式详解(全网最全)
-
一、分页查询基础概念与原理1.1什么是分页查询分页查询是指将大量数据分割成多个小块(页)进行展示的技术,它是现代Web应用中必不可少的功能。想象一下你去图书馆找书,如果所有书都堆在一张桌子上,你很难...
- 《战场兄弟》全事件攻略 一般事件合同事件红装及隐藏职业攻略
-
《战场兄弟》全事件攻略,一般事件合同事件红装及隐藏职业攻略。《战场兄弟》事件奖励,事件条件。《战场兄弟》是OverhypeStudios制作发行的一款由xcom和桌游为灵感来源,以中世纪、低魔奇幻为...
- LoadRunner(loadrunner录制不到脚本)
-
一、核心组件与工作流程LoadRunner性能测试工具-并发测试-正版软件下载-使用教程-价格-官方代理商的架构围绕三大核心组件构建,形成完整测试闭环:VirtualUserGenerator(...
- Redis数据类型介绍(redis 数据类型)
-
介绍Redis支持五种数据类型:String(字符串),Hash(哈希),List(列表),Set(集合)及Zset(sortedset:有序集合)。1、字符串类型概述1.1、数据类型Redis支持...
- RMAN备份监控及优化总结(rman备份原理)
-
今天主要介绍一下如何对RMAN备份监控及优化,这里就不讲rman备份的一些原理了,仅供参考。一、监控RMAN备份1、确定备份源与备份设备的最大速度从磁盘读的速度和磁带写的带度、备份的速度不可能超出这两...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- oracle位图索引 (63)
- oracle批量插入数据 (62)
- oracle事务隔离级别 (53)
- oracle 空为0 (50)
- oracle主从同步 (55)
- oracle 乐观锁 (51)
- redis 命令 (78)
- php redis (88)
- redis 存储 (66)
- redis 锁 (69)
- 启动 redis (66)
- redis 时间 (56)
- redis 删除 (67)
- redis内存 (57)
- redis并发 (52)
- redis 主从 (69)
- redis 订阅 (51)
- redis 登录 (54)
- redis 面试 (58)
- 阿里 redis (59)
- redis 搭建 (53)
- redis的缓存 (55)
- lua redis (58)
- redis 连接池 (61)
- redis 限流 (51)