Redis的应用场景及类型

目录

一、Redis的应用场景

1、限流

2、分布式锁

3、点赞

4、消息队列

二、Redis类型的命令及用法

1、String类型

2、Hash类型

3、List类型

4、Set类型

 5、Zset类型

 6、Redis工具类


Redis使用缓存的目的就是提升读写性能

实际业务场景下,我们就可以把 Mysql 中的热点数据缓 存到 Redis 中,提升读取性能,同时也减轻了 Mysql 的读取压力

一、Redis的应用场景

Redis除了做缓存以外,还可以用

  • 限流
  • 分布式锁
  • 点赞/排行榜
  • 消息队列

还有其他场景:计数器、互关好友、购物车和商品标签等等

1、限流

利用Redis的过期键和计数器功能,实现API的限流功能,防止服务被滥用

方法一记录IP在某个时间段访问某接口的次数

使用IP作为Key和其他信息作为Key,访问次数作为Value,访问一次Incr增加一次,超过规定次数则返回false

但问题是:限流时间段是固定的

比如:某接口在1分钟内请求次数不超过1000次

就是00:59分,用户已经访问了999次,1:00key值过期,1:01又访问了999次

看起来好像是没问题,但是00:59—1:01的仅2s时间段内,接口被访问了1000+999次,明显错误

方法二滑动窗口

为了避免方法一种由于key过期导致短期内访问量增大的情况,将时间改成动态的

在每次接口访问时,记录当前访问的时间点,并计算前1min内用户访问该接口的总次数,如果总次数大于1000次,则不允许用户访问该接口

以上两种利用redis实现限流的方式基本能满足我们大部分的业务需要,对于部分要求限流粒度更高更准的业务,可以引入Sentinel来满足业务需要

2、分布式锁

为什么使用分布式锁?

在单机部署的时候,可以使用 Java 中提供的 JUC 锁机制避免多线程同时操作一个共享变量产生的安全问题,通过锁(synchronzied 或 lock)来锁住自己的线程资源,从而防止缓存击穿

Redis的缓存问题:缓存穿透、缓存击穿和缓存雪崩-CSDN博客

这是一种本地加锁的方式,在分布式情况下会带来数据不一致的问题

比如:服务 A 获取数据后,更新缓存 key =100,服务 B 不受服务 A 的锁限制,并发去更新缓存 key = 99,最后的结果可能是 99 或 100,但这是一种未知的状态,与期望结果不一致

①基础版

Redis的SET命令有一个NX参数,可以实现「key不存在才插入」,因此可以用它来实现分布式锁:

SetNX(key,value)

redisTemplate.opsForValue().setIfAbsent(“k”, “v”)

也就是 

SET lockKey requestId NX PX expireTime
  • lockKey 表示锁的资源,

  • requestId 全局唯一的业务id,避免存在加锁和释放锁乱掉的情况

  • NX:表示只有 lockKey 不存在的时候才能 SET 成功,从而保证只有一个客户端可以获得锁。

  • PX expireTime设置锁的超时时间,单位是毫秒;也可以使用 EX seconds以秒为单位设置超时时间

伪代码:

try {if (jedis.set(lockKey, requestId, "NX", "PX", expireTime))) {//业务处理return true;}
} finally{//判断是不是当前线程加的锁,是才释放if(requestd.equals.(jedis.get(lockKey))){//释放锁unlock(lockKey,requestId);}
}  
return false;

②Redisson实现

Redisson 是 Redis 的 Java 客户端之一,支持原子性加/解锁、锁重试、可重入锁、RedLock 等功能

// 获取分布式锁
RLock lock = redissonClient.getLock("myLock");
try {// 尝试加锁,最多等待 10 秒,加锁后的锁有效期为 30 秒boolean locked = lock.tryLock(10, 30, TimeUnit.SECONDS);if (locked) {// 成功获取锁,执行业务逻辑System.out.println("获取锁成功,执行业务逻辑...");} else {// 获取锁失败,可能是超时等待或者其他原因System.out.println("获取锁失败...");}
} catch (InterruptedException e) {e.printStackTrace();
} finally {// 释放锁lock.unlock();// 关闭 Redisson 客户端redissonClient.shutdown();
}

