概述
setNX设置锁时 设置值和过期时间是个原子的操作。
对于setNX的并发性问题在底层实现上有这些措施。
1.对锁加一个过期时间,防止客户端A设置了锁,然后客户端挂了,锁一直得不到释放这种情况。
2.释放锁的操作必须使用Lua脚本来实现。释放锁其实包含三步操作:'GET'、判断和'DEL',用Lua脚本来实现能保证这三步的原子性。否则,如果把这三步操作放到客户端逻辑中去执行的话,就有可能发生这种问题:
-
客户端1获取锁成功。
-
客户端1访问共享资源。
-
客户端1为了释放锁,先执行'GET'操作获取随机字符串的值。
-
客户端1判断随机字符串的值,与预期的值相等。
-
客户端1由于某个原因阻塞住了很长时间。
-
过期时间到了,锁自动释放了。
-
客户端2获取到了对应同一个资源的锁。
-
客户端1从阻塞中恢复过来,执行
DEL
操纵,释放掉了客户端2持有的锁。
3.设置锁的时候每个客户端会带上一个随机值。
SET resource_name my_random_value NX PX 30000
-
my_random_value
是由客户端生成的一个随机字符串,它要保证在足够长的一段时间内在所有客户端的所有获取锁的请求中都是唯一的。 -
NX
表示只有当resource_name
对应的key值不存在的时候才能SET
成功。这保证了只有第一个请求的客户端才能获得锁,而其它客户端在锁被释放之前都无法获得锁。 -
PX 30000
表示这个锁有一个30秒的自动过期时间。当然,这里30秒只是一个例子,客户端可以选择合适的过期时间。
可以防止客户端A释放了客户端B的锁这种情况。
-
客户端1获取锁成功。
-
客户端1在某个操作上阻塞了很长时间。
-
过期时间到了,锁自动释放了。
-
客户端2获取到了对应同一个资源的锁。
-
客户端1从阻塞中恢复过来,释放掉了客户端2持有的锁。
4.在设置锁时使用getset命令,这个命令会返回旧的时间,将旧的时间和现在比较就能判断出当前的锁是否超时,可以防止两个客户端同时获得锁。
C0操作超时了,但它还持有着锁,C1和C2读取锁检查时间戳,先后发现超时了。
C1 发送DEL lock.foo
C1 发送SETNX lock.foo 并且成功了。
C2 发送DEL lock.foo
C2 发送SETNX lock.foo 并且成功了。
用getset后,c1、c2在读取时间戳这一步,都会拿当前时间比较一下是否是真的过时了。
另外,锁过期了并不一定是立即释放的。
Redis中有三种处理策略:定时删除、惰性删除和定期删除。
定时删除:在设置键的过期时间的时候创建一个定时器,当过期时间到的时候立马执行删除操作。不过这种处理方式是即时的,不管这个时间内有多少过期键,不管服务器现在的运行状况,都会立马执行,所以对CPU不是很友好。
惰性删除:惰性删除策略不会在键过期的时候立马删除,而是当外部指令获取这个键的时候才会主动删除。处理过程为:接收get执行、判断是否过期(这里按过期判断)、执行删除操作、返回nil(空)。
定期删除:定期删除是设置一个时间间隔,每个时间段都会检测是否有过期键,如果有执行删除操作。
最后
以上就是孤独信封为你收集整理的Redis setNX锁实现的全部内容,希望文章能够帮你解决Redis setNX锁实现所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复