Redis高级应用

文章目录

      • 1.5.1 布隆过滤器BloomFilter
        • 1.5.1.1 原理
        • 1.5.1.2 使用场景
      • 1.5.2 Redis分布式锁
        • 1.5.2.1 使用案例分析
          • 1.5.2.1.1 单机版没加锁
          • 1.5.2.1.2 单节点Redis实现分布式锁
          • 1.5.2.1.3 集群下的分布式及CAP
          • 1.5.2.1.4 Redisson可靠分布式锁
          • 1.5.2.1.5 Redis分布式锁-Redlock算法
            • 1.5.2.1.5.1 多机案例
      • 1.5.3 Redisson源码分析
            • 1.5.3.1 单机案例
        • 1.5.3.2 加锁及可重入原理
        • 1.5.3.3 watchDog原理
        • 1.5.3.4 互斥原理
        • 1.5.3.5 解锁原理
        • 1.5.3.6 案例代码解释加锁和可重入
      • 1.5.4 Reids实战问题
        • 1.5.4.1 缓存双写一致性之更新策略探讨
          • 1.5.4.1.1 方案1:先更新数据库,再更新缓存
          • 1.5.4.1.2 方案2:先删除缓存,再更新数据库
            • 1.5.4.1.2.1 分布式锁防止缓存击穿
            • 1.5.4.1.2.2 延时双删策略
          • 1.5.4.1.3 方案3:先更新数据库,再删除缓存
          • 1.5.4.1.4 总结对比
        • 1.5.4.2 缓存预热+缓存雪崩+缓存击穿+缓存穿透
          • 1.5.4.2.1 缓存雪崩
          • 1.5.4.2.2 缓存穿透
          • 1.5.4.2.3 缓存击透
          • 案例:淘宝聚划算功能实现+防止缓存击穿

1.5.1 布隆过滤器BloomFilter

问题:现有50亿个电话号码,现有10万个电话号码,如何要快速准确的判断这些电话号码是否已经存在?
1、通过数据库查询-------实现快速有点难。
2、数据预放到内存集合中:50亿*8字节大约40G,内存太大了。




1.5.1.1 原理

实质就是一个大型位数组(初值0)和几个不同的无偏hash函数(无偏表示分布均匀)。

当有变量被加入集合时,通过N个映射函数将这个变量映射成位图中的N个点,
把它们置为 1(假定有两个变量都通过 3 个映射函数)。

若现在只存如obj1和obj2,如上图。
假设现在obj3,经过多个hash映射到1、3、8的位置,宏观上看我们知道obj3不存在,但是底层三个位置为1,最后给我的是true,故而只能确定为不一定存在

假设现在obj3,经过多个hash映射到1、3、4的位置,宏观上看我们知道obj4不存在,但是底层三个位置存在0,最后给我的是false,故而判定一定不存在
布隆过滤器误判率,为什么不要删除?(多个元素共享一个位置)

1.5.1.2 使用场景

解决缓存穿透的问题

黑名单

1.5.2 Redis分布式锁





1.5.2.1 使用案例分析

背景:秒杀,超卖现象分析

先总结:

  1. synchronized单机版OK,上分布式
  2. nginx分布式微服务单机锁不行/(ㄒoㄒ)/~~
  3. 取消单机锁,上redis分布式锁setnx
  4. 只加了锁,没有释放锁,出异常的话,可能无法释放锁,必须要在代码层面finally释放锁
  5. 宕机了,部署了微服务代码层面根本没有走到finally这块,没办法保证解锁,这个key没有被删除,需要有lockKey的过期时间设定
  6. 为redis的分布式锁key,增加过期时间,此外,还必须要setnx+过期时间必须同一行
  7. 必须规定只能自己删除自己的锁,你不能把别人的锁删除了,防止张冠李戴,1删2,2删3
  8. redis集群环境下,我们自己写的也不OK,直接上RedLock之Redisson落地实现
1.5.2.1.1 单机版没加锁