Redisson存储分布式锁是通过Hash结构进行存储的,内置的键值对是< 线程标识,重入次数>,其中重入次数便可用于实现可重入机制

3、点赞

点赞就需要用到Redis的Set类型,Set类型是Redis中的一个无序集合,它可以存储一组字符串元素,并且每个元素都是唯一的

【点赞】SADD key member:向集合中添加一个或多个元素

 redisTemplate.opsForSet().add(key, values)

【取消点赞】SREM key:删除元素

redisTemplate.opsForSet().remove(key,value)

【点赞的所有用户】SMEMBERS key:返回集合中的所有元素

 redisTemplate.opsForSet().members(key)

【是否点赞】SISMEMBER key member:判断元素是否在集合中

redisTemplate.opsForSet().isMember(key, value)

【点赞数】scard(key):元素长度

redisTemplate.opsForSet().size(key)

场景:对活动的相册图片进行点赞且对点赞数量进行计数的功能

public void like(Long activityId) {LzmUserInfo userInfo = UserContextHolder.getLzmUserInfo();String key = ACTIVITY.ACTIVITY_LIKE_KEY + activityId;Boolean member = stringRedisTemplate.opsForSet().isMember(key, userInfo.getUserId().toString());if (BooleanUtil.isFalse(member)) {//未点赞,点赞数+1this.update(Wrappers.<ActivityManageEntity>lambdaUpdate().setSql("like_num = like_num + 1").eq(ActivityManageEntity::getActivityId, activityId).eq(ActivityManageEntity::getIsDelete, IsDeleteEnum.NORMAL.getCode()));redisTemplate.opsForSet().add(key, userInfo.getUserId().toString());} else {//取消点赞,点赞数-1this.update(Wrappers.<ActivityManageEntity>lambdaUpdate().setSql("like_num = like_num - 1").gt(ActivityManageEntity::getLikeNum, 0).eq(ActivityManageEntity::getActivityId, activityId).eq(ActivityManageEntity::getIsDelete, IsDeleteEnum.NORMAL.getCode()));redisTemplate.opsForSet().remove(key, userInfo.getUserId().toString());}}

4、消息队列

Redis可以实现简单的队列。在生产者端,使用LPUSH加入到某个列表中;在消费端,不断的使用RPOP指令取出这些数据,或者使用阻塞的BRPOP指令获取数据,用于处理异步任务,例如邮件发送、后台任务处理,小规模的抢购需求等

场景:邮件发送

生产者发布消息

LPUSH queue msg1

 消费者 拉取消息

RPOP queue

二、Redis类型的命令及用法

1、String类型

操作命令用法
设置set(“k”,“v”)template.opsForValue().set(“k”,“v”)
获取get(“k”)template.opsForValue().get(“k”)
增1incr(“k”)template.boundValueOps(“k”).increment(1)
减1decr(“k”)template.boundValueOps(“k”).increment(-1)
设置时效setex(“k”,seconds,“v”)template.opsForValue().set(“k”,“v”,20, TimeUnit.SECONDS)
key不存在就设置setnx(“k”,“v”)template.opsForValue().setIfAbsent(“k”, “v”)
获取key过期时间ttl(“k”)template.getExpire(“k”)
删除del(“k”)template.delete(“k”)

2、Hash类型

操作命令用法
设置hset(“k1”,“k2”,“k3”)template.opsForHash().put(“k1”,“k2”,“k3”)
获取hget(“k1”,“k2”)template.opsForHash().get(“k1”,“k2”) /template.opsForHash().values(“k1”)
删除hdel(“k1”,“k2”)template.opsForHash().delete(“k1”,“k2”)
是否存在hexists(“k1”,“k2”)template.opsForHash().hasKey(“k1”,“k2”)

3、List类型

操作命令用法
从右侧添加rpush(“k”,“v”)template.opsForList().rightPush(“k”,“v”)
从右侧移除rpop(“list”)template.opsForList().rightPop(“k”)
长度llen(“k”)template.opsForList().size(“k”)
获取指定范围的元素lrange(“list”,0,-1)  -1指全部template.opsForList().range(“list”, 0, -1)

4、Set类型

操作命令用法
添加sadd(“k”,“v”)template.opsForSet().add(“k”,“v”)
值移除srem(“k”,“v”)template.opsForSet().remove(“k”,“v”)
直接移除spop(“k”)template.opsForSet().pop(“k”)
获取所有smembers("k")template.opsForSet().members("k")
是否存在sismember("k")emplate.opsForSet().isMember("k","v")
长度scard(“k”)template.opsForSet().size(“k”)
交集sinter(“k1”,“k2” )template.opsForSet().intersect(“k1”, “k2”)
并集sunion(“k1”,“k2” )template.opsForSet().union(“k1”, “k2”)
差集sdiff(“k1”,“k2” )template.opsForSet().difference(“k1”, “k2”)

 5、Zset类型

操作命令用法
增加zadd(“k”,1,“a”)template.opsForZSet().add(“k”,“1”,a)
排名结果zrevrange(“k”, 0, -1)template.opsForZSet().reverseRange(“k”, 0, -1)
排名分数

zrevrangeByScoreWithScores(“k”, 1, 10);

template.opsForZSet().reverseRangeByScore(“k”, 1, 100)
修改分数zincrby(“k”,20,“a”)template.opsForZSet().incrementScore(“k”,“20”,a)
数量zcard(“k”)template.opsForZSet().zCard(“k”)
获取排名zrank(“k”,“a”)template.opsForZSet().rank(“k”,“a”)

 6、Redis工具类

@Component
public class RedisUtils {@Resourceprivate RedisTemplate<String, Object> redisTemplate;/*** 添加数据*/public void set(String key, Object value) {redisTemplate.opsForValue().set(key, value);}/*** 如果key不存在,则设置key的值为value并返回true,否则返回false*/public boolean setNx(String key, String value) {return Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, value));}/*** 如果key不存在,则设置key的值为value以及过期时间,并返回true,否则返回false,*/public boolean setNx(String key, String value, long timeout, TimeUnit unit) {return Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, value, timeout, unit));}/*** 根据key获取数据*/public Object get(String key) {return redisTemplate.opsForValue().get(key);}/*** 根据key删除数据*/public void delete(String key) {redisTemplate.delete(key);}/*** 向集合中添加一个或多个元素*/public void add(String key, Object... values) {redisTemplate.opsForSet().add(key, values);}/*** 将 value 插入到 key 对应的列表的头部*/public void leftPush(String key, Object value) {//。redisTemplate.opsForList().leftPush(key, value);}/*** 将 value 插入到 key 对应的列表的尾部*/public void rightPush(String key, Object value) {redisTemplate.opsForList().rightPush(key, value);}/*** 从 key 对应的列表的头部删除并返回一个元素*/public Object leftPop(String key) {return redisTemplate.opsForList().leftPop(key);}/*** 从 key 对应的列表的尾部删除并返回一个元素*/public Object rightPop(String key) {return redisTemplate.opsForList().rightPop(key);}/*** 返回列表中指定范围内的元素*/public List<Object> range(String key, long start, long end) {return redisTemplate.opsForList().range(key, start, end);}/*** 返回集合中的所有元素*/public Set<Object> members(String key) {return redisTemplate.opsForSet().members(key);}/*** 判断元素是否在集合中*/public Boolean isMember(String key, Object value) {return redisTemplate.opsForSet().isMember(key, value);}/*** 向有序集合中添加一个元素并指定分数*/public void add(String key, double score, Object value) {redisTemplate.opsForZSet().add(key, value, score);}/*** 获取指定score范围内元素*/public Set<ZSetOperations.TypedTuple<Object>> rangeWithScores(String key, long start, long end) {return redisTemplate.opsForZSet().rangeWithScores(key, start, end);}
}

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

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

