广州建设交易中心网站首页,网站缩写的英文,网站怎么做才不会被墙,本地视频怎么生成链接Redis知识点总结#xff08;五#xff09;——Redis实现分布式锁 setnxsetnx expiresetnx expire lua脚本set nx exset nx ex 随机值set nx ex 随机值 lua脚本set ex nx 随机值 lua脚本 锁续期RedissonRedLock 在Redis的众多应用场景中#xff0c;分布式锁是Redis比… Redis知识点总结五——Redis实现分布式锁 setnxsetnx expiresetnx expire lua脚本set nx exset nx ex 随机值set nx ex 随机值 lua脚本set ex nx 随机值 lua脚本 锁续期RedissonRedLock 在Redis的众多应用场景中分布式锁是Redis比较重要的一个应用场景今天我们就来了解一下如何用Redis实现一个分布式锁。
setnx
Redis提供了一个setnx命令它的作用是在给缓存设置值时判断key是否存在如果key已经存在则不执行操作只有当缓存中不存在指定key时才设置该缓存。
使用setnx命令就可以实现一个简单的分布式锁。我们指定一个特定的值作为setnx命令的key然后当一个线程要获取分布式锁时就要通过setnx命令在redis中设置该key如果设置成功代表获取分布式锁成功如果设置失败表示获取分布式锁失败那么当前线程就要通过自旋加sleep的机制等待别的线程释放锁。
一个线程通过setnx命令获取到分布式锁后通过del命令释放锁。释放锁之后其他线程就可以再次尝试获取锁。 但是这种方案有一个严重的问题就是当一个线程获取到锁后在处理业务逻辑时发生了异常或者直接宕机了没能执行del命令释放锁那么其他线程就会一直获取不到锁。
setnx expire
单靠setnx命令实现的分布式锁存在锁得不到释放的风险。因此我们可以引入expire命令给key设置一个过期时间。当一个线程执行setnx命令成功后就通过expire命令给该key设置一个过期时间。假如该线程执行业务逻辑出错没能执行del命令那么等该key的过期时间到后该key也会被失效清理锁最终还是能得到释放的。 这样问题就解决了吗
显然是没有的因为setnx和expire是两条命令是非原子性的如果setnx执行成功expire命令执行失败那么锁还是得不到释放。
setnx expire lua脚本
setnx命令与expire命令的非原子性问题可以通过lua脚本去解决把setnx和expire两条命令包在一个lua脚本里面就可以保证这两条命令同时成功或者同时失败这就就具备了原子性。 除了使用lua脚本Redis还可以通过一条命令实现setnx加expire两条命令的功能。
set nx ex
我们可以通过以下命令实现setnx命令加expire命令的功能并且可以保证原子性
set key value nx ex {过期时间}这样分布式锁得不到释放的问题就彻底解决了。
但是这样就没问题了吗显然不是的还会出现以下这种情况 线程1获取分布式锁成功但是业务处理时间过长锁早已过期。而线程2随后获取分布式锁成功进行业务处理。然后这时候线程1处理完了于是执行del命令释放锁而此时它释放的是线程2加的锁并且线程2还没有处理完。线程2的锁被线程1释放后如果线程3到来也获取了分布式锁那么等线程2处理完毕又会释放掉线程3获取的锁以此类推这个分布式锁就失效了。
set nx ex 随机值
锁错误释放的问题可以通过给value指定一个随机值来解决实现方案是这样 执行set命令时生成一个随机值作为value值并且当前线程要保存该随机值。等到当前线程处理完业务逻辑要准备释放锁时先拿着自己保存的随机值跟redis中的value比对一下如果相等才执行del命令释放锁否则就不能执行del命令。
这样看似可以解决锁误删的问题一般情况下也确实如此但是在高并发场景下有可能会出现以下这种情况 线程1执行完业务逻辑后判断自己的value随机值与redis中的value相等正准备释放锁此时正好锁过期了然后线程2又刚好获取到了锁然后线程1才执行del命令那么线程2的锁又被误释放了。
这是低概率事件非高并发场景其实不用考虑这个问题但是在高并发场景还是有可能出现的。问题的原因是if判断与del命令是两个操作非原子性。
set nx ex 随机值 lua脚本
if判断与del命令的非原子性问题可以通过lua脚本解决把if判断与del命令用lua脚本包起来就可以彻底解决锁误释放的问题。 现在获取锁与释放锁的操作都是原子性的我们的分布式锁也逐步变得完善但是还有一个问题那就是锁过期也就是业务处理还未结束锁就过期的这种情况。
我们可以给锁设置一个较长的过期时间但是我们无法百分之一百保证所有的业务处理都在锁过期前得到释放如果在某种特殊情况下处理时间比平常的要长锁还是过期了那么还是有问题的。
set ex nx 随机值 lua脚本 锁续期
锁过期的解决办法就是开一个“看门狗”线程进行锁续期这个线程会定时地不断的检测主线程释放处理完毕并且释放了锁如果主线程还没有处理完毕那么这个“看门狗”线程就会执行expire命令进行锁续期。 此时我们的分布式锁就完美了。但是这样太复杂了吧我们为了实现一个分布式锁居然要处理这么多问题使得原本简单的代码变得非常复杂那有没有一个开源框架可以帮我们实现这一切我们只需要简单的调用一下就完事了呢
当然是有的那就是使用Redisson的分布式锁。
Redisson
Redisson是一个用Java语言实现的用于操作Redis的客户端工具包比起原生的Jedis工具包Redisson是更好用功能更强大的。
Redisson就自带了分布式锁的实现把我们上面说到的获取锁的原子性、释放锁的原子性、利用看门狗线程进行锁续期等一系列的操作都包装起来对外暴露了简单的获取锁和释放锁的方法我们无需手动编码实现这复杂的获取锁与释放锁的逻辑只要使用Redisson提供的分布式锁的方法就可以实现分布式锁的功能。 使用Redisson我们就可以轻松简单的实现分布式锁的功能但是如果Redis是单节点的话Redis宕机了那么所有线程都无法获取到锁了。 解决Redis单节点的问题自然是上多个Redis节点。但是上了多个Redis节点之后分布式锁又怎么实现呢
RedLock
Redis的作者提出了一个多redis节点的分布式锁实现方案那就是RedLock。
首先我们要启5个redis节点但是这5个节点是独立的节点互相之间没有主从关系也没有组成集群。
当一个线程加锁时需要按顺序的访问这五个节点还是利用set nx ex或set nx px命令进行加锁。
当一个线程在至少3个以上的redis节点成功执行了set命令那么就表示获取锁成功此时该线程要计算锁的有效期需要用锁的有效期减去加锁耗费的时间如果还有剩余时间表示获取锁成功如果没有剩余时间了那么表示获取锁失败。
如果一个线程获取锁失败要按相反的顺序向所有redis节点执行del命令释放锁。 这个RedLock的功能同样可以通过Redisson实现只需要调用Redisson提供的简单的API方法即可无需我们编写复杂的RedLock代码。