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,一经查实,立即删除!

相关文章

服务器选择租用还是托管?托管和租用哪个比较划算

在构建或扩展IT基础设施时&#xff0c;服务器作为关键组件&#xff0c;其选择方式——租用或托管&#xff0c;直接关系到企业的运营成本、灵活性、安全性及长期发展战略。本文将从技术、经济、安全等多个维度&#xff0c;深入解析这两种方案的优缺点&#xff0c;并探讨在何种情…

Dav_笔记11:SQL Tuning Overview-sql调优 之 1

Introduction to SQL Tuning SQL调优简介 SQL调优涉及以下基本步骤&#xff1a; ■通过查看系统中可用的过去SQL执行历史记录&#xff0c;识别负责大量应用程序工作负载和系统资源的高负载或顶级SQL语句 ■验证查询优化器为这些语句生成的执行计划是否合理执行 ■实施纠正…

力扣第三十一题——下一个排列

内容介绍 整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。 例如&#xff0c;arr [1,2,3] &#xff0c;以下这些都可以视作 arr 的排列&#xff1a;[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。 整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地&…

redis详解--springboot整合redis

1. java连接redis 思考: 我们之前操作redis都是通过命令行的客户端来操作。 在开发时都是通过java项目操作redis.引入Redis依赖 <!--引入java连接redis的驱动--><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactI…

ThreadLocal面试三道题

针对ThreadLocal的面试题&#xff0c;我将按照由简单到困难的顺序给出三道题目&#xff0c;并附上参考答案的概要。 1. 简单题&#xff1a;请简述ThreadLocal是什么&#xff0c;以及它的主要作用。 参考答案&#xff1a; ThreadLocal是Java中的一个类&#xff0c;用于提供线…

力扣笔试题系列(一)

1、给你两个字符串 word1 和 word2 。请你从 word1 开始&#xff0c;通过交替添加字母来合并字符串。如果一个字符串比另一个字符串长&#xff0c;就将多出来的字母追加到合并后字符串的末尾。 char * mergeAlternately(char * word1, char * word2){int len1 strlen(word1);i…

C++ primer plus 第16章string 类和标准模板库, 无序关联容器(C++11)

C primer plus 第16章string 类和标准模板库, 无序关联容器(C11) C primer plus 第16章string 类和标准模板库, 无序关联容器(C11) 文章目录 C primer plus 第16章string 类和标准模板库, 无序关联容器(C11)16.4.5 无序关联容器(C11) 16.4.5 无序关联容器(C11) 无序关联容器是…

【常微分方程】

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

力扣138【随机链表的复制】

给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成&#xff0c;其中每个新节点的值都设为其对应的原节点的值。新节点的 ne…

【.NET全栈】ASP.NET开发Web应用——ASP.NET数据绑定技术

文章目录 前言一、绑定技术基础1、单值绑定2、重复值绑定 二、数据源控件1、数据绑定的页面生存周期2、SqlDataSource3、使用参数过滤数据4、更新数据和并发处理5、编程执行SqlDataSource命令6、ObjectDataSource控件介绍7、创建业务对象类8、在ObiectDataSource中使用参数9、使…

06 ES6中的Map类型

ES6 中的 Map 是一种集合数据结构&#xff0c;与 Set 类似&#xff0c;但它是用来存储键值对的。Map 提供了更灵活的迭代和更广泛的键类型支持&#xff0c;与普通的对象相比&#xff0c;Map 的键可以是任何值&#xff0c;包括对象和函数。 以下是 Map 类型的一些关键特性和常用…

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:引导…

神经网络的发展历程及未来展望

神经网络的发展历程及未来展望 神经网络&#xff0c;作为人工智能和机器学习领域的核心技术之一&#xff0c;经历了多次起伏波动&#xff0c;每一次的高潮和低谷都深刻影响了该技术的发展轨迹和应用范围。本文将详细探讨神经网络的发展历程&#xff0c;包括其爆发期、低谷期&am…

linux快速入门-学习笔记

linux快速入门-学习笔记 第一章&#xff1a;Linux系统概念及命令学习Linux系统基本概念命令终端介绍命令格式介绍Linux系统辨别目录与文件的方法通过文件详细属性辨别ls 查看目录/文件命令Linux 系统下的归属关系命令行编辑技巧Linux 基本权限的类别课后练习 第二章&#xff1a…

扫地机器人离线语音识别芯片,工业级智能交互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…