相关文章

【常微分方程】

框架 常微分方程的概念一阶微分方程可变离分量齐次方程一阶线性微分方程可降阶的高阶微分方程二阶常系数齐次线性微分方程二阶常系数非齐次线性微分方程 讲解 【1】 常微分方程&#xff1a;是微分方程的特殊情况&#xff1b; 阶&#xff1a;是方程未知函数的最高阶导数的阶数&…

ElementUI,修改el-table中的数据,视图无法及时更新

需求&#xff1a;点击table表格中的“修改”之后&#xff0c;当前行变为可输入状态的行&#xff0c;点击“确定”后变为普通表格&#xff1b; 先贴上已经完美解决问题的代码 实现代码&#xff1a; <section><div style"display: flex;justify-content: space-b…

爬虫学习1:初学者简单了解爬虫的基本认识和操作(详细参考图片)

爬虫 定义&#xff1a;爬虫&#xff08;Web Crawler 或 Spider&#xff09;是一种自动访问互联网上网页的程序&#xff0c;其主要目的是索引网页内容&#xff0c;以便搜索引擎能够快速检索到相关信息。以下是爬虫的一些关键特性和功能&#xff1a; 自动化访问&#xff1a;爬虫能…

【React】事件绑定:深入解析高效处理用户交互的最佳实践