在单机环境下,可以使用synchronized或Lock来实现。
但是在分布式系统中,因为竞争的线程可能不在同一个节点上(同一个jvm中),
所以需要一个让所有进程都能访问到的锁来实现(比如redis或者zookeeper来构建)
不同进程jvm层面的锁就不管用了,那么可以利用第三方的一个组件,来获取锁,未获取到锁,则阻塞当前想要运行的线程

1.5.2.1.2 单节点Redis实现分布式锁

分布式部署后,单机锁还是出现超卖现象,需要分布式锁


问题1:若服务异常,无法释放锁,需代码层面finally释放锁

问题2:服务宕机,未执行finally,需给锁添加超时时间

问题3:设置key+过期时间分开了,必须要合并成一行具备原子性

问题4:删除了别人的锁,需 删除时判断是否是自己的锁

当锁的粒度粗,两次请求使用同一把锁,若业务执行时间长,锁过期时间短
第一次请求锁过期,业务还未执行完,此时
第二个请到来获取到锁,最后被第一请求删锁,故删除时需判断是否是自己的锁

此处判断是否是自己的锁根据,锁持有值判断,两次请求值不一样

问题5:finally块的判断+del删除操作不是原子性的

解决方案:Redis调用Lua脚本通过eval命令保证代码执行的原子性

问题6:确保redisLock过期时间大于业务执行时间的问题(Redis分布式锁如何续期?)

1.5.2.1.3 集群下的分布式及CAP

确保redisLock过期时间大于业务执行时间的问题(Redis分布式锁如何续期?)
集群+CAP,redis对比zookeeper

Zookeeper集群的CAP:


1.5.2.1.4 Redisson可靠分布式锁


上述代码解决了续期问题(Redisson),
问题1: 但无锁时执行unlock,报错,需判断

尝试解锁,没锁当前线程节点id

1.5.2.1.5 Redis分布式锁-Redlock算法

使用场景:多个服务间保证同一时刻同一时间段内同一用户只能有一个请求(防止关键业务出现并发攻击)
Redis分布式锁比较正确的姿势是采用redisson这个客户端工具

1.5.2.1.5.1 多机案例

基于setnx的分布式锁有什么缺点?解决方案红锁


破坏了锁的排他特性,同一时间只能有一个建redis锁成功并持有锁,严禁出现2个以上的请求线程拿到锁。危险的

Redlock:
redis之父提出了Redlock算法解决这个问题:Redis也提供了Redlock算法,用来实现基于多个实例的分布式锁。锁变量由多个实例维护,即使有实例发生了故障,锁变量仍然是存在的,客户端还是可以完成锁操作。Redlock算法是实现高可靠分布式锁的一种有效解决方案,可以在实际开发中使用。

设计理念:

该方案为了解决数据不一致的问题,直接舍弃了异步复制只使用 master 节点,同时由于舍弃了 slave,为了保证可用性,引入了 N 个节点,官方建议是 5。

解决方案:采用3个说明

为什么是奇数? N = 2X + 1 (N是最终部署机器数,X是容错机器数)

那你知道redis之父设计的这个方案,还有bug吗?分布式难以避免的,系统时钟影响
如果线程1从3个实例获取到了锁。但是这3个实例中的某个实例的系统时间走的稍微快一点,则它持有的锁会提前过期被释放,当他释放后,此时又有3个实例是空闲的,则线程2也可以获取到锁,则可能出现两个线程同时持有锁了。

1.5.3 Redisson源码分析

使用:

1.5.3.1 单机案例

一般中小公司,不是高并发场景,是可以使用的。单机redis小业务也撑得住

加锁关键逻辑:

NX:等效setNx,key不存在才能加锁成功
解锁关键逻辑:

1.5.3.2 加锁及可重入原理





1.5.3.3 watchDog原理

看门狗是为了解决业务耗时,锁过期失效问题
场景:A、B线程执行同一业务方法,A加分布式锁,锁过期失效,A还没执行完,B获取到锁执行,可能会出现安全问题
解决方法:在获取锁成功后,给锁加一个watchdog后台线程,定期检查(每1/3的锁时间检查1次),如果线程还持有锁,则刷新过期时间
加锁成功触发监听执行如下方法,

