
Redis
文章平均质量分 81
Redis学习笔记
洛上言
路漫漫我不畏
展开
-
【Redis】分布式锁-Redission快速入门
并且官方还提供了一个Redission-springboot-starter,但这种方式并不推荐大家去使用,因为它会去替代spring官方提供的对于redis的这套配置和实现。Redission的配置其实有两种方式,这是一种利用Java配置方式来实现,事实上它也可以利用yml文件跟SpringBoot去整合实现。注入RedissonClient。原创 2024-08-07 12:04:37 · 358 阅读 · 0 评论 -
【Redis】分布式锁-redission
我们一路走来,利用添加过期时间,防止死锁问题的发生,但是有了过期时间之后,可能出现误删别人锁的问题,这个问题我们开始是利用删之前 通过拿锁,比锁,删锁这个逻辑来解决的,也就是删之前判断一下当前这把锁是否是属于自己的,但是现在还有原子性问题,也就是我们没法保证拿锁比锁删锁是一个原子性的动作,最后通过lua表达式来解决这个问题。原创 2024-08-07 12:03:55 · 1121 阅读 · 0 评论 -
【Redis】利用Java代码调用Lua脚本改造分布式锁
现在我们实现的分布式锁其实就已经是一个生产可用的、相对完善的分布式锁了。小总结:基于Redis的分布式锁实现思路:获取锁的时候利用set nx ex获取锁,set nx目的是互斥,确保只有一个线程能拿到锁,ex是一个兜底方案,防止宕机导致锁无法释放。释放锁时先判断线程标示是否与自己一致,一致则删除锁,防止误删并且使用Lua脚本保证它的原子性,这样避免在多线程的情况下因为阻塞导致的误删。那这样的分布式锁有什么特性呢?利用set nx满足互斥性。原创 2024-08-07 12:03:23 · 938 阅读 · 0 评论 -
【Redis】Lua脚本解决多条命令原子性问题
而且它事务中多个操作,其实是一个批处理,是在最终一次性趣执行的,也就是说没有办法先去查询,然后判断,最后释放。因为编写这个脚本的是一个叫做Lua的编程语言,并且我们不需要熟练精通Lua语言,你只需要懂它的一些基本用法就行了,它的基本语法大家可以参考网站:https://www.runoob.com/lua/lua-tutorial.html,我们最终要做的事情是:利用Lua语言编写脚本调用redis。表示参数的数量,我们这个脚本内容全都是写死的,内容固定的,脚本可以理解为一个函数,里面有一堆的代码。原创 2024-08-07 12:02:47 · 428 阅读 · 0 评论 -
【Redis】分布式锁的原子性问题
同样的是线程1一上来就申请锁,线程1现在持有锁之后,在执行业务逻辑过程中,他正准备删除锁,而且已经走到了判断锁标识的过程中,由于这把锁是自己的,因此肯定返回ok,紧接着就要执行释放锁的动作了。锁一旦超时释放,其他的线程又可以乘虚而入了,例如此时线程2进来,由于锁被释放掉了,因此它可以继续获取锁,可以开始执行自己的业务,而就在他获取锁成功的那一刻,如果GC结束了,阻塞结束,我们的线程恢复运行,此时它就会去执行释放锁的动作了,因为判断已经执行过了,它认为锁还是自己的,但现在锁已经是线程2的了。原创 2024-08-07 12:02:12 · 302 阅读 · 0 评论 -
【Redis】解决Redis分布式锁误删问题
之前使用的是线程的id,线程id是一个递增的数字,JVM内部每创建一个线程,它的数字就会递增。但是如果我们是在集群的模式下,我们有多个JVM,这样一来,每个JVM内部都会维护这样一个递增的数字,那两个JVM很有可能出现线程id冲突的情况,所以说我们直接使用线程的id去作为线程标识是不够的,我们还要去区分不同的JVM,让它们产生一些差异,因此这里使用UUID。总结:在存入锁时,放入自己线程的标识,在删除锁时,判断当前这把锁的标识是不是自己存入的,如果是,则进行删除,如果不是,则不进行删除。原创 2024-08-07 11:58:13 · 997 阅读 · 0 评论 -
【Redis】Redis分布式锁误删情况说明
解决这个问题的关键就是在释放锁的时候做一个判断,因此业务流程与原来业务相比就会有些变化了。原创 2024-08-07 11:57:38 · 793 阅读 · 0 评论 -
【Redis】实现分布式锁初级版本
ILock其实就是一个锁接口。原创 2024-08-07 11:52:48 · 912 阅读 · 0 评论 -
【Redis】Redis分布式锁的实现核心思路
我们利用redis 的setNx 方法,当有多个线程进入时,我们就利用该方法,第一个线程进入时,redis 中就有这个key 了,返回了nil;还有一种方式就是非阻塞式的获取,也就是说我来尝试获取锁,如果我获取失败了,我就会立即结束,返回一个结果,而不是一直尝试一直等待。其实在我们JDK中提供的锁有两种机制,一种是:如果获取失败,就会阻塞等待,等到有人释放锁为止,也就是说是一种阻塞式的获取。因此我们会利用非阻塞的方式,也就是说我尝试一次,成功返回true,失败返回false,然后我就不再尝试了。原创 2024-08-06 20:50:31 · 295 阅读 · 0 评论 -
【Redis】分布式锁
经过上节课的演示,可以发现在集群模式下synchronized锁失效了,synchronized只能保证单个JVM内部的多个线程之间的互斥,而没有办法让我们集群下的多个JVM进程之间互斥,要想解决这个问题,我们必须使用分布式锁。原创 2024-08-06 20:49:40 · 1650 阅读 · 0 评论 -
【Redis】集群环境下的并发问题
通过加锁可以解决在单机情况下的一人一单安全问题,但是在集群模式下就不行了。原创 2024-08-06 20:47:08 · 985 阅读 · 0 评论 -
【Redis】优惠券秒杀 —— 一人一单
但是以上代码还是存在问题,问题的原因在于当前方法被spring的事务控制,如果你在方法内部加锁,可能会导致当前方法事务还没有提交,但是锁已经释放也会导致问题,因此当方法结束后,锁其实就已经释放了,锁释放了就意味着其他线程可以进来了,而此时因为事务尚未提交,如果有其他线程进来去查询订单的话,那我们刚刚新增的这个订单还没有写入数据库,因为你还没提交事务,因此这个线程查询的时候依然不存在,就有可能出现并发安全问题。思考一下,像这种秒杀券(特有券),它的优惠力度非常大,商家可能会赔本,那这种券的目的是什么?原创 2024-08-06 20:46:14 · 898 阅读 · 0 评论 -
【Redis】乐观锁解决超卖问题
超卖这样的线程安全问题,解决方案有哪些?1.悲观锁:添加同步锁,让线程串行执行优点:简单粗暴缺点:性能一般2.乐观锁:不加锁,在更新时判断是否有其它线程在修改优点:性能好缺点:存在成功率低的问题有些业务中不是库存,它只能通过数据有没有变化来去判断是否安全,这种情况下想要提高成功率,我们还可以采用分批加锁的方案,即分段锁,即我可以将数据的资源分成几份,例如库存总共是100,我可以将100库存分到十张表中,每张表中库存量是10,然后抢的时候就可以去多张表中分别抢,这样一来成功率相当于提高了十倍。原创 2024-08-06 20:45:21 · 939 阅读 · 0 评论 -
【Redis】库存超卖问题分析
乐观锁:会有一个版本号,每次操作数据会对版本号+1,再提交回数据时,会去校验是否比之前的版本大1 ,如果大1 ,则进行操作成功,这套机制的核心逻辑在于,如果在操作过程中,版本号只比原来大1 ,那么就意味着操作过程中没有人对他进行过修改,他的操作就是安全的,如果不大1,则数据被修改过。,因为之前查询出来的version是1,如果执行这个条件时version依然等于1,说明跟我们之前查询到的一样,说明在我执行修改之前,是没有人修改过这个数据的,既然没有人修改过,我就可以放心大胆的去减了。原创 2024-08-06 20:44:24 · 1367 阅读 · 0 评论 -
【Redis】实现秒杀下单
比如时间是否充足,如果时间充足,则进一步判断库存是否足够,如果两者都满足,则扣减库存,创建订单,然后返回订单id,如果有一个条件不满足则直接结束。下单核心思路:当我们点击抢购时,会触发右侧的请求,我们只需要编写对应的controller即可。当用户开始进行下单,我们应当根据ID去查询优惠卷信息,查询到优惠卷信息,判断是否满足秒杀条件。原创 2024-08-06 20:43:51 · 299 阅读 · 0 评论 -
【Redis】添加优惠卷
而代金券由于优惠力度大,所以像第二种卷,就得限制数量,从表结构上也能看出,特价卷除了具有优惠卷的基本信息以外,还具有库存,抢购时间,结束时间等等字段。tb_seckill_voucher:优惠券的库存、开始抢购时间,结束抢购时间。特价优惠券才需要填写这些信息。每个店铺都可以发布优惠券,分为平价券和特价券。**新增普通卷代码: **VoucherController。tb_voucher:优惠券的基本信息,优惠金额、使用规则等。平价卷由于优惠力度并不是很大,所以是可以任意领取。这两张表对应的数据库表如下。原创 2024-08-06 20:43:13 · 296 阅读 · 0 评论 -
【Redis】Redis实现全局唯一Id
全局唯一ID生成策略:UUID这个直接利用JDK自带的工具类UUID工具类就能生成了。这种生成策略生成的其实是16进制的一长串的数值,因为这一长串是十六进制,因此它返回的结果其实是字符串结构,并且也不是单调递增的一种特性。因此虽然可以做唯一ID,但是并不够友好,没有满足之前我们所说的哪些特性,因此这种用的比较少。Redis自增这种方法相对来讲各种特性都能满足,而且整体是单调递增的,数值的长度也不大,总共也不超过long,而且它是一个数字类型它存储的数据库里占用的空间相对来讲也比较小,比较有好一些。原创 2024-08-06 20:42:26 · 1113 阅读 · 0 评论 -
【Redis】封装Redis工具类
经过前面几节的学习,我们已经解决了缓存使用过程中的各种各样的一些问题,但是我们发现无论是解决缓存穿透还是解决缓存击穿,她们的代码逻辑都是挺复杂的,如果说我们每次开发的时候都要去写这些逻辑,其实开发的成本还是挺高的,所以将来我们一定会将这些解决方案封装成工具。这四个方法你可以认为这个方法是两两配对的,方法一和方法三对应起来,即对应一些常见的、普通的缓存,可以解决缓存穿透、设置TTL过期。因为你封装的这个工具类,它并不知道使用者要操作的数据是什么类型,因此这个方法在使用的时候需要指定类型,然后才能做反序列化。原创 2024-08-06 20:37:27 · 715 阅读 · 0 评论 -
【Redis】利用逻辑过期解决缓存击穿问题
顾名思义,逻辑过期不是真正的过期,它要求我们在存储数据到redis的时候,额外的要添加一个过期时间的字段,这个key本身是不用去设置ttl的,所以它的过期时间不是由redis控制的,而是由我们程序员自己去判断它是否过期,这样我们的业务上就会复杂很多,因此我们先来看一下整个业务流程上有什么变化。由于我们在单元测试的时候已经向redis中插入了一条数据了,并且这条数据当时设置的过期时间也比较短,大概是10秒钟的样子,因此理论上这些数据应该早就过期了,但是它依然还在这里,这就是逻辑过期。原创 2024-08-06 20:36:29 · 1665 阅读 · 0 评论 -
【Redis】利用互斥锁解决缓存击穿问题
核心思路:相较于原来从缓存中查询不到数据后直接查询数据库而言,现在的方案是 进行查询之后,如果从缓存没有查询到数据,则进行互斥锁的获取,获取互斥锁后,判断是否获得到了锁,如果没有获得到,则休眠,过一会再进行尝试,直到获取到锁为止,才能进行查询。,这两种,拿到锁了就执行,没拿到锁就需要一直等待。接下来回到IDEA看一下日志,可以发现对数据库的查询其实只触发了一次,证明在高并发的场景下,我们并没有出现所有的请求都打到数据库的情况,而是只有一个人进来了,说明我们这块基于互斥锁的解决方案已经成功了。原创 2024-08-06 20:34:35 · 1667 阅读 · 0 评论 -
【Redis】缓存击穿问题及解决思路
如果缓存雪崩是无数key同时过期而引发的问题,那么缓存击穿就是部分key过期而导致的严重后果。那为什么部分key过期也会导致严重的后果?这是因为这部分key不是普通的key。缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。高并发访问:这个Key访问的非常多,可能是在做活动的某一件商品的缓存,同一时刻可能会有无数的请求来访问这一个key。原创 2024-08-06 20:33:52 · 1590 阅读 · 0 评论 -
【Redis】缓存雪崩问题及解决思路
所谓多级缓存:之前讲过,缓存的使用场景是多种多样的,不仅仅是说可以在应用层添加,请求从浏览器发出,浏览器是可以添加缓存的,但是浏览器的缓存一般缓存的是静态的数据,而对于需要从数据库中查询的动态数据是无法做缓存的,对于这部分我们还可以在反向代理服务器Nginx层面做缓存,Nginx缓存未命中,再去找我们的redis,redis未命中,到达我们的JVM,我们还可以在JVM内部建立本地缓存,最后是数据库,即在多个层面建立缓存,这样redis这一环崩了,还有很多别的缓存可以去弥补。的随机数,此时它的有效期就会在。原创 2024-08-06 07:36:28 · 468 阅读 · 0 评论 -
【Redis】编码解决商品查询的缓存穿透问题
缓存穿透产生的原因是什么?用户请求的数据在缓存中和数据库中都不存在,这样的请求一定会到达我们的数据库。如果不断发起这样的请求,就会给数据库带来巨大压力缓存穿透的解决方案有哪些?缓存null值(我们采用的方式)布隆过滤(采用一种哈希算法)上面两种是我们之前提到过的,但是缓存穿透任然不仅仅靠这两种解决,这两种其实属于一种被动的方案:人家已经来穿透你,然后你想办法去弥补。事实上我们也可以主动采取一些措施去解决缓存穿透。增强id的复杂度,避免被猜测id规律,这样一来它就不太容易输入一些自己编的id了。原创 2024-08-06 07:35:52 · 1040 阅读 · 0 评论 -
【Redis】缓存穿透问题的解决思路
并不是的,如果是这样,我们就多余存储了很多数据了。当我们客户端访问不存在的数据时,先请求redis,但是此时redis中没有数据,此时会访问到数据库,但是数据库中也没有数据,这个数据穿透了缓存,直击数据库,我们都知道数据库能够承载的并发不如redis这么高,如果大量的请求同时过来访问这种不存在的数据,这些请求就都会访问到数据库,如果是一个不怀好意的人,它整了无数的线程,并发的向这个不存在的数据发起请求,这样所有的请求都会到达数据库,很有可能就将我们的数据库搞垮,这就是缓存穿透带来的危害了。原创 2024-08-05 21:04:39 · 1040 阅读 · 0 评论 -
【Redis】实现商铺和缓存与数据库双写一致
在上节我们已经分析了缓存更新策略的最佳实践,因此这节课我们就动手来试试,。修改ShopController中的业务逻辑,满足下面的需求,需求分为两步,也就是我们查询和更新时要干的事:① 根据id查询店铺时,如果缓存未命中,则查询数据库,将数据库结果写入缓存,并设置超时时间,这样将来就可以做到超时剔除了② 根据id修改店铺时,先修改数据库,再删除缓存。原创 2024-08-05 21:03:56 · 983 阅读 · 0 评论 -
【Redis】缓存更新策略
这三种都有可能出现线程不安全的问题,但是方案二相对来讲出现的可能性更低。就算方案二的特殊情况真的发生了,我们将来加上一个超时时间就行了,万一写了旧数据也没关系,过一段时间就会清除。先操作数据库,再删除缓存。内存淘汰超时剔除主动更新,具体如何选择,需要看业务需求:1、低一致性需求:使用Redis自带的内存淘汰机制,最多加一个超时更新2、高一致性需求:主动更新,并以超时剔除作为兜底方案,这样才能在发生意外的时候保证数据的恢复。写回。原创 2024-08-05 21:03:02 · 1754 阅读 · 0 评论 -
【Redis】添加商户缓存
这样客户端请求就会优先到达我们的缓存,即redis,如果缓存数据存在,则直接从缓存中返回,如果缓存数据不存在,再查询数据库,然后将数据存入redis,这样将来随着用户请求的数据越来越多,redis中缓存的数据是不是也会越来越多,那么redis的命中率也就会越来越高,这样就形成了一个良性的工作模型了。在我们查询商户信息时,我们是直接操作从数据库中去进行查询的,大致逻辑是这样,直接查询数据库那肯定慢咯,所以我们需要增加缓存。代码思路:如果缓存有,则直接返回,如果缓存不存在,则查询数据库,然后存入redis。原创 2024-08-05 21:02:30 · 293 阅读 · 0 评论 -
【Redis】什么是缓存?
缓存就是数据交换的缓冲区(称作),是存贮数据的临时地方,一般读写性能较高。重点:读写性能高,这也是缓冲被用作数据交换的缓冲区的原因。比较典型的例子就是计算机,在计算机中典型的构造就是CPU、内存、硬盘。CPU的运算能力发展到现在已经非常非常厉害了,这种运算能力已经远远的超过了内存和磁盘的读写数据的能力,但是CPU做的任何的运算都需要先从内存 / 磁盘中读到数据放到自己的寄存器才能做运算。原创 2024-08-05 21:01:57 · 977 阅读 · 0 评论 -
【Redis】基于Redis实现短信登录
在这个方案中,他确实可以使用对应路径的拦截,同时刷新登录token令牌的存活时间,但是现在这个拦截器他只是拦截需要被拦截的路径,假设当前用户访问了一些不需要拦截的路径,那么这个拦截器就不会生效,所以此时令牌刷新的动作实际上就不会执行,所以这个方案他是存在问题的Redis代替session需要考虑的问题:选择合适的数据结构选择合适的key选择合适的存储粒度。我们在存储的时候并没有存完整的用户信息,而是将敏感信息去掉了,并且还节约了内存空间。原创 2024-08-05 21:00:30 · 480 阅读 · 0 评论 -
【Redis】Redis代替session的业务流程(设计key的结构)
Redis代替session,这个替代不是简单的说你将数据存入redis就行了,在业务流程上会有比较多的一些变化,这节就一起来分析一下基于redis代替session之后业务上有哪些变化。原创 2024-08-05 20:59:48 · 1135 阅读 · 0 评论 -
【Redis】session共享问题
早期的方案是session拷贝,就是说虽然每个tomcat上都有不同的session,但是每当任意一台服务器的session修改时,都会同步给其他的Tomcat服务器的session,这样的话,就可以实现session的共享了。可以发现redis正好就满足上面的要求。虽然我们的项目是一个单体式的项目,但是我们的tomcat将来为了应对并发,肯定还是要水平扩展,部署多个,形成多个负载均衡的集群,一旦形成负载均衡的集群,这个时候当请求进入nginx,此时它会做一个负载均衡,在多态nginx间做一个轮询。原创 2024-08-05 20:59:12 · 403 阅读 · 0 评论 -
【Redis】隐藏用户敏感信息
上节我们实现了登录校验的功能,但还是有一些小问题,登录校验的功能返回的信息有点多,但其实我们做登录用户的时候,其实只要返回用户的id、名称、头像等信息就够了,像时间、密码、电话这些敏感信息就没必要返回,因此这是有泄漏风险的。因此,从一开始我们存入session的就不应该是完整的信息,而是部分信息。采用的核心思路就是书写一个UserDto对象,这个UserDto对象就没有敏感信息了,我们在返回前,将有用户敏感信息的User对象转化成没有敏感信息的UserDto对象,那么就能够避免这个尴尬的问题了。原创 2024-08-05 20:58:20 · 892 阅读 · 0 评论 -
【Redis】实现登录拦截功能
实现HandlerInterceptor接口,并重写其所有方法这三个方法都有默认实现,根据需要重写快捷键:ctrl + O定义好后加上@Component,需要交给IOC容器管理@Override//1.获取session//2.获取session中的用户//3.判断用户是否存在//4.不存在,拦截,返回401状态码//5.存在,保存用户信息到Threadlocal// 由于前面获取到的是Object类型,因此这里需要强转//6.放行@Override。原创 2024-08-05 20:55:04 · 818 阅读 · 0 评论 -
【Redis】实现短信验证码登录和注册功能
在登录的时候是不需要返回登录凭证的,因为这里是基于session登录,session的原理就是cookie,每一个session都会有一个唯一的sessionId,你访问tomcat的时候,这个sessionId就已经自动的写到你的cookie中了,以后请求就会带着这个sessionId,带着这个sessionId过来我们就能找到session,找到session就能找到这个用户了。技巧:校验一般都是反向校验,这样可以避免if嵌套越来越深。原创 2024-08-05 20:54:11 · 284 阅读 · 0 评论 -
【Redis】实现发送短信验证码功能
进入个人页面的时候必须先登录,因此当你点击按钮的这一刻,它就会跳转到登录页面了,在登陆的时候需要先输入手机号,然后发送验证码,点击。,api是用来标识发向tomcat服务的请求,我们不用管,它会自动过滤掉,因此真正的请求路径应该是。的时候,一个请求就会发送到服务端了,而服务端要处理的就是这个请求。,拿到手机号我们就可以向这个手机号发验证码了。发送验证码,功能全部放到Service中做。重启服务测试,可以发现发送成功。原创 2024-08-05 20:52:59 · 372 阅读 · 0 评论 -
【Redis】基于Session实现登录流程
用户将验证码和手机号进行输入,后台从session中拿到当前验证码,然后和用户输入的验证码进行校验,如果不一致,则无法通过校验,如果一致,则后台根据手机号查询用户,如果用户不存在,则为用户创建账号信息,保存到数据库,无论是否存在,都会将用户信息保存到session中,方便后续获得当前登录信息。如果手机号合法,后台此时生成对应的验证码,同时将验证码进行保存,然后再通过短信的方式将验证码发送给用户。用户在提交手机号后,会校验手机号是否合法,如果不合法,则要求用户重新输入手机号。原创 2024-08-05 20:51:57 · 326 阅读 · 0 评论 -
【Redis】导入黑马点评项目
nginx基于七层模型走的事HTTP协议,可以实现基于Lua直接绕开tomcat访问redis,也可以作为静态资源服务器,轻松扛下上万并发, 负载均衡到下游tomcat服务器,打散流量,我们都知道一台4核8G的tomcat,在优化和处理简单业务的加持下,大不了就处理1000左右的并发, 经过nginx的负载均衡分流后,利用集群支撑起整个项目,同时nginx在部署了前端项目后,更是可以做到动静分离,进一步降低tomcat服务的压力,这些功能都得靠nginx起作用,所以nginx是整个项目中重要的一环。原创 2024-08-05 20:50:08 · 357 阅读 · 0 评论 -
【Redis】Hash结构操作
PS:在spring中并不是以命令作为方法名,方法名类似于Java的HashMap的方法名。hashKey和value就是对应字段和字段值,事实上value中可能会有多个字段,一个。就需要执行多次,这样其实就不太好了,等于你是与服务器做多次交互。在基础篇的最后,咱们对Hash结构操作一下,收一个小尾巴。只能存一个字段,如果有多个字段,拿到的就是跟哈希有关的操作。拿到的是对字符串的操作,因此推荐大家的方案是使用。,里面可以存好几个键值对。运行代码,可以发现插入成功。原创 2024-08-05 20:49:42 · 234 阅读 · 0 评论 -
【Redis】StringRedisTemplate
RedisTemplate的两种序列化实践方案:自定义RedisTemplate修改RedisTemplate的序列化器为GenericJackson2JsonRedisSerializer弊端:会占用额外空间,用来记录class字段使用StringRedisTemplate写入Redis时,手动把对象序列化为JSON读取Redis时,手动把读取到的JSON反序列化为对象。原创 2024-08-04 06:56:00 · 700 阅读 · 0 评论 -
【Redis】数据序列化器
并且最神奇的是,当我们去获取结果的时候,它还能自动的帮我们反序列化成了对象,它是怎么做到这个反序列化的呢?如下图,事实上在写入JSON的同时,还帮我们写出了一个class的属性,对应的就是User类的字节码名称,就是因为有这样的一条属性,所以它在反序列化的时候才能够读取到我们对应的字节码,即类的名称,然后再帮我们反序列化为对应的User写进来。因此我们希望的是:我写的是什么,你就存什么,所见即所得多好。的一个特殊功能,它可以接收任何类型的对象,然后帮我们转成redis可以处理的字节,因此我们存进去的。原创 2024-08-04 06:55:19 · 1167 阅读 · 0 评论