Redis setNX 实现分布式锁(重复数据插入可用其来实现排他锁)
使用Redis的 SETNX 命令可以实现分布式锁,下文介绍其实现方法。
SETNX命令简介
命令格式
SETNX key value
将 key 的值设为 value,当且仅当 key 不存在。
若给定的 key 已经存在,则 SETNX 不做任何动作。
SETNX 是SET if Not eXists的简写。
返回值
返回整数,具体为
- 1,当 key 的值被设置
- 0,当 key 的值没被设置
例子
redis> SETNX mykey “hello”
(integer) 1
redis> SETNX mykey “hello”
(integer) 0
redis> GET mykey
“hello”
redis>
使用SETNX实现分布式锁
多个进程执行以下Redis命令:
SETNX lock.foo <current Unix time + lock timeout + 1>
如果 SETNX 返回1,说明该进程获得锁,SETNX将键 lock.foo 的值设置为锁的超时时间(当前时间 + 锁的有效时间)。
如果 SETNX 返回0,说明其他进程已经获得了锁,进程不能进入临界区。进程可以在一个循环中不断地尝试 SETNX 操作,以获得锁。
解决死锁
考虑一种情况,如果进程获得锁后,断开了与 Redis 的连接(可能是进程挂掉,或者网络中断),如果没有有效的释放锁的机制,那么其他进程都会处于一直等待的状态,即出现“死锁”。
上面在使用 SETNX 获得锁时,我们将键 lock.foo 的值设置为锁的有效时间,进程获得锁后,其他进程还会不断的检测锁是否已超时,如果超时,那么等待的进程也将有机会获得锁。
然而,锁超时时,我们不能简单地使用 DEL 命令删除键 lock.foo 以释放锁。考虑以下情况,进程P1已经首先获得了锁 lock.foo,然后进程P1挂掉了。进程P2,P3正在不断地检测锁是否已释放或者已超时,执行流程如下:
- P2和P3进程读取键 lock.foo 的值,检测锁是否已超时(通过比较当前时间和键 lock.foo 的值来判断是否超时)
- P2和P3进程发现锁 lock.foo 已超时
- P2执行 DEL lock.foo命令
- P2执行 SETNX lock.foo命令,并返回1,即P2获得锁
- P3执行 DEL lock.foo命令将P2刚刚设置的键 lock.foo 删除(这步是由于P3刚才已检测到锁已超时)
- P3执行 SETNX lock.foo命令,并返回1,即P3获得锁
- P2和P3同时获得了锁
从上面的情况可以得知,在检测到锁超时后,进程不能直接简单地执行 DEL 删除键的操作以获得锁。
为了解决上述算法可能出现的多个进程同时获得锁的问题,我们再来看以下的算法。
我们同样假设进程P1已经首先获得了锁 lock.foo,然后进程P1挂掉了。接下来的情况:
- 进程P4执行 SETNX lock.foo 以尝试获取锁
- 由于进程P1已获得了锁,所以P4执行 SETNX lock.foo 返回0,即获取锁失败
- P4执行 GET lock.foo 来检测锁是否已超时,如果没超时,则等待一段时间,再次检测
- 如果P4检测到锁已超时,即当前的时间大于键 lock.foo 的值,P4会执行以下操作
GETSET lock.foo <current Unix timestamp + lock timeout + 1> - 由于 GETSET 操作在设置键的值的同时,还会返回键的旧值,通过比较键 lock.foo 的旧值是否小于当前时间,可以判断进程是否已获得锁
- 假如另一个进程P5也检测到锁已超时,并在P4之前执行了 GETSET 操作,那么P4的 GETSET 操作返回的是一个大于当前时间的时间戳,这样P4就不会获得锁而继续等待。注意到,即使P4接下来将键 lock.foo 的值设置了比P5设置的更大的值也没影响。
另外,值得注意的是,在进程释放锁,即执行 DEL lock.foo 操作前,需要先判断锁是否已超时。如果锁已超时,那么锁可能已由其他进程获得,这时直接执行 DEL lock.foo 操作会导致把其他进程已获得的锁释放掉。
程序代码
用以下python代码来实现上述的使用 SETNX 命令作分布式锁的算法。
LOCK_TIMEOUT = 3
lock = 0
lock_timeout = 0
lock_key = 'lock.foo'
# 获取锁
while lock != 1:
now = int(time.time())
lock_timeout = now + LOCK_TIMEOUT + 1
lock = redis_client.setnx(lock_key, lock_timeout)
if lock == 1 or (now > int(redis_client.get(lock_key))) and now > int(redis_client.getset(lock_key, lock_timeout)):
break
else:
time.sleep(0.001)
# 已获得锁
do_job()
# 释放锁
now = int(time.time())
if now < lock_timeout:
redis_client.delete(lock_key)
java代码:
Redis setNX 实现分布式锁(重复数据插入可用其来实现排他锁)的更多相关文章
- Redis setnx命令 分布式缓存
setnx命令 将 key 的值设为 value,当且仅当 key 不存在. 若给定的 key 已经存在,则 SETNX 不做任何动作. SETNX 是SET if Not eXists的简写. re ...
- 重复数据插入unique列时,锁加在哪?
1.测试目的 当插入重复数据到有unique索引的表中时,采用何种加锁机制. 2.测试思路 利用10046确定是什么操作导致加锁阻塞了进程: dump锁定前最近一次操作的块结构来分析加锁机制. 3.测 ...
- Redis SETNX实现分布式锁
1.某进程1执行 SETNX lock 以尝试获取锁 2.由于某进程2已获得了锁,所以进程1执行 SETNX lock 返回0,即获取锁失败 3.进程1执行 GET lock 来检测锁是否已超时,如果 ...
- (8)MySQL进阶篇SQL优化(InnoDB锁-共享锁、排他锁与意向锁)
1.锁的分类 锁(Locking)是数据库在并发访问时保证数据一致性和完整性的主要机制.之前MyISAM锁章节已经讲过锁分类,而InnoDB锁按照粒度分为锁定整个表的表级锁(table-level l ...
- kafka 如何不消费重复数据?比如扣款,我们不能重复的扣?
其实还是得结合业务来思考,我这里给几个思路: 比如你拿个数据要写库,你先根据主键查一下,如果这数据都有了,你就别插入 了,update 一下好吧. 比如你是写 Redis,那没问题了,反正每次都是 s ...
- 【问答分享第一弹】MySQL锁总结:MySQL行锁、表锁、排他锁、共享锁的特点
大家好,我是小于哥哈.前几天能分享了第一期面试题,MySQL 中有哪几种锁 和 这些锁各有哪些特点 ,这道面试题是经常会被问到的一个面试题,大家反馈的都挺不错的.今天特此来总结一下. 首发于公众号[终 ...
- Mysql共享锁、排他锁、悲观锁、乐观锁
一.相关名词 |--表级锁(锁定整个表) |--页级锁(锁定一页) |--行级锁(锁定一行) |--共享锁(S锁,MyISAM 叫做读锁) |--排他锁(X锁,MyISAM 叫做写锁) |--间隙锁( ...
- 使用Redis SETNX 命令实现分布式锁
基于setnx和getset http://blog.csdn.net/lihao21/article/details/49104695 使用Redis的 SETNX 命令可以实现分布式锁,下文介绍其 ...
- redis setnx 分布式锁
private final String RedisLockKey = "RedLock"; private final long altTimeout = 1 * 60 * 60 ...
随机推荐
- 摘抄转载前辈们的Java集合类总结
本文摘自 Blue Sky:http://www.cnblogs.com/hubcarl JAVA 集合类介绍和使用 类关系示意图Iterable(接口) │ └--Collection (接口) ├ ...
- 使用Git、Git GUI和TortoiseGit
1. 关于命令行 我一直建议在命令行中使用Git或者SVN.因为这样可能更加了解他们的工作方式,也不容易遗漏重要的问题和提醒. 在Windows习惯的驱使下,大多数人是不会看弹出的对话框中有什么信息的 ...
- c#:如何处理对对象进行深度拷贝
/// <summary> /// 对对象进行深度拷贝 /// </summary> /// <param name="obj"></pa ...
- nodejs cookie管理
Cookie 管理 我们可以使用中间件向 Node.js 服务器发送 cookie 信息,以下代码输出了客户端发送的 cookie 信息: // express_cookie.js 文件 var ex ...
- vmware workstation unrecoverable error: (vmui)报错解决方法
实验室7月份刚换了电脑,之前一直用vmware来跑linux搞嵌入式开发,无论是宿舍的笔记本,还是之前用的旧台式机,都可以妥妥的跑vmware没有问题,结果换了新电脑之后,装上vmware works ...
- html5,增加flash插件
<embed src="2.swf" type="" width="500" height="" >< ...
- Weblogic的架构
WebLogic管理控制台 WebLogic服务器提供了一个健壮的基于Web 的工具——管理控制台,它是执行上述任务的主要工具.通过管理控制台,你可以访问WebLogic管理服务.管理服务实现了 S ...
- PostgreSQL的OGG -- bucardo
bucardo是PostgreSQL数据库中实现双向同步的软件,可以实现PostgreSQL数据库的双master的方案,不过bucardo中的同步都是异步的,它是通过触发器记录变化,程序是perl写 ...
- DP专题训练之HDU 2955 Robberies
打算专题训练下DP,做一道帖一道吧~~现在的代码风格完全变了~~大概是懒了.所以.将就着看吧~哈哈 Description The aspiring Roy the Robber has seen a ...
- MySQL获取汉字的首字母
)) ) CHARSET utf8 BEGIN ); )),,), 0xB0A1,0xB0C5,0xB2C1,0xB4EE,0xB6EA,0xB7A2,0xB8C1,0xB9FE,0xBBF7, 0x ...