如何设计一把分布式锁

我们用 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. Linux装mysqli.so

    php 5.2.3+mysqli 安装与常见错误 总结 php 5.2.3+mysqli 安装与常见错误 总结   记得原来在编译php的已经已经加上参数--with-mysql=/usr/local ...

  2. eclipse svn插件 删除原账号信息重新登录

    1.通过删除SVN客户端的账号配置文件    1)查看你的Eclipse中使用的是什么SVN Interface(中文:svn接口)windows > preference > Team ...

  3. inflate, findViewById与setContentView的差别与联系

    protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentV ...

  4. 吐血整理:PyTorch项目代码与资源列表 | 资源下载

    http://www.sohu.com/a/164171974_741733   本文收集了大量基于 PyTorch 实现的代码链接,其中有适用于深度学习新手的“入门指导系列”,也有适用于老司机的论文 ...

  5. Web大文件(夹)上传(断点续传)控件-Xproer.HttpUploader6

    版权所有 2009-2017荆门泽优软件有限公司 保留所有权利 官方网站:http://www.ncmem.com/ 产品首页:http://www.ncmem.com/webapp/up6.2/in ...

  6. OpenSUSE 13.1上安装StrongSwan

    结果: 1)iOS 7.1设备能够拨IPSec VPN到StrongSwan电脑上面来 - Connect to VPN 2)iOS 设备浏览器能够訪问StrongSwan VPN所在的内网地址服务器 ...

  7. ubuntu系统安装好后一些基本软件的安装

    preface: 由于某些原因重装了系统,原来在ubuntu下安装的一些软件又要又一次安装.稍微麻烦,整理下须要安装的步骤. #==================================== ...

  8. Angualr 实现复选框全选功能

    html <html lang="en"> <head> <meta charset="UTF-8"> <title& ...

  9. Memcache安装与使用

    一.资源下载 安装memcached 之前必需要先安装 libevent 分别在libevent和memcached的官网下载安装包libevent-1.4.14b-stable.tar.gz和mem ...

  10. 链接数据库代码封装DBDA

    <?php class DBDA { public $host = "localhost"; //服务器地址 public $uid = "root"; ...