文章目录 一、什么是事件绑定&#xff1f;二、基本事件绑定三、绑定 this 上下文四、传递参数五、事件对象六、事件委托七、常见事件处理八、优化事件处理 React 是现代前端开发中最受欢迎的框架之一&#xff0c;其组件化和高效的状态管理能力使得构建复杂的用户界面变得更加容…

嵌入式MCU固件的几种Flash划分方式详解

通过OTA远程等方式下载的程序,其实还需要提前下载bootloader程序,才能进一步下载APP程序。 今天就来说说通过OTA方式升级固件时,几种flash划分方式。 独立型 所谓独立型就是专门划出一部分闪存(Flash)空间用来存储引导程序(BootLoader)。 如下图: BootLoader:引导…

扫地机器人离线语音识别芯片,工业级智能交互ic,NRK3301

随着科技的飞速发展&#xff0c;智能家居已成为人们追求高品质生活的新趋势。扫地机器人&#xff0c;作为智能家居的重要一员&#xff0c;正逐步从简单的清扫工具进化为具备高度智能的家居助手。 在这一背景下&#xff0c;离线语音识别技术显得尤为重要。传统的扫地机器人大多依…

问题记录-Spring Security- bean httpSecurity not found

问题描述 最近使用Security的时候报了下面的错误&#xff1a; 配置如下&#xff1a; EnableWebSecurity Slf4j public class SecurityConfig {Resourceprivate CustUserService custUserService;Beanpublic AuthenticationProvider authenticationProvider() {return new A…

element-plus时间组件el-date-picker只能选择当前及之前日期

<el-date-picker v-model"timeVal" type"daterange" value-format"YYYY-MM-DD" range-separator"To" start-placeholder"开始时间" end-placeholder"结束时间" />默认是这样的&#xff0c;需要绑定disabled…

一款基于Cortex-M0+的单片机音频编解码 - CJC2100

USBCodec芯片可以对数字音频信号进行多种处理&#xff0c;例如增加音量、均衡调节、音效处理等。这些处理可以通过耳机的控制按钮来实现&#xff0c;让用户可以根据自己的喜好来调整音频效果。USBCodec芯片还可以控制噪声和失真的水平&#xff0c;以提供高品质的音频输出。噪声…

[IMX6ULL]移植NXP Linux Kernel 5.15

移植NXP Linux Kernel 5.15 2024-7-7 hongxi.zhu 1. 下载NXP Linux Kernel 5.15 仓库[nxp-imx/linux-imx] git clone -b lf-5.15.y https://github.com/nxp-imx/linux-imx.git 2. 编译NXP Linux Kernel 5.15 make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- distclean make…

【3D 重建】NeRF,3D Gaussian Splatting

文章目录 AI 甘安捏【入门介绍&#xff0c;形象生动】3D 重建技術 (一): 什麼是 3D 重建 (3D Reconstruction)&#xff1f;為什麼需要 3D 重建&#xff1f;【NeRF&#xff0c;3D Gaussian Splatting简介】3D 重建技術 (二): NeRF&#xff0c;AI技術革命 -- 用神經網路把場景「背…

