大家好,我是散步coding, 最近在整顿2022年Redis最新面试题, 大家也能够通过我上面的博客地址在线浏览, 明天讲讲第7篇 - Redis分布式锁。 本文首发于公众号: 散步coding
2022年Redis最新面试题目录
- Redis基础知识
- Redis数据结构
- Redis事务
- Redis数据长久化
- Redis集群
- Redis淘汰策略
- Redis分布式锁
- Redis缓存问题
- 运维和部署
你晓得实现实现分布式锁有哪些计划?
呈现概率: ★★★★★
分布式锁无论是在平时开发中还是面试中都是很常见的问题, 所以倡议本人多梳理一下。知其然, 且知其所以然。
1、在开始之前首先要晓得什么是分布式锁 ?
分布式锁其实就是管制分布式系统不同过程独特访问共享资源的一种锁的实现。如果不同的零碎或同一个零碎的不同主机之间共享了某个临界资源,往往须要互斥来避免彼此烦扰,以保障一致性。
在分布式锁方面, Redis有广泛应用, 日常开发中分布式锁的一些常见常见有秒杀下单、抢红包等等。
图片
2、分布式锁的特点如下:
互斥性:和咱们本地锁一样互斥性是最根本,然而分布式锁须要保障在不同节点的不同线程的互斥。
可重入性:同一个节点上的同一个线程如果获取了锁之后那么也能够再次获取这个锁。
锁超时:和本地锁一样反对锁超时,避免死锁。
高效,高可用:加锁和解锁须要高效,同时也须要保障高可用避免分布式锁生效,能够减少降级。
反对阻塞和非阻塞:和 ReentrantLock 一样反对 lock 和 trylock 以及 tryLock(long timeOut)。
反对偏心锁和非偏心锁(可选):偏心锁的意思是依照申请加锁的程序取得锁,非偏心锁就相同是无序的。这个一般来说实现的比拟少。
3、一些比拟常见的分布式锁计划
SETNX + EXPIRE
SETNX + value值是(零碎工夫+过期工夫)
SET EX PX NX + 校验惟一随机值,再开释锁
多机实现的分布式锁Redlock
ZooKeeper实现分布式锁
应用数据库实现分布式锁
1)、计划一: SETNX和EXPIRE
伪代码如下:
if (setnx(key, 1) == 1){
expire(key, 10)try { //TODO 业务逻辑} finally { del(key)}
}
这种计划的长处是长处是实现简略,通过批改过期工夫能够反对锁重入,锁超时主动开释;
毛病:因为上述命令是分两步执行,如果第二步执行失败,将造成无奈解锁, 很容易导致死锁。
2)、计划二: SETNX + value值是(零碎工夫+过期工夫)
以后工夫: '2022-04-17 11:00:00'.to_time
value = '2022-04-17 11:00:10'.to_time // 10s后过期
if (setnx(key, value) == 1){ // 代码1
try { //TODO 业务逻辑} finally { del(key)}
}else{
if redisClient.get(key_resource_id) < Time.now { // 示意已过期
del(key) goto 代码1
}
}
长处:加锁是原子操作,解决了计划一的毛病,防止了死锁问题。
毛病:实现简单,每个机器的工夫必须放弃同步,其余加锁线程会批改过期工夫,锁有可能被其余线程谬误开释。
3)、SET EX PX NX + 校验惟一随机值,再开释锁
SETNX和EXPIRE 为两个指令, 不是原子性操作,如果 SETNX 胜利,在设置锁超时工夫后,服务器挂掉、重启或网络问题等,导致 EXPIRE 命令没有执行,锁没有设置超时工夫变成死锁。针对该问题,redis 在2.6.12版本过后减少新的解决方案。
set key value [expiration EX seconds|PX milliseconds] [NX|XX]
EX seconds:将键的过期工夫设置为 seconds 秒。
SET key value EX seconds 等同于 SETEX key seconds value
PX millisecounds:将键的过期工夫设置为 milliseconds 毫秒。
SET key value PX milliseconds 等同于 PSETEX key milliseconds value
NX:只在键不存在的时候,才对键进行设置操作。
SET key value NX 等同于 SETNX key value
XX:只在键曾经存在的时候,才对键进行设置操作
比方当key不存在时, 设置10s的锁, 能够这么设置:
SET product:10001 true ex 10 nx
如果SET操作胜利后,返回的是OK,失败返回NIL
最初为删除时,避免可能被其余线程误删。能够加锁时设置一下以后线程的一个随机ID, 而后在删除时判断一下。
伪代码如下:
if(redisClient.set(key_resource_id, uni_request_id, "NX", "EX", 100s) == 1){ //加锁
try { ... //业务解决}
finally {
//判断是不是以后线程加的锁,是才开释 if (redisClient.get(key_resource_id) == uni_request_id) { redisClient.del(lockKey); //开释锁 }
}
}
这种计划的长处是能够保障加锁的原子性,应用LUA开释锁的话,锁不会被其余线程谬误开释。
毛病:锁没有主动续期机制,锁无奈反对重入。不过其实平时开发中, 大部分场景不必思考主动续期机制。
4)、多机实现的分布式锁Redlock
Redis 官网站提出了一种权威的基于 Redis 实现分布式锁的形式名叫Redlock,此种形式比原先的单节点的办法更平安。
Redlock 的计划基于 2 个前提:
不再须要部署从库和哨兵实例,只部署主库
但主库要部署多个,官网举荐至多 5 个实例
整体的流程是这样的,一共分为 5 步:
a)、客户端先获取「以后工夫戳T1」
b)、客户端顺次向这 5 个 Redis 实例发动加锁申请(用后面讲到的 SET 命令),且每个申请会设置超时工夫(毫秒级,要远小于锁的无效工夫),如果某一个实例加锁失败(包含网络超时、锁被其它人持有等各种异常情况),就立刻向下一个 Redis 实例申请加锁
c)、如果客户端从 >=3 个(大多数)以上 Redis 实例加锁胜利,则再次获取「以后工夫戳T2」,如果 T2 - T1 < 锁的过期工夫,此时,认为客户端加锁胜利,否则认为加锁失败
d)、加锁胜利,去操作共享资源(例如批改 MySQL 某一行,或发动一个 API 申请)
e)、加锁失败,向「全副节点」发动开释锁申请(后面讲到的 Lua 脚本开释锁)
Redlock加锁步骤绝对还是比拟繁琐的,有点重,官网给出的解释是为了「容错」,局部实例异样宕机,残余的实例加锁胜利,整个锁服务仍旧可用。
其实Redlock一进去, 就受到了业界驰名的分布式系统专家Martin的质疑,他马上写了篇文章,质疑这个 Redlock 的算法模型是有问题的,并对分布式锁的设计,提出了本人的认识,之后,Redis 作者 Antirez 面对质疑,不甘示弱,也写了一篇文章,反驳了对方的观点,并具体分析了 Redlock 算法模型的更多设计细节。这里就不开展细讲了, 感兴趣的敌人能够看看这个文章。
5)、也能够应用ZooKeeper实现分布式锁
Zookeeper的节点Znode有四种类型:
长久节点:默认的节点类型。创立节点的客户端与zookeeper断开连接后,该节点仍旧存在。
长久节点程序节点:所谓程序节点,就是在创立节点时,Zookeeper依据创立的工夫程序给该节点名称进行编号,长久节点程序节点就是有程序的长久节点。
长期节点:和长久节点相同,当创立节点的客户端与zookeeper断开连接后,长期节点会被删除。
长期程序节点:有程序的长期节点。
Zookeeper分布式锁实现利用了长期程序节点, 这里大略讲下zk分布式锁的实现原理吧。
大抵思维为:每个客户端对某个办法加锁时,在ZooKeeper上与该办法对应的指定节点的目录下,生成一个惟一的长期有序节点。判断是否获取锁的形式很简略,只须要判断有序节点中序号最小的一个。当开释锁的时候,只需将这个长期节点删除即可。同时,其能够防止服务宕机导致的锁无奈开释,而产生的死锁问题。
图片
长处:
无效的解决单点问题,不可重入问题,非阻塞问题以及锁无奈开释的问题
实现较为简单
毛病:
性能上不如应用缓存实现的分布式锁,因为每次在创立锁和开释锁的过程中,都要动态创建、销毁长期节点来实现锁性能
须要对ZooKeeper的原理有所理解
这里就不开展细讲ZooKeeper的原理, 前面会专门用一篇文章讲如何用Zookeeper实现分布式锁。
6)、应用数据库实现分布式锁
能够应用select … for update 来实现分布式锁。咱们本人的我的项目,分布式定时工作,就应用相似的实现计划
长处:
简略,使用方便,不须要引入Redis、zookeeper等中间件。
毛病:
不适宜高并发的场景
db操作性能较差,有锁表的危险;
结语:
从性能角度(从高到低)Redis > Zookeeper >= 数据库;
从了解的难易水平角度(从低到高)数据库 > Redis > Zookeeper;
从实现的复杂性角度(从低到高)Zookeeper > Redis > 数据库;
从可靠性角度(从高到低)Zookeeper > Redis > 数据库。
平时开发中 SET EX PX NX + 校验惟一随机值,再开释锁 计划就能够了。
也欢送关注我的公众号: 散步coding。一起交换, 在coding的世界里散步, 回复: redis, 收费获取最新Redis面试题(含答案)。