这里面初始化了一个定时器,dely 的时间是 internalLockLeaseTime/3。
在 Redisson 中,internalLockLeaseTime 是 30s,也就是每隔 10s 续期一次,每次 30s。

客户端A加锁成功,就会启动一个watch dog看门狗,他是一个后台线程,会每隔10秒检查一下,如果客户端A还持有锁key,那么就会不断的延长锁key的生存时间,默认每次续命又从30秒新开始

1.5.3.4 互斥原理

当其他线程进入临界资源,发现存在分布式锁,会执行到加锁lua脚本中

“return redis.call(‘pttl’, KEYS[1]);” // 如果已经锁定,但并非本线程,返回锁还有锁失效ttl


1.5.3.5 解锁原理

1.5.3.6 案例代码解释加锁和可重入



1.5.4 Reids实战问题


目的:最终一致性

1.5.4.1 缓存双写一致性之更新策略探讨




1.5.4.1.1 方案1:先更新数据库,再更新缓存

方案1:先更新数据库,再更新缓存
问题分析:更新db成功,更新缓存失败(缓存还是旧的),导致db和缓存数据不一致

1.5.4.1.2 方案2:先删除缓存,再更新数据库

方案2:先删除缓存,再更新数据库问题1:请求A删缓存成功,写db业务耗时,此时请求B查询数据,会出现两个问题:
请求B从mysql中获取旧值
请求B获取的旧值写回redis
请求A更新完成,但是缓存缓存中是旧数据

1.5.4.1.2.1 分布式锁防止缓存击穿

解决方案1:阿里内部缓存击穿的方案
多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个互斥锁来锁住它。其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后做缓存。后面的线程进来发现已经有缓存了,就直接走缓存。

1.5.4.1.2.2 延时双删策略



优化写法,做成组件,通过aop切入。
问题:

这个删除该休眠多久呢?
统计下线程读数据和写缓存的操作时间,自行评估自己的项目的读数据业务逻辑的耗时,
以此为基础来进行估算。然后写数据的休眠时间则在读数据业务逻辑的耗时基础上加百毫秒即可。
思考:即使写在读上加时间,在高并发下还是不能得到保证的。分析:若读将旧值写入缓存,写更新完db还未删除缓存,由于高并发读在写更新完db和未删除缓存空隙读取就是脏数据。

问题:写操作在读操作基础上加时间,降低了吞吐
解决方案:延时删除采用异步线程

上述效果是mysql单机,如果mysql主从读写分离架构如何?

1.5.4.1.3 方案3:先更新数据库,再删除缓存


解决方案:canal+mq方案

  1. 可以把要删除的缓存值或者是要更新的数据库值暂存到消息队列中(例如使用Kafka/RabbitMQ等)。
  2. 当程序没有能够成功地删除缓存值或者是更新数据库值时,可以从消息队列中重新读取这些值,然后再次进行删除或更新。
  3. 如果能够成功地删除或更新,我们就要把这些值从消息队列中去除,以免重复操作,此时,我们也可以保证数据库和缓存的数据一致了,否则还需要再次进行重试
  4. 如果重试超过的一定次数后还是没有成功,我们就需要向业务层发送报错信息了,通知运维人员。
1.5.4.1.4 总结对比

方案2和方案3用那个?利弊如何
优先使用方案3:先更新数据库,再删除缓存。理由如下:

方案2难点:

  1. 先删除缓存值再更新数据库,有可能导致请求因缓存缺失而访问数据库,给数据库带来压力,严重导致打满mysql。(为防止缓存击穿,读需加分布式锁)
  2. 如果业务应用中读取数据库和写缓存的时间不好估算,那么,延迟双删中的等待时间就不好设置

方案3保证的是最终一致性
若要保证强一致:读写接口使用读写锁

落地方案推荐:方案3,使用canal+mq的异步删除

1.5.4.2 缓存预热+缓存雪崩+缓存击穿+缓存穿透



缓存预热:热点数据放入缓存、经常使用的静态数据放入缓存

