众所周知,redis是一个开源的高性能的基于内存运行的键值对数据库,现在众多商品信息网站和app在应用着redis,那么redis的安全性能也就值得注意,本文基于redis如何实现分布式锁和锁的一些问题进行讨论,都是个人总结的,借鉴自敖丙大佬,若有偏差,还请多多批评!
既然是分布式锁,萌新肯定会疑问,为什么会有分布式锁,这里就先大概说一下分布式锁是怎么来的。
框架的演进:
传统互联网最开始的架构为单体架构,即前端和后端都在一个web项目中,部署到一台tomcat容器上,各部分之间紧密解耦,不宜开发和扩展。
这时我们将单体架构内部应用拆开,具体的按照分类打包项目,垂直拓展成多个模块,分属不同项目,解决了并发,但是就会出现很多重复代码,且相互之间无法调用,这就为分布式的发展提供了动力。
在一个分布式的系统中我们将整个服务拆分成服务层和表现层,服务层提供服务,表现层控制页面交互,解决了代码重复,但是又出现了多层调用的关系。
这时我们出现了SOA及微服务,开始使用注册中心来对各集群实时管理,开始拥有资源调度,治理中心,开始面向服务,将服务进行原子化的彻底拆分。
1. redis分布式锁的实现
这里我们在如此大的服务中大量使用MySQL的话,效率就会很低,因为MySQL的数据是存储到硬盘的,对硬盘操作,尽管有很多优化但依旧无法满足高并发高效率,所以我们使用redis内存式数据库,数据在内存上操作,速度会快到飞起,这时我们可能就要考虑到安全问题了,就像MySQL一样,大家都知道现在大多都是微服务架构,一个应用会部署很多个进程,如果这些进程同时修改MySQL中的一条数据,为了避免数据出现错误,就要引入分布式锁来保证数据的安全可读。
分布式锁的实现是有很多途径的,可以是一个外部服务来给你上锁,也可以是内部的命令,在现实里,应该redis和zookeeper用的多。
这个分布式锁的功能就是一个请求来调用一条数据,进行上锁,并在使用期间,将过来的其他请求都拒绝。
在redis里我们实现分布式锁是通过内部命令SETNX(set if not exist)来实现,不同的是我们需要先判断一下这条数据是否有锁,没有锁才会上锁,有锁则会请求失败。
setnx lock 1
(integer) 1 //加锁成功
setnx lock 1
(integer) 0 //加锁失败
当请求使用完这个资源也要及时释放锁,这样其他请求才能再来访问,释放锁我们只要删除掉它即可。
del lock
(integer) 1 //释放成功
实现很简单,但是貌似也存在很大问题!
如果我们一条请求过来访问数据,加上锁,这时出现问题,网络断开或者出现异常,这条请求挂掉了,没有及时释放锁,这条数据不就锁死了???
2.死锁的处理
那其实更简单的方法就是给锁一个计时器即过期时间,到了过期时间,自动释放就好了, Redis同样给我们提供了这个功能
set lock 1 ex 10 nx
OK
10s是过期时间,且为一条命令保证了事务的原子性。
但貌似依旧有问题,很自然就会想到,这个过期时间怎么评估?如果一条请求在使用资源时,锁过期释放了,另一条请求过来,加上锁,开始操作,这时前一条请求刚操作完,默认释放锁,不就释放了正在操作的请求的锁吗?
这里可以看出两个问题:
1.释放别人锁怎么处理
2.过期时间如何评估
3.释放别人锁怎么办?
其实也很简单,本质上还是一条请求拥有释放别人锁的权限,如果每条请求都有自己的锁,且只能释放自己的锁,问题就可以解决了,这个可以通过一个唯一标识来完成,如UUID或者线程的id,上锁时将id标为锁,释放锁时判断锁是否等于自己的id,等于才可以del,但是这里显然不止一条命令,无法保证原子性,即多条命令如果无法同时执行成功,就会出现问题!
这时我们可以用lua脚本写解锁的这个判断过程,并植入redis,因为Redis是单线程,所以执行一个脚本时是不会中断的。释放别人锁的问题就解决了。
4. 过期时间如何评估?
这其实是根据具体需求具体考虑的,但我们还是分析一种大多数情况,我们可以设计一个守护进程,运用心跳机制连接着锁,判断锁快过期了操作还没有执行完,就重新设置过期时间,实现锁的自动续期。
这里redis也帮我们解决了---------Redisson!
这是一个基于java实现的redis SDK的客户端,在使用分布式锁的时候,它采用了自动续期的方案,完美解决了过期时间不好评估的问题。Redisson里面不仅集成了各种锁,可重入锁,乐观锁,红锁等等,非常好用的。
这样分布式就简单实现了。
但是redis真的就没有问题了嘛?前面说的都是单机的情况,而大家都知道,实际应用中我们都是用主从+哨兵的模式来部署的,主库宕机,哨兵会自动故障切换,将从库提升为主库,数据同步,实现可用性。
那么在主从切换的时候,分布式锁显然也可能出现问题,只要是通过多步完成的动作,其中一步出现问题整个就会出现问题。
这里就需要使用Redlock!!!
redis的设计者提出了红锁解决方案,在网上也有不同的讨论,有人支持有人提问有人反对,这里不做过多描述,不过Redlock的实现原理还是很精妙的,推荐大家去找敖丙大佬的博客看一看,很值得思考。
整理不易,如果你喜欢本文的话请多多点赞收藏分享!
最后,学习之路永无止境,大家共勉!