基于Redis实现简单的分布式锁

分布式锁要点

  1. 不一样的节点访问到的应该是同一把锁。
  2. 锁的基本特新不变,须要有加锁和释放两大操做。
  3. 一个最重要的,不像JVM的锁,在单机上,可使用try-catch-finally来进行锁的释放,可是分布式锁,加锁和释放锁都是经过网络的,因此要考虑一个节点获取锁以后万一崩掉了没有释放锁的场景。
  4. 其余还有不少细节,我这里只说明一个简单的分布式锁须要注意的地方,能知足80%的需求。

加锁实现说明

基于Redis的单线程特性和Lua脚本,咱们能够简单的实现一把分布式锁。通常锁在Redis上就是一个KV值,加锁就是新建一个KV,释放锁就把这个KV删除。利用Redis在设置KV时能够设置NX和PX特性,咱们就能够很方便的加锁。废话很少说,先上伪代码:git

String kId = UuidUtil.uuid();
String result = jedis.set("lockName", kId, "NX", "PX", 30 * 1000);

在获取锁时,先在本地生成uuid,做为锁的钥匙。而后用这个uuid做为值去redis设置KV,同时指定NX模式,NX模式的意思就是若是不存在才新增这个KV,若是对应的K已经存在,则放弃新增。这里返回的result=OK表示新增成功即加锁成功,否者加锁失败。后面两个参数PX表明,这个KV有一个过时时间,若是时间过时了,KV会被自动的删除。这个就是解决了上面说到的,若是一个节点没有即时返回锁,那么防止死锁的出现,这里有个锁持有的超时时间。redis

释放实现说明

释放锁很简单,就是直接删除这个KV。可是咱们要作到的是,谁获取锁谁才有资格释放,因此在释放时,不能简单的根据K来删除,还须要匹配V的值,这样才能保证谁拿的锁,谁有资格释放。如此一来,释放锁就须要两个步骤。第一步:先获取当前锁对应的值,和本身手上的钥匙是否匹配。第二步,在匹配符合时再操做删除。因此这里就要用Lua脚原本保证这两个步骤的原子性。伪代码以下:网络

String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
redis.eval(script, Collections.singletonList("lockName"), Collections.singletonList(kId));

简单说明一下,这里在本地编写一段Lua脚本,大体意思你应该也能够看出来了,先get取到锁对应的值,判断持有的kId是否相等,若是相等才进行del操做,进行锁的删除释放。redis能够原子的让这个脚本执行。分布式

所有代码

刚才说的所有代码实现,在这个开源项目里面有,里面还有一些别的分布式组件,若是对你有帮助,请给我个star哈。 https://gitee.com/xuan698400/distributed-toolui

相关文章
相关标签/搜索