1.5.4.2.1 缓存雪崩


其他解决方案:

1.5.4.2.2 缓存穿透


解决方案1:空对象缓存或者缺省值

存在问题:黑客或恶意攻击

大量不同id打击,也会造成数据压力

方案2:布隆过滤器

方案2.1:Google布隆过滤器Guava解决缓存穿透(缺点:单机)
方案2.2:Redis布隆过滤器解决缓存穿透

基于布隆过滤器的白名单架构:

黑名单架构:

1.5.4.2.3 缓存击透

热点key失效,导致大量请求打到mysql

方案1:加分布式锁(互斥独占锁防止击穿),获得锁的请求会将查询数据放入缓存,后续请求查缓存
方案2:对于访问频繁的热点key,干脆就不设置过期时间

案例:淘宝聚划算功能实现+防止缓存击穿

秒杀商品分页展示
分析过程:

redis数据类型选型:list

采用定时器将参与聚划算活动的特价商品新增进入redis中:

问题:QPS上1000后导致可怕的缓存击穿

解决方案:定时轮询,互斥更新,差异失效时间

定时任务修改

Controller修改:

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

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

相关文章

Linux之进程间通信(system V 共享内存)

目录 一、共享内存 1、基本原理 2、共享内存的创建 3、共享内存的释放 4、共享内存的关联 5、共享内存的去关联 6、查看IPC资源 二、完整通信代码 三、共享内存的特点 四、信号量 1、相关概念 2、信号量概念 进程间通信的本质就是让不同的进程看到同一个资源。而前…

微信小程序(二十)Vant组件库的配置

教程很详细,直接上过程 上一篇 官方文档也有,但是因为版本的更新,官方文档并没有跟着改变,这里我写一份最新版能用的教程 (口头禅还是不能少的🤣🤣🤣) 灵魂拷问&#xf…

Android Studio 提示Use app:drawableStartCompat instead of android:drawableStart

每次提交代码时,AS这个老妈子总爱唠叨一堆warning,这些Warning都在讲什么? 1.Use app:drawableStartCompat instead of android:drawableStart 在Android开发中,android:drawableStart和app:drawableStartCompat是两个用于设置…

基于YOLOv8的摄像头吸烟行为检测系统(Python源码+Pyqt6界面+数据集)

💡💡💡本文主要内容:详细介绍了摄像头下吸烟行为检测系统,在介绍算法原理的同时,给出Pytorch的源码、训练数据集以及PyQt6的UI界面。在界面中可以选择各种图片、视频进行检测识别,可进行置信度、Iou阈值设定…

智能分析网关V4智慧冶金工厂视频智能监管方案

一、背景与需求 随着工业4.0的推进,冶金行业正面临着转型升级的压力。为了提高生产效率、降低能耗、保障安全,冶金智能工厂视频监管方案应运而生。该方案通过高清摄像头、智能分析技术、大数据处理等手段,对工厂进行全方位、实时监控&#xf…

详细解读vcruntime140_1.dll修复的手段,如何快速解决vcruntime140_1.dll丢失问题

当出现“无法找到vcruntime140_1.dll”或程序“未能正常启动”时,这通常指示系统中缺失了一个关键文件:vcruntime140_1.dll。作为Visual C Redistributable组件的一部分,这个小文件在很多用Visual Studio编译的C程序运行时发挥着重要作用。解…

Qslog开源库使用

Qslog源码下载地址:https://github.com/victronenergy/QsLog 1.QSLOG使用方式 (1)源码集成 在你的工程中,直接包含QsLog.pri文件,进行源码集成。当然你也可以包含QsLog.pri后,编译为xx.dll,在…

MVCC原理讲解(深入浅出)

目录 一、什么是MVCC 二、当前读、快照读都是什么鬼 三、当前读 四、快照读 五、数据库的并发场景 六、MVCC解决并发的哪些问题 1.解决问题如下: 七、MVCC的实现原理 1.版本链 八、undo日志 1.undo log 的用途 2.undo log主要分为两种 九、Read View…

简洁思路推理 KMP 算法——子字符串匹配