【维普网】收录的电子刊汇总(部分省市职称评审认可)

《中国科技期刊数据库&#xff08;文摘版&#xff09;医药卫生》是经国家新闻出版总署批准&#xff0c;科技部西南信息中心主管、重庆维普资讯有限公司主办的连续型电子出版物。国内刊号&#xff1a;50-9212/R&#xff0c;国际刊号&#xff1a; 1671-5608。主要栏目为影像与检验…

Cornerstone3D 演示库恢复更新啦~

前言 从0上手Cornerstone3D系列的git库终于有时间更新优化了一版。主要更新以下内容&#xff1a; ✨ vue2更新至vue3版本&#xff0c;代码迁移为vue3组合式写法 ✨ UI风格升级&#xff0c;新增交互提示 ✨ 修复页面切换报错问题 ✨ … 关于git库 &#x1f3af; 地址&…

el-upload照片墙自定义上传多张图片(手动一次性上传多张图片)包含图片回显,删除

需求&#xff1a;el-upload照片墙自定义上传多张图片&#xff08;手动一次性上传多张图片&#xff09;包含图片回显&#xff0c;删除&#xff0c;预览&#xff0c;在网上看了很多&#xff0c;都没有说怎么把数据转为file格式的&#xff0c;找了很久最终实现&#xff0c; 难点&a…

第4章 .NET 8.0 ASP.NET Core图书管理系统 :项目布局

第1章 框架学习的基石与实战策略 第2章 大话ASP.NET Core 入门 第3章 创建最小&#xff08;Minimal APIs&#xff09;API应用程序 第4章 .NET 8.0 ASP.NET Core图书管理系统 &#xff1a;项目布局 在第3章中&#xff0c;我们利用ASP.NET Core的“空”模板创建了BookQueryS…

lua 游戏架构 之 游戏 AI (五)ai_autofight_find_way

这段Lua脚本定义了一个名为 ai_autofight_find_way 的类&#xff0c;继承自 ai_base 类。 lua 游戏架构 之 游戏 AI &#xff08;一&#xff09;ai_base-CSDN博客文章浏览阅读238次。定义了一套接口和属性&#xff0c;可以基于这个基础类派生出具有特定行为的AI组件。例如&…

【Python】成功解决conda创建虚拟环境时出现的CondaHTTPError: HTTP 000 CONNECTION FAILED错误

【Python】成功解决conda创建虚拟环境时出现的CondaHTTPError: HTTP 000 CONNECTION FAILED错误 &#x1f308; 欢迎莅临我的个人主页&#x1f448;这里是我深耕Python编程、机器学习和自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;并乐于分享知识与经验的小天地&a…

20240724----idea的Java环境卸载与安装

1.删除旧有的jdk https://blog.csdn.net/weixin_42168713/article/details/112162099 &#xff08;补充&#xff1a;我把用户变量和java有关的都删了&#xff09; 2.下载新的jdk百度网盘链接 链接&#xff1a;https://pan.baidu.com/s/1gkuLoxBuRAtIB1IzUTmfyQ 提取码&#xf…

Docker 部署常用中间件(redis,rabbitMQ,mysql8,es,kibana,nginx等)亲测成功~~~

Docker 部署常用中间件 在日常开发中必要的环境&#xff0c;大多数都是单点后续持续更新集群模式~~~ docker 安装reids docker pull redis:7.2.5 编辑redis.conf # 绑定地址&#xff0c;默认只允许本机访问 # bind 192.168.1.100 10.0.0.1 # bind 127.0.0.1 ::1 bind 0.0…

Vue中el的两种写法

大家好我是前端寄术区博主PleaSure乐事。今天了解到了Vue当中有关el的两种写法&#xff0c;记录下来与大家分享&#xff0c;希望对大家有所帮助。 方法一 解释 第一种方法我们直接用new创建并初始化一个新的 Vue 实例&#xff0c;并定义了 Vue 实例的数据对象&#xff0c;在给…