redis分布式锁及ZK锁的区别?缓存穿透、击穿、雪崩及布隆过滤器
mhr18 2024-12-13 12:59 24 浏览 0 评论
1、使用redis如何设计分布式锁?
(1)最普通的实现方式,就是在redis里创建一个key来加锁。
语法:SET my:lock 随机值 NX PX 30000
用这个命令就ok,这个NX的意思就是只有key不存在的时候才会设置成功,PX 30000的意思是30秒后锁自动释放。别人创建的时候如果发现已经有了就不能加锁了。
释放锁就是删除key,但是一般可以用lua脚本删除,判断value一样才删除。
如:
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
为啥要用随机值呢?
因为如果某个客户端获取到了锁,但是阻塞了很长时间才执行完,此时可能已经自动释放锁了,此时可能别的客户端已经获取到了这个锁,要是你这个时候直接删除key的话会有问题,所以得用随机值加lua脚本来释放锁。
但是这样是肯定不行的。因为如果是普通的redis单实例,那就是单点故障。或者是redis普通主从,那redis主从异步复制,如果主节点挂了,key还没同步到从节点,此时从节点切换为主节点,别人就会拿到锁。
(2)RedLock算法有效防止单点故障
这个场景是假设有一个redis cluster,有5个redis master实例。然后执行如下步骤获取一把锁:
1)获取当前时间戳,单位是毫秒。
2)跟上面类似,轮流尝试在每个master节点上创建锁,过期时间较短,一般就几十毫秒。
3)尝试在大多数节点上建立一个锁,比如5个节点就要求是3个节点(n / 2 +1)。
4)客户端计算建立好锁的时间,如果建立锁的时间小于超时时间,就算建立成功了。
5)要是锁建立失败了,那么就依次删除这个锁。
6)只要别人建立了一把分布式锁,你就得不断轮询去尝试获取锁。
(3)集成redisson,用redisson的api进行操作
代码原理就是基于lua脚本加监听机制(watchdog监听)来实现的,就不用文字描述了,以下流程是看源码所画,如下,按序号进行查看,虚线为备注:
redisson的实现RedLock的方式,如下:
关于redisson的更多锁实现方式及如果看不清楚,可私信我获取。
2、redis分布式锁和zk分布式锁的对比
redis分布式锁,其实需要自己不断去尝试获取锁,比较消耗性能。
zk分布式锁,获取不到锁,注册个监听器即可,不需要不断主动尝试获取锁,性能开销较小
另外一点就是,如果是redis获取锁的那个客户端出bug了或者挂了,那么只能等待超时时间之后才能释放锁;而zk的话,因为创建的是临时znode,只要客户端挂了,znode就没了,此时就自动释放锁。
至于ZK的加锁方式,后面会进行分享。
3、缓存穿透
正常情况下,我们去查询数据都是存在的。那么请求去查询一条压根数据库中根本就不存在的数据,也就是说缓存和数据库都查询不到这条数据,但是请求每次都会打到数据库上面去。这种查询不存在数据的现象我们称为缓存穿透。
解决办法:
1、那么我们就可以为这些key 设置的值设置为null ,并且设置过期时间丢到缓存里面去。后面再出现查询这个key 的请求的时候,直接返回null ,就不用在到数据库中。但这种可能会存在空间的浪费。
2、采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的BitSet中,不存在的数据将会被拦截掉,从而避免了对底层存储系统的查询压力。
布隆过滤器
布隆过滤器是Burton Howard Bloom在1970年提出来的,一种空间效率极高的概率型算法和数据结构,主要用来判断一个元素是否在集合中存在。因为他是一个概率型的算法,所以会存在一定的误差,如果传入一个值去布隆过滤器中检索,可能会出现检测存在的结果但是实际上可能是不存在的,但是肯定不会出现实际上不存在然后反馈存在的结果。因此,Bloom Filter不适合那些"零错误"的应用场合。而在能容忍低错误率的应用场合下,Bloom Filter 通过极少的错误换取了存储空间的极大节省。
bitmap
所谓的Bit-map就是用一个bit位来标记某个元素对应的Value,通过Bit为单位来存储数据,可以大大节省存储空间。
所以我们可以通过一个int型的整数的32比特位来存储32个10进制的数字,那么这样所带来的好处是内存占用少、效率很高(不需要比较和位移)比如我们要存储5(101)、3(11)四个数字,那么我们申请int型的内存空间,会有32个比特位。这四个数字的二进制分别对应从右往左开始数,比如第一个数字是5,对应的二进制数据是101, 那么从右往左数到第5位,把对应的二进制数据存储到32个比特位上。
第一个5就是 00000000000000000000000000101000
输入3时候 00000000000000000000000000001100
布隆过滤器原理
有了对位图的理解以后,我们对布隆过滤器的原理理解就会更容易了,以40亿数据为案例,假设这40亿数据为某邮件服务器的黑名单数据,邮件服务需要根据邮箱地址来判断当前邮箱是否属于垃圾邮件。原理如下 :
假设集合里面有3个元素{x, y, z},哈希函数的个数为3。首先将位数组进行初始化,将里面每个位都设置位0。对于集合里面的每一个元素,将元素依次通过3个哈希函数进行映射,每次映射都会产生一个哈希值,这个值对应位数组上面的一个点,然后将位数组对应的位置标记为1。查询W元素是否存在集合中的时候,同样的方法将W通过哈希映射到位数组上的3个点。如果3个点的其中有一个点不为1,则可以判断该元素一定不存在集合中。反之,如果3个点都为1,则该元素可能存在集合中。
接下来按照该方法处理所有的输入对象,每个对象都可能把bitMap中一些白位置涂黑,也可能会遇到已经涂黑的位置,遇到已经为黑的让他继续为黑即可。处理完所有的输入对象之后,在bitMap中可能已经有相当多的位置已经被涂黑。至此,一个布隆过滤器生成完成,这个布隆过滤器代表之前所有输入对象组成的集合。
如何去判断一个元素是否存在bit array中呢? 原理是一样,根据k个哈希函数去得到的结果,如果所有的结果都是1,表示这个元素可能(假设某个元素通过映射对应下标为4,5,6这3个点。虽然这3个点都为1,但是很明显这3个点是不同元素经过哈希得到的位置,因此这种情况说明元素虽然不在集合中,也可能对应的都是1)存在。 如果一旦发现其中一个比特位的元素是0,表示这个元素一定不存在。
至于k个哈希函数的取值为多少,能够最大化地降低错误率(因为哈希函数越多,映射冲突会越少),这个地方就会涉及到最优的哈希函数个数的一个算法逻辑。
4、缓存击穿
缓存击穿是在平常高并发的系统中,大量的请求同时查询一个 key 时,此时这个key 正好失效了,就会导致大量的请求都打到数据库上面去。这种现象我们成为击穿。会造成某一时刻数据库请求量过大,压力剧增。
如何解决
上面的现象是多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个互斥锁(同一个客户端可有用双简锁的方式来实现,如集群方试,可用分布式锁)来锁住它。其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后做缓存。后面的线程进来发现已经有缓存了,就直接走缓存。
5、缓存雪崩
缓存雪崩是在用缓存的情况下,当某一时刻发生大规模的缓存失效的情况。比如你的缓存服务过期或宕机了,会有大量的请求进来直接打到DB上面。结果就是DB 撑不住挂掉。
解决办法
事前:使用集群缓存,保证缓存服务的高可用。
事中:使用 ehcache 本地缓存 + Hystrix 限流&降级 ,避免 MySQL 被打死的情况发生。
事后:开启 Redis 持久化机制,尽快恢复缓存集群。
1. 对缓存的访问,如果发现从缓存中取不到值,那么通过加锁或者队列的方式保证缓存的单进程操作,从而避免失效时并发请求全部落到底层的存储系统上;但是这种方式会带来性能上的损耗。
2. 将缓存失效的时间分散,降低每一个缓存过期时间的重复率。
3. 如果是因为缓存服务器故障导致的问题,一方面需要保证缓存服务器的高可用、另一方面,应用程序中可以采用多级缓存。
热点数据集中失效问题怎么解决
我们在设置缓存的时候,一般会给缓存设置一个失效时间,过了这个时间,缓存就失效了。对于一些热点的数据来说,当缓存失效以后会存在大量的请求过来,然后打到数据库去,从而可能导致数据库崩溃的情况。
解决办法:
设置不同的失效时间或加互斥锁。
相关推荐
- IT界10倍高效学习法!用这种方式,一年学完清华大学四年的课程
-
有没有在某一个瞬间,让你放弃学编程刚开始学python时,我找了几十本国内外的python编程书籍学习后,我还是似懂非懂,那些书里面到处都是抽象的概念,复杂的逻辑,这样的书,对于专业开发者来说,在平常...
- 数据库设计原则:构建高效可靠的数据存储
-
简介数据库设计是软件开发中至关重要的一环,它决定了数据在系统中的存储方式和组织结构。良好的数据库设计可以提高系统的性能、可靠性和可维护性。本文将介绍一些常见的数据库设计原则,包括范式化、冗余与一致性、...
- Deno 操作 Oracle 数据库全解析:从入门到精通的实战指南
-
在企业级应用开发中,Oracle数据库凭借其强大的功能、高可靠性和安全性,一直是大型系统的首选数据存储方案。Deno作为新一代JavaScript/TypeScript运行时,以安全、高效和现...
- APIJSON快速入门-零后端代码,接口所见即所得
-
1.流程说明一个接口的开发,比如Java用SpringBoot,Mybatis来开发一般来说就像下面这个流程部署上这个项目后,流程变成了这样如果使用apijson-framework,还可进一步简化...
- 每天从外包系统同步百万数据,用什么方案?Java实战讲解
-
用户问的是每天从外包系统同步百万数据,用什么方案,还要Java实战讲解。首先,我需要明确用户的需求。他们每天需要处理的数据量是百万级别,这个量级不算特别大,但也要考虑效率和稳定性。可能用户是企业的开发...
- SpringBoot条件化配置(@Conditional)全面解析与实战指南
-
一、条件化配置基础概念1.1什么是条件化配置条件化配置是Spring框架提供的一种基于特定条件来决定是否注册Bean或加载配置的机制。在SpringBoot中,这一机制通过@Conditional...
- 禁止join,我该怎么办(禁止后不再提示是什么意思)
-
join是什么SQL中JOIN子句用于把来自两个或多个表的行结合起来。在实际的数据库应用中,经常需要从多个数据表中读取数据,这时就可以使用SQL语句中的连接(JOIN)子句,在两个或多个数据...
- Spring IoC Container 原理解析(spring的ioc底层实现原理)
-
IoC、DI基础概念关于IoC和DI大家都不陌生,我们直接上martinfowler的原文,里面已经有DI的例子和spring的使用示例《InversionofControlContainer...
- SUSE再次宣布推出自己的 RHEL 兼容发行版
-
SUSE再次宣布推出自己的RHEL兼容发行版变色龙牧场主选择了一条新的岩石路线周三12七月2023SUSE正在重新考虑去年初所做的改变:它正在推出自己的RHEL兼容发行版,或者用它的话说...
- Seatunnel介绍与datax对比及使用教程
-
什么是SeatunnelSeaTunnel是一款极简易用、性能卓越的分布式数据集成平台。它以轻量高效的架构设计,为用户提供了一站式数据集成解决方案,可轻松实现跨系统、跨格式的数据实时/批量同步与...
- Spring Boot 大屏展示,私活项目,已开源,接私活必备,真香
-
一款基于代码生成器的低代码平台!前后端分离架构SpringBoot2.x,SpringCloud,AntDesign&Vue,Mybatis-plus,Shiro,JWT,支持微服务。强大的代码生...
- 互联网大厂后端必看Spring Boot3与Oracle数据库的高效连接全攻略
-
你是不是在使用SpringBoot3开发项目时,正为连接Oracle数据库而头疼?明明按照常规方法配置,却总是出现各种错误,耗费大量时间却毫无进展?别担心,这篇文章就是为你量身定制的!开发现...
- Oracle 和 Microsoft 推出 Oracle Exadata 数据库服务
-
据oracle网3月20日报道,Oracle宣布在OracleDatabase@Azure的百万兆次级基础设施上全面推出OracleExadata数据库服务。百万兆次级部署选项无需供应...
- Oracle数据库中查询最前面的100条数据,三种方法对比
-
在Oracle数据库中查询最前面的100条数据,可通过多种方法实现,不同方法适用于不同场景和版本。以下是基于当前技术文档的三种核心方案及其适用性分析:一、使用ROWNUM伪列(通用方案,全版本适用...
- Oracle数据库中判断字段不为空?(oracle判断字段是否包含数字)
-
Oracle数据库中如何判断字段不为空在Oracle数据库中,判断字段(列)不为空通常涉及到几种不同的场景和需求。下面是一些常见的方法来检查字段是否不为空:1.使用NVL函数NVL函数可以用来将NU...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- IT界10倍高效学习法!用这种方式,一年学完清华大学四年的课程
- 数据库设计原则:构建高效可靠的数据存储
- Deno 操作 Oracle 数据库全解析:从入门到精通的实战指南
- APIJSON快速入门-零后端代码,接口所见即所得
- 每天从外包系统同步百万数据,用什么方案?Java实战讲解
- SpringBoot条件化配置(@Conditional)全面解析与实战指南
- 禁止join,我该怎么办(禁止后不再提示是什么意思)
- Spring IoC Container 原理解析(spring的ioc底层实现原理)
- SUSE再次宣布推出自己的 RHEL 兼容发行版
- Seatunnel介绍与datax对比及使用教程
- 标签列表
-
- 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)