原创文章首发微信公众号「后端技术学堂」转载请先与我联系,点文末链接「了解更多」 今天就来说说高并发编程中redis分布式锁实现,这里罗列出3种redis实现的分布式锁,并分别对比说明各自特点。 setnx用法参考redis官方文档 SETNX key value 将key设置值为value,如果key不存在,这种情况下等同SET命令。当key存在时,什么也不做。SETNX是”SET if Not eXists”的简写。 返回值: 解锁相对简单,只需GET lock.foo时间戳,判断是否过期,过期就调用删除DEL lock.foo set用法参考官方文档 SET key value [EX seconds|PX milliseconds] [NX|XX] 将键key设定为指定的“字符串”值。如果 key 已经保存了一个值,那么这个操作会直接覆盖原来的值,并且忽略原始类型。当set命令执行成功之后,之前设置的过期时间都将失效。 从2.6.12版本开始,redis为SET命令增加了一系列选项: 版本>= 6.0 一条命令即可加锁: SET resource_name my_random_value NX PX 30000 The command will set the key only if it does not already exist (NX option), with an expire of 30000 milliseconds (PX option). The key is set to a value “myrandomvalue”. This value must be unique across all clients and all lock requests. 这个命令只有当key 对应的键不存在resource_name时(NX选项的作用)才生效,同时设置30000毫秒的超时,成功设置其值为my_random_value,这是个在所有redis客户端加锁请求中全局唯一的随机值。 解锁时需要确保my_random_value和加锁的时候一致。下面的Lua脚本可以完成 这段Lua脚本在执行的时候要把前面的my_random_value作为ARGV[1]的值传进去,把resource_name作为KEYS[1]的值传进去。释放锁其实包含三步操作:’GET’、判断和’DEL’,用Lua脚本来实现能保证这三步的原子性。 前面两种分布式锁的实现都是针对单redis master实例,既不是有互为备份的slave节点也不是多master集群,如果是redis集群,每个redis master节点都是独立存储,这种场景用前面两种加锁策略有锁的安全性问题。 比如下面这种场景: 客户端1从Master获取了锁。 Master宕机了,存储锁的key还没有来得及同步到Slave上。 Slave升级为Master。 客户端2从新的Master获取到了对应同一个资源的锁。 于是,客户端1和客户端2同时持有了同一个资源的锁。锁的安全性被打破。 针对这种多redis服务实例的场景,redis作者antirez设计了Redlock (Distributed locks with Redis)算法,就是我们接下来介绍的。 集群加锁的总体思想是尝试锁住所有节点,当有一半以上节点被锁住就代表加锁成功。集群部署你的数据可能保存在任何一个redis服务节点上,一旦加锁必须确保集群内任意节点被锁住,否则也就失去了加锁的意义。 具体的: 我们接着讲 客户端向所有Redis节点发起释放锁的操作,不管这些节点当时在获取锁的时候成功与否。 上面描述的算法已经有现成的实现,各种语言版本。 源码在这 https://github.com/jacket-code/redlock-cpp my_resource_name是加锁标识;1000是锁的有效期,单位毫秒。 综上所述,三种实现方式。 https://redis.io/topics/distlock https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html http://zhangtielei.com/posts/blog-redlock-reasoning.html 我会持续分享软件编程和程序员那些事,欢迎关注。若你对编程感兴趣,我整理了这些年学习编程的各种资源,关注公众号「后端技术学堂」发送「资源」分享给你,点下方「了解更多」链接。Redis单实例分布式锁
实现一:SETNX实现的分布式锁
语法
加锁步骤
解锁步骤
实现二:SET实现的分布式锁
语法
加锁步骤
解锁步骤
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
Redis集群分布式锁
实现三:Redlock
加锁步骤
解锁步骤
算法实现
比如我用的C++实现
创建分布式锁管理类CRedLock
CRedLock * dlm = new CRedLock();
dlm->AddServerUrl("127.0.0.1", 5005);
dlm->AddServerUrl("127.0.0.1", 5006);
dlm->AddServerUrl("127.0.0.1", 5007);
加锁并设置超时时间
CLock my_lock;
bool flag = dlm->Lock("my_resource_name", 1000, my_lock);
加锁并保持直到释放
CLock my_lock;
bool flag = dlm->ContinueLock("my_resource_name", 1000, my_lock);
加锁失败返回false, 加锁成功返回Lock结构如下
class CLock {
public:
int m_validityTime; => 9897.3020019531 // 当前锁可以存活的时间, 毫秒
sds m_resource; => my_resource_name // 要锁住的资源名称
sds m_val; => 53771bfa1e775 // 锁住资源的进程随机名字
};
解锁
dlm->Unlock(my_lock);
总结
参考
创作不易,点赞关注支持一下吧