如何设计一把分布式锁

我们用 redis 来实现这把分布式的锁,redis 速度快、支持事务、可持久化的特点非常适合创建分布式锁。

分布式环境中如何消除网络延迟对锁获取的影响

锁,简单来说就是存于 redis 中一个唯一的 key。一般而言,redis 用 set 命令来完成一个 key 的设置(加锁),使用 get 命令获取 key 的信息(检查锁)。由于网络延迟的存在,简单的使用 setget 命令可能会带来如下问题:

线程 A 检查锁是否存在(get)–>否–>加锁(set),在 A 发起加锁命令但是还没有加锁成功的时候,可能线程 B 已经完成了 set 操作,锁被 B 获得,但是 A 也发起了加锁请求,由于 set 命令并不检查 key 的存在,B 的锁很可能会被 A 的 set 操作破坏。

幸运的是,redis 提供了另一个命令 setx : 当指定的 key 不存在时,设置 key 的值为指定 value,如果存在,不做任何操作,成功则返回 1,失败则返回 0。也就是只要命令返回成功,线程就能正确获得锁,不需要再做类似 get 检查操作。

使用 setx 可以消除网络延迟对锁设置的影响。

加锁的客户端发生 crash 导致锁不能被正确释放应该怎么处理?

加锁成功并操作完成时候,就需要加锁线程对锁进行释放,以让出资源的控制权。释放锁,简单来说就是删除 redis 中这个唯一的 key,但是一定要保证删除的这个 key 是该线程创建的,因而锁创建时必须携带执行线程的唯一特征以标示创建者的身份。

如果加锁的线程出现异常 crash 了而不能及时删除锁,则会导致锁一直无法被正确释放,资源处于一直被占有,别的线程处于一直等待的状态。为了避免这样的情况发生,锁一定要在异常发生之后 可以自己释放,以让出资源的控制权,可以使用 redis 的超时机制来达到这个目的。超时时间视不同的业务场景而定,一般是最大允许等待时间。需要注意的是,只有在加锁成功之后才可以对 key 设置 TTL,否则很容易导致 key 被多个线程不断设置 TTL 而无法过期。

if CONN.setnx(lockname, identifier):
CONN.expire(lockname, timeout)

加锁之后如何有效监测锁是否被篡改?

redis 提供了 pipeline 和事务操作来保证多个命令可以在一个事务内全部完成从而减少多次网络请求带来的开销,watch 命令又可以在事务开始执行之前对所要操作的 key 执行监测,从而保证了事务的完整性和一致性。因此,为了防止锁篡改,可以在加锁完成之后对锁进行 watch 操作,一旦锁发生变化,则终止事务,回滚操作。

pipe = CONN.pipeline(True)
pipe.watch(lock)

提供锁的宿主机( redis 服务器) crash 导致锁不能被正确建立和释放该如何处理?**

不论是通信故障或是服务器故障而导致的锁服务器无法响应,此时都会导致客户端加锁和释放锁的请求无法完成,因此一定要有相应的应急处理,以确保程序流程的完整体验,加强客户端的健壮性。比如相应的超时提示,异常告警等。

哪些边界需要注意

1.只有锁正确释放才算是整个事务的完整结束,如果锁释放失败,比如被篡改、锁服务器异常等,不同的业务可以根据自己的需求进行变动和调整。

2.设置 TTL 一定要是加锁成功之后,否则所有获取锁的客户端都会尝试 TTL 导致锁无法过期。

3.锁的过期时间也就是获取锁的客户端的最大等待时间,这个时间根据执行的事务能够容忍的最长时间为限

一个简单的 python 实现

import time
import redis
import logging logger = logging.getLogger('service.redis_lock') CONN = redis.Redis(host='localhost') def acquire_lock(lockname, identifier, wait_time=20, timeout=15):
end = time.time() + wait_time
while end > time.time():
if CONN.setnx(lockname, identifier):
CONN.expire(lockname, timeout) # set expire time
return identifier time.sleep(0.001) #wait until the lock expired or release by some thread return False def release_lock(lockname, identifier):
pipe = CONN.pipeline(True)
try:
#watch lock once lock has been changed, break this transaction
pipe.watch(lockname)
#check if lock has been changed
if pipe.get(lockname) == identifier:
pipe.multi()
pipe.delete(lockname)
pipe.execute()
return True pipe.unwatch() #execu when identifier not equal
except redis.exceptions.WatchError as e:
logger.error(e)
return False
except Exception as e:
logger.error(e)
return False return False if __name__ == '__main__':
print release_lock('h', 'a') 转自:https://gold.xitu.io/entry/57bae53f5bbb500063fedf31

