分布式锁注意点及其实现
SegmentFault
共 2717字,需浏览 6分钟
·
2021-04-19 15:47
来源:SegmentFault思否社区
作者:skyarthur
分布式锁注意点
1)互斥性
在任意时刻只有一个客户端可以获取锁
2)防死锁
假如一个客户端在持有锁的时候崩溃了,没有释放锁,那么别的客户端无法获得锁,则会造成死锁,所以要保证客户端一定会释放锁。Redis中我们可以设置锁的过期时间来保证不会发生死锁。
3)持锁人解锁
解铃还须系铃人,加锁和解锁必须是同一个客户端,客户端A的线程加的锁必须是客户端A的线程来解锁,客户端不能解开别的客户端的锁。
4)可重入
当一个客户端获取对象锁之后,这个客户端可以再次获取这个对象上的锁。
redis分布式锁
实现
Redis 锁主要利用 Redis 的 setnx 命令。
加锁命令:SETNX key value,当键不存在时,对键进行设置操作并返回成功,否则返回失败。KEY 是锁的唯一标识,一般按业务来决定命名。 解锁命令:DEL key,通过删除键值对释放锁,以便其他线程可以通过 SETNX 命令来获取锁。 锁超时:EXPIRE key timeout, 设置 key 的超时时间,以保证即使锁没有被显式释放,锁也可以在一定时间后自动释放,避免资源被永远锁住。
if (setnx(key, 1) == 1){
expire(key, 30)
try {
//TODO 业务逻辑
} finally {
del(key)
}
}
注意点
setnx的时候,value设置和过期时间设置不是原子,可以用set命令,redis版本2.6.12以后 lua脚本,示例如下
if (redis.call('setnx', KEYS[1], ARGV[1]) < 1)
then return 0;
end;
redis.call('expire', KEYS[1], tonumber(ARGV[2]));
return 1;
// 使用实例
EVAL "if (redis.call('setnx',KEYS[1],ARGV[1]) < 1) then return 0; end; redis.call('expire',KEYS[1],tonumber(ARGV[2])); return 1;" 1 key value 100
错误解除
释放锁的时候,只能释放自己的,不能释放别人,所以需要getValue然后和自己的id比较,一致了才能delete,这里也需要是原子操作(其实觉得可以通过重试机制来避免)
超时解锁导致并发
将过期时间设置足够长,确保代码逻辑在锁释放之前能够执行完成。 为获取锁的线程增加守护线程,为将要过期但未释放的锁增加有效时间。
评论