例题 28. 找出字符串中第一个匹配项的下标 暴力遍历解法 枚举原串 ss 中的每个字符作为「发起点」,每次从原串的「发起点」和匹配串的「首位」开始尝试匹配: 匹配成功:返回本次匹配的原串「发起点」。 匹配失败:枚举原串的下一个…

UI自动化搭建背景及优劣势分析

经常有人会问,什么样的项目才适合进行UI自动化测试呢?UI自动化测试相当于模拟手工测试,通过程序去操作页面上的控件。而在实际测试过程中,经常会遇到无法找到控件,或者因控件定义变更而带来的维护成本等问题。 哪些场…

Docker—入门及Centos7安装

1、Docker入门 1.1、Docker是什么? Docker是基于Go语言实现的云开源项目。 Docker的主要目标是“Build,Ship,and Run Any App,Anywhere”,也就是通过对应组件的封装、分发、部署、运行等生命周期的管理,使用户的APP&…

docker环境搭建及其安装常用软件

centos安装docker Install Docker Engine on CentOS | Docker Docs 下载docker sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo yum install -y docker-ce docker-ce-cli containerd.io…

MongoDB本地部署并结合内网穿透实现公网访问本地数据库

文章目录 前言1. 安装数据库2. 内网穿透2.1 安装cpolar内网穿透2.2 创建隧道映射2.3 测试随机公网地址远程连接 3. 配置固定TCP端口地址3.1 保留一个固定的公网TCP端口地址3.2 配置固定公网TCP端口地址3.3 测试固定地址公网远程访问 4. 结语 前言 MongoDB是一个基于分布式文件…

TCP 三次握手 四次挥手以及滑动窗口

TCP 三次握手 简介: TCP 是一种面向连接的单播协议,在发送数据前,通信双方必须在彼此间建立一条连接。所谓的 “ 连接” ,其实是客户端和服务器的内存里保存的一份关于对方的信息,如 IP 地址、端口号等。 TCP 可以…

【c语言】详解操作符(下)

前言: 在上文中,我们已经学习了 原码、反码、补码、移位 操作符、移位操作符、位操作符、逗号表达式、下标访问[ ]、函数调用( ),接下来我们将继续学习剩下的操作符。 1. 结构成员访问操作符 1.1 结构体成员的直接访…

台式电脑的ip地址在哪里找

在网络连接方面,IP地址是非常重要的信息,它是用于标识网络设备的唯一地址。对于台式电脑用户来说,了解自己设备的IP地址是非常有必要的,因为它可以帮助解决网络连接问题,进行远程访问和共享文件等功能。本文将指导读者…

深度学习知识

context阶段和generation阶段的不同 context阶段(又称 Encoder)主要对输入编码,产生 CacheKV(CacheKV 实际上记录的是 Transformer 中 Attention 模块中 Key 和 Value 的值),在计算完 logits 之后会接一个Sampling 采…

幻兽帕鲁服务器多少钱?4核16G支持32人在线吗?

4核16G服务器是幻兽帕鲁Palworld推荐的配置,阿里云和腾讯云均推出针对幻兽帕鲁的4核16G服务器,阿里云4核16G幻兽帕鲁专属服务器32元1个月、66元3个月,腾讯云4核16G14M服务器66元1个月、277元3个月、1584元一年。云服务器吧yunfuwuqiba.com分享…

vue笔记(一):部署

首 文档 安装 1、安装nodejs(链接),18.0以上版本。 2、在想要创建项目的目录下执行命令 npm create vuelatest 按提示创建项目,其中vue router是实现路由功能,pinia实现组件之间共享数据。如果项目需要两个功能建…

python-自动化篇-运维-监控-简单实例-道出如何使⽤Python进⾏网络监控?

如何使⽤Python进⾏⽹络监控? 使⽤Python进⾏⽹络监控可以帮助实时监视⽹络设备、流量和服务的状态,以便及时识别和解决问题。 以下是⼀般步骤,说明如何使⽤Python进⾏⽹络监控: 选择监控⼯具和库:选择适合⽹络监控需…