redis实现分布式锁——核心 setx+pipe watch监控key变化-事务的更多相关文章

  1. 用Redis实现分布式锁 与 实现任务队列(转)

    这一次总结和分享用Redis实现分布式锁 与 实现任务队列 这两大强大的功能.先扯点个人观点,之前我看了一篇博文说博客园的文章大部分都是分享代码,博文里强调说分享思路比分享代码更重要(貌似大概是这个意 ...

  2. Redis实现分布式锁与任务队列

    Redis实现分布式锁 与 实现任务队列 这一次总结和分享用Redis实现分布式锁 与 实现任务队列 这两大强大的功能.先扯点个人观点,之前我看了一篇博文说博客园的文章大部分都是分享代码,博文里强调说 ...

  3. 使用Redis作为分布式锁的一些注意点

    Redis实现分布式锁 最近看分布式锁的过程中看到一篇不错的文章,特地的加工一番自己的理解: Redis分布式锁实现的三个核心要素: 1.加锁 最简单的方法是使用setnx命令.key是锁的唯一标识, ...

  4. 用Redis实现分布式锁 与 实现任务队列

    这一次总结和分享用Redis实现分布式锁 与 实现任务队列 这两大强大的功能.先扯点个人观点,之前我看了一篇博文说博客园的文章大部分都是分享代码,博文里强调说分享思路比分享代码更重要(貌似大概是这个意 ...

  5. 用Redis实现分布式锁 与 实现任务队列【转载】

    这一次总结和分享用Redis实现分布式锁 与 实现任务队列 这两大强大的功能.先扯点个人观点,之前我看了一篇博文说博客园的文章大部分都是分享代码,博文里强调说分享思路比分享代码更重要(貌似大概是这个意 ...

  6. 从零到一手写基于Redis的分布式锁框架

    1.分布式锁缘由 学习编程初期,我们做的诸如教务系统.成绩管理系统大多是单机架构,单机架构在处理并发的问题上一般是依赖于JDK内置的并发编程类库,如synchronize关键字.Lock类等.随着业务 ...

  7. Spring Boot Redis 实现分布式锁,真香!!

    之前看很多人手写分布式锁,其实 Spring Boot 现在已经做的足够好了,开箱即用,支持主流的 Redis.Zookeeper 中间件,另外还支持 JDBC. 本篇栈长以 Redis 为例(这也是 ...

  8. 聊聊如何用 Redis 实现分布式锁?

    作者:小林coding 计算机八股文网站:https://xiaolincoding.com 哈喽,我是小林. 今天跟大家聊聊两个问题: 如何用 Redis 实现分布式锁? Redis 是如何解决集群 ...

  9. 基于redis 实现分布式锁的方案

    在电商项目中,经常有秒杀这样的活动促销,在并发访问下,很容易出现上述问题.如果在库存操作上,加锁就可以避免库存卖超的问题.分布式锁使分布式系统之间同步访问共享资源的一种方式 基于redis实现分布式锁 ...

随机推荐

  1. 0x…… is not a valid instance ID怎么解决

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  2. EXCel鼠标右键不能用解决办法

    EXCel鼠标右键不能用解决办法 倒腾vba首要是保证安全,各路大神的代码非常神奇,莫名的就让你的excel嘎嘣了,如出现右键无法使用(确定不是您的鼠标问题),那么以下代码可完全修复设置.操作步骤:打 ...

  3. Android---63---Android中的动画效果

    Android中有四种动画效果: AlphaAnimation:透明度动画效果 ScaleAnimation:缩放动画效果 TranslateAnimation:位移动画效果 RotateAnimat ...

  4. 京东2015年应届生招聘笔试题(A)卷答案选择题部分

    1.操作系统死锁的必要条件(多选题):()   A.相互排斥条件   B.系统资源有限   C.进程调度不合理   D.环路等待条件 答:A,C,D 參考资料:http://blog.sina.com ...

  5. IDEA小技巧-随时更新

    © 版权声明:本文为博主原创文章,转载请注明出处 1.设置删除一行快捷键 File->Settings->keymap->Delete Line 2.设置代码提示快捷键 File-& ...

  6. jquery单选框radio绑定click事件实现方法

    本文实例讲述了jquery单选框radio绑定click事件实现方法.分享给大家供大家参考. 具体实现方法如下: 复制代码代码如下: <html><head><title ...

  7. JAVA进阶-多线程(2)

    堵塞队列: 1)BlockingQueue该接口提供了: add()/remove() 假设当队列没有数据,从队列中取数据;或者队列中数据已满, 向队列中加入数据;则会抛出异常. put()/take ...

  8. java和erlang之间的DES加解密

    app登录,登录的密码要用DES加密,服务器是用erlang,客户端要同时支持多平台(Android.iOS).首先,Java端的DES加密的实现方式, 少说废话了,直接上代码,如下: public ...

  9. html 锚点定位

    在html中设置锚点定位我知道的有几种方法.在此和大家分享一下: 1.使用id定位: <a href="#1F" name="1F">锚点1< ...

  10. NDK以及C语言基础语法(二)

    一.字符串类:(属于类类型) -String (在C++中才有) 使用之前必学引入String 类型: 引入String头文件(系统的头文件): #include <string>   p ...