Redis分布式锁服务(转)
原文:http://www.cnblogs.com/mushroom/p/4752499.html
概述
在多线程环境下,通常会使用锁来保证有且只有一个线程来操作共享资源。比如:
- object obj = new object();
- lock (obj)
- {
- //操作共享资源
- }
利用操作系统提供的锁机制,可以确保多线程或多进程下的并发唯一操作。但如果在多机环境下就不能满足了,当A,B两台机器同时操作C机器的共享资源时,就需要第三方的锁机制来保证在分布式环境下的资源协调,也称分布式锁。
Redis有三个最基本属性来保证分布式锁的有效实现:
- 安全性: 互斥,在任何时候,只有一个客户端能持有锁。
- 活跃性A:没有死锁,即使客户端在持有锁的时候崩溃,最后也会有其他客户端能获得锁,超时机制。
- 活跃性B:故障容忍,只有大多数Redis节点时存活的,客户端仍可以获得锁和释放锁。
分布式锁
由于Redis是单线程模型,命令操作原子性,所以利用这个特性可以很容易的实现分布式锁。 获得一个锁
- SET key uuid NX PX timeout
- SET resource_name uniqueVal NX PX 30000
命令中的NX表示如果key不存在就添加,存在则直接返回。PX表示以毫秒为单位设置key的过期时间,这里是30000ms。 设置过期时间是防止获得锁的客户端突然崩溃掉或其他异常情况,导致redis中的对象锁一直无法释放,造成死锁。
Key的值需要在所有请求锁服务的客户端中,确保是个唯一值。 这是为了保证拿到锁的客户端能安全释放锁,防止这个锁对象被其他客户端删除。
举个例子:
- A客户端拿到对象锁,但在因为一些原因被阻塞导致无法及时释放锁。
- 因为过期时间已到,Redis中的锁对象被删除。
- B客户端请求获取锁成功。
- A客户端此时阻塞操作完成,删除key释放锁。
- C客户端请求获取锁成功。
- 这时B、C都拿到了锁,因此分布式锁失效。
要避免例子中的情况发生,就要保证key的值是唯一的,只有拿到锁的客户端才能进行删除。 基于这个原因,普通的del命令是不能满足要求的,我们需要一个能判断客户端传过来的value和锁对象的value是否一样的命令。遗憾的是Redis并没有这样的命令,但可以通过Lua脚本来完成:
- if redis.call("get",KEYS[1]) == ARGV[1] then
- return redis.call("del",KEYS[1])
- else
return 0- end
逻辑很简单,获取key中的值和参数中的值相比较,相等删除,不相等返回0。
多实例分布式锁
上面是在单个Redis实例实现分布式锁的,这存在一个问题就是,如果这台实例因某些原因崩溃掉,那么所有客户端的锁服务全部失效。
Redis本身支持Master-Slave结构,可以一主多从,采用高可用方法,可以保证在master挂的时候自动切换到slave。 但是由于主从之间是异步同步数据的,所以redis并不能完全的实现锁的安全性。 举个例子来说:
- A客户端在master实例上获得一个锁。
- 在对象锁key传送到slave之前,master崩溃掉。
- 一个slave被选举成master。
- B客户端可以获取到同个key的锁,但A也已经拿到锁,导致锁失效。
在多台master情况下实现这个算法,并保证锁的安全性。 步骤如下:
- 客户端以毫秒为单位获取当前时间。
- 使用同样key和值,循环在多个实例中获得锁。 为了获得锁,客户端应该设置个偏移时间,它小于锁自动释放时间(即key的过期时间)。 举个例子来说,如果一个锁自动释放时间是10秒,那偏移时间应该设置在5~50毫秒的范围。 防止因为某个实例崩溃掉或其他原因,导致client在获取锁时耗时过长。
- 计算获取所有锁的耗时,即当前时间减去开始时间,得到a值。 用锁自动释放时间减去a值,在减去偏移时间,得到c值,如果获取锁成功的实例数量大于实际的数量一半,并且c大于0,那么锁就被获取成功。
- 锁获取成功,锁对象的有效时间是上面的c值。
- 若是客户端因为一些原因获取失败,原因可能是上面的c值为负数或者锁成功的数量小于实例数,以用N/2+1当标准(N为实例数)。 那么会释放所有实例上的锁。
上面描述可能不方便理解,用代码表示如下:

- //锁自动释放时间
- TimeSpan ttl=new TimeSpan(0,0,0,30000)
- //获取锁成功的数量
- int n = 0;
- //记录开始时间
- var startTime = DateTime.Now;
- //在每个实例上获取锁
- for_each_redis(
- redis =>
- {
- if (LockInstance(redis, resource, val, ttl)) n += 1;
- }
- );
- //偏移时间是锁自动释放时间的1%,根据上面10s是5-50毫秒推出。
- var drift = Convert.ToInt32(ttl.TotalMilliseconds * 0.01);
- //锁对象的有效时间=锁自动释放时间-(当前时间-开始时间)-偏移时间
- var validity_time = ttl - (DateTime.Now - startTime) - new TimeSpan(0, 0, 0, 0, drift);
- //判断成功的数量和有效时间c值是否大于0 if (n >= (N/2+1) && validity_time.TotalMilliseconds > 0) { }

