Redis优雅实现分布式锁本平台不定期更新,喜欢我的文章,请关注我的微信公众号。在实际项目开发中,经常会遇到这样的业务场景:如果同一台机器上的多个线程抢夺同一个共享资源,同一个线程执行多次就会出现异常,这种情况下就会出现非线程安全。我们的解决方案通常是使用锁。但是如果有多台机器呢?这时候,我们通常会使用分布式锁来解决分布式环境下共享资源的同步问题。Redis、zookeeper等常用来实现分布式锁。今天主要讲一下如何使用Redis实现分布式锁。使用Redis实现分布式锁的方案其实很简单。首先,我们先实现一个方案1:每执行一次请求,机器先查询Redis中是否有分布式锁的key。如果锁没有key,就用key锁就是key,value取一个随机数写入Redis,然后开始执行请求。方案一看似很简单,但是这个处理逻辑难免有两个致命的bug:第一:如果一个进程成功获取到锁,但是此时机器出现故障宕机,分布式锁没有获取到Release,导致僵局。第二:如果有两台机器同时查询Redis,都发现Redis中没有lockkey,所以都成功获取到了锁。这时我们可以处理改进方案1,实现方案2:Redis提供了一个原子写操作命令:setnx,setnx只允许在锁的key不存在的情况下设置key值,所以问题2是可以解决同一个锁可能被不同机器同时获取的问题,setnx命令可以设置key值的超时时间,所以在写锁的key的时候,可以设置一个超时时间锁。如果超时时间超过了超时时间,锁仍然会释放,如果没有释放,其他机器可以继续写key,在key释放后占用锁来执行相应的请求。这样,问题1,由于机器宕机无法及时释放锁的问题也迎刃而解了。但这又产生了另一个潜在的问题:如果某台机器执行了一个耗时操作,超时后请求还没有执行完,锁就会被释放并被新的机器占用,到时间再执行释放-消耗任务结束。锁操作,此时释放的锁不是自己的锁而是已经被其他机器占用的锁。这时候我们可以对计划进行适当的修改,成为计划三:当某台机器占用锁并在Redis中设置一个key时,将value设置为一个随机数,在request之后增加释放锁之前的步骤isprocessed:判断key的值是否等于之前设置的随机数。如果这意味着锁的拥有者还是你自己,那么你可以执行解锁操作。否则说明锁已经被别人占用了,你不能进行释放锁的操作。这可以解决可能的错误。该操作释放其他人的锁。但是由于查询和释放锁的操作是非原子的,可能会出现这样一种情况:在查询key的时候,发现key的值和机器本身是一致的,但是在紧急释放锁之前,锁到期并被释放。这个时候执行释放锁操作会导致其他机器持有的锁被释放,所以我们的方案3还需要进一步改进,就是要保证查询锁和释放锁这两个步骤必须是原子的.有时我们需要用另一种方法:引入Jedis,使用Lua脚本将查询锁和释放锁的两部分逻辑写成一个脚本,这样当Redis执行Lua脚本时,其他机器的所有命令都必须等到执行完毕Lua脚本的部分在执行之前已经完成,所以查询锁不可能在释放之前被其他机器占用。至此我们介绍了如何使用Redis实现分布式锁,但是这是基于单机部署的。如果Redis部署在多台机器上,每个主节点都有子节点。由于Redis主从复制是一个异步操作,所以上面的方案肯定会有问题。多机部署可以使用Redission实现分布式锁。这是一个官方组件。有兴趣的可以自己看文档:https://github.com/redisson/r...欢迎关注公众号:程序员周先森
