怎样实现redis分布式锁

实现redis分布式锁的步骤:1、加锁;2、释放锁;3、给锁设置有效期;4、给锁设置少数值;5、通过LUA脚本实现释放锁的原子性。加锁是指,执行 setnx 为一个代表锁键设置值,如果能设置成功,则表示获得锁,失败则无法获得锁。

怎样实现redis分布式锁-Worktile社区

1、加锁

Redis 的 setnx 命令会判断键值是否存在,如果存在则不做任何操作,并返回0,如果不存在,则创建并赋值,并返回1,因此我们可以执行 setnx 为一个代表锁键设置值,如果能设置成功,则表示获得锁,失败则无法获得锁。

代码:

# 使用key为lock来表示一个锁
setnx lock 1 

2、释放锁

当执行好操作之后,要释放锁的时候直接把 Redis 里的键值 lock 删除就可以了,这样其他进程才能通过 setnx 命令重新设置并获得该锁。

代码:

# 释放锁
del lock

通过上面两个命令,我们实现了一个简单的分布式锁,但这里就出现了一个问题:如果一个进程通过 setnx 命令加锁之后,在执行具体操作出错了,没有办法及时释放锁,那么其他进程就无法获得该锁,系统便无法继续往下执行,解决这个问题的办法就是为锁设置一个有效期,在这个有效期之后,自动释放锁。

3、给锁设置有效期

给锁设置有效期非常简单,直接使用 Redis 的 expire 命令就可以了,如:

# 加锁
setnx lock 1 
# 给锁设置10s有效期
expire lock 10 

但是,现在又出现另一个问题了,如果我们在设置了锁之后,执行 expire 命令之前该进程挂掉了,那么 expire 就没有执行成功,锁一样是没有被释放掉的,所以一定要保证上面两个命令要一起执行,怎么保证呢?

有两个方法,一个是使用 LUA 语言编写的脚本,另一个是使用 Redis 的 set 命令, set 命令后面跟 nx 参数后,执行的效果与 setnx 一致,且 set 命令可以跟 ex 参数来设置过期时间,所以我们可以使用 set 命令把 setnx 和 expire 两个合并在一起,这样就可以保证执行的原子性了。

4、给锁设置少数值

如何区分其他进程的锁,避免删除其他进程的锁呢?答案就是每个进程在加锁的时候,给锁设置一个少数值,并在释放锁的时候,判断是不是自己设置的锁。给锁设置少数值的时候,一样是使用 set 命令,少数的不同是将键值1改为一个随机生成的少数值,比如uuid。

代码:

 # rand_uid表示少数id
set lock rand_id nx ex 10

当锁里的值由进程设置后,释放锁的时候,就需要判断锁是不是自己的,步骤如下:

  • 通过 Redis 的 get 命令获得锁的值
  • 根据获得的值,判断锁是不是自己设置的
  • 如果是,通过 del 命令释放锁。

此时我们看到,释放锁需要执行三个操作,如果三个操作依次执行的话,是没有办法保证原子性的。解决这个问题的办法就是保证上述三个操作执行的原子性,即在执行释放锁的三个操作中,其他进程不可以获得锁,想要做到这一点,需要使用到LUA脚本。

5、通过LUA脚本实现释放锁的原子性

Redis 支持 LUA 脚本, LUA 脚里的代码执行的时候,其他客户端的请求不会被执行,这样可以保证原子性操作,所以我们可以使用下面脚本进行锁的释放:

if redis.call("get",KEYS[1]) == ARGV[1] then 
  return redis.call("del",KEYS[1])
else 
  return 0
end

将上述脚本保存为脚本后,可以调用 Redis 客户端命令 redis-cli 来执行,如下:

# lock为key,rand_id表示key里保存的值
redis-cli --eval unlock.lua lock , rand_id 

延伸阅读

分布式锁的特征

  • 互斥性:任意时刻,只有一个客户端能持有锁。
  • 锁超时释放:持有锁超时,可以释放,防止不必要的资源浪费,也可以防止死锁。
  • 可重入性:一个线程如果获取了锁之后,可以再次对其请求加锁。
  • 高性能和高可用:加锁和解锁需要开销尽可能低,同时也要保证高可用,避免分布式锁失效。
  • 安全性:锁只能被持有的客户端删除,不能被其他客户端删除

文章标题:怎样实现redis分布式锁,发布者:Z, ZLW,转载请注明出处:https://worktile.com/kb/p/34703

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
Z, ZLW的头像Z, ZLW
上一篇 2023年1月8日 上午4:03
下一篇 2023年1月8日 上午4:39

相关推荐

发表回复

登录后才能评论
注册PingCode 在线客服
站长微信
站长微信
电话联系

400-800-1024

工作日9:30-21:00在线

分享本页
返回顶部