总结
用Redis做分布式锁相比其他分布式锁(zookeeper)实现更简单,速度更快。
在ServiceStack.Redis客户端组件上是直接支持锁实现的。
或者用stackexchange客户端组件,锁实现及示例代码:https://github.com/kidfashion/redlock-cs。
官方介绍文档:http://redis.io/topics/distlock。
Redis分布式锁服务(转)的更多相关文章
- Redis分布式锁服务(八)
阅读目录: 概述 分布式锁 多实例分布式锁 总结 概述 在多线程环境下,通常会使用锁来保证有且只有一个线程来操作共享资源.比如: object obj = new object(); lock (ob ...
- Redis分布式锁服务
阅读目录: 概述 分布式锁 多实例分布式锁 总结 概述 在多线程环境下,通常会使用锁来保证有且只有一个线程来操作共享资源.比如: object obj = new object(); lock (ob ...
- redis实现分布式锁服务
译自Redis官方文档 在多线程共享临界资源的场景下,分布式锁是一种非常重要的组件.许多库使用不同的方式使用redis实现一个分布式锁管理.其中有一部分简单的实现方式可靠性不足,可以通过一些简单的修改 ...
- redis分布式锁和消息队列
最近博主在看redis的时候发现了两种redis使用方式,与之前redis作为缓存不同,利用的是redis可设置key的有效时间和redis的BRPOP命令. 分布式锁 由于目前一些编程语言,如PHP ...
- redis咋么实现分布式锁,redis分布式锁的实现方式,redis做分布式锁 积极正义的少年
前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介 ...
- Redis分布式锁的正确实现方式
前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介 ...
- Redis分布式锁---完美实现
这几天在做项目缓存时候,因为是分布式的所以需要加锁,就用到了Redis锁,正好从网上发现两篇非常棒的文章,来和大家分享一下. 第一篇是简单完美的实现,第二篇是用到的Redisson. Redis分布式 ...
- 关于分布式锁原理的一些学习与思考-redis分布式锁,zookeeper分布式锁
首先分布式锁和我们平常讲到的锁原理基本一样,目的就是确保,在多个线程并发时,只有一个线程在同一刻操作这个业务或者说方法.变量. 在一个进程中,也就是一个jvm 或者说应用中,我们很容易去处理控制,在j ...
- Lua脚本在redis分布式锁场景的运用
目录 锁和分布式锁 锁是什么? 为什么需要锁? Java中的锁 分布式锁 redis 如何实现加锁 锁超时 retry redis 如何释放锁 不该释放的锁 通过Lua脚本实现锁释放 用redis做分 ...
随机推荐
- Linux下rar 命令压缩和解压详解
例1:添加文件或目录到压缩档案中,使用a命令.例如把文件files1添加到abc.rar中,使用a或m命令,a命令把file1文件添加到abc.rar档案中保持原有的file1文件不变,m命令移动fi ...
- maven 如何引入本地jar包
比如我下载了 一.怎么添加jar到本地仓库呢?步骤:1.cmd命令进入该jar包所在路径2.执行命令:mvn install:install-file -Dfile=lucene-queryparse ...
- Ant 学习笔记
1.下载Ant 官方网站http://ant.apache.org/bindownload.cgi 下载最新版本 .zip archive: apache-ant-1.9.4-bin.zip [PG ...
- 09观察者模式ObServer
一.什么是观察者模式 Observer模式是行为模式之一,它的作用是当 一个对象的状态发生变化时,能够自动通知其他 关联对象,自动刷新对象状态. Observer模式提供给关联对象一种同步通信的 手段 ...
- SpringBoot(十七)-- 定时任务
日常开发中,经常会使用定时任务来执行跑批,springboot默认已经帮助我们整合了定时任务. 参考:https://blog.csdn.net/u013845177/article/details/ ...
- LostRoutes项目日志——编辑project.json
第一个Scene编译后运行会报错: Uncaught TypeError: Cannot read property 'style' of null 这是因为没有在project.json中包含已经编 ...
- 如何用Baas快速在腾讯云上开发小程序-系列4:实现客户侧商品列表、商品详情页程序
版权声明:本文由贺嘉 原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/431172001487671163 来源:腾云阁 h ...
- 关于rem适配的3种常用封装
在之前写了一篇关于rem适配的文章,但是没有给出具体的封装,那么今天这里给出常用的三种方法,分享出来供大家参考学习,下面话不多说了,来随着小编一起学习学习吧 一.rem1.js 第一种方法考虑了m端屏 ...
- 7.16顺便贴一下 pep8的标准
PEP8 Python 编码规范 一 代码编排 1 缩进.4个空格的缩进(编辑器都可以完成此功能),不使用Tap,更不能混合使用Tap和空格. 2 每行最大长度79,换行可以使用反斜杠,最好使用圆括号 ...
- 180714、JRebel插件安装配置与破解激活(多方案)详细教程
JRebel 介绍 IDEA上原生是不支持热部署的,一般更新了 Java 文件后要手动重启 Tomcat 服务器,才能生效,浪费不少生命啊.目前对于idea热部署最好的解决方案就是安装JRebel插件 ...