使用Redis实现分布式锁
在天猫、京东、苏宁等等电商网站上有很多秒杀活动,例如在某一个时刻抢购一个原价1999现在秒杀价只要999的手机时,会迎来一个用户请求的高峰期,可能会有几十万几百万的并发量,来抢这个手机,在高并发的情形下会对数据库服务器、文件服务器、应用服务器造成巨大的压力,严重时甚至宕机了。另一个问题是,秒杀的东西都是有量的,例如一款手机只有10台的量秒杀,那么,在高并发的情况下,成千上万条数据更新数据库(例如10台的量被人抢一台就会在数据集某些记录下减1),那这个时候的先后顺序是很乱的,很容易出现10台的量,抢到的人就不止10个这种严重的问题。那么,以后所说的问题我们该如何去解决呢?使用 分布式锁和任务队列,本节主要阐述基于redis的分布式锁实现思路:
思路很简单,主要用到的redis函数是setnx(),这个应该是实现分布式锁最主要的函数。首先是将某一任务标识名(这里用Lock:order作为标识名的例子)作为键存到redis里,并为其设个过期时间,如果是还有Lock:order请求过来,先是通过setnx()看看是否能将Lock:order插入到redis里,可以的话就返回true,不可以就返回false。
(1)为避免特殊原因导致锁无法释放,在加锁成功后,锁会被赋予一个生存时间(通过lock方法的参数设置或者使用默认值),超出生存时间锁会被自动释放,锁的生存时间默认比较短(秒级),因此,若需要长时间加锁,可以通过expire方法延长锁的生存时间为适当时间,比如在循环内。
(2)系统级的锁当进程无论何种原因时出现crash时,操作系统会自己回收锁,所以不会出现资源丢失,但分布式锁不用,若一次性设置很长时间,一旦由于各种原因出现进程crash 或者其他异常导致unlock未被调用时,则该锁在剩下的时间就会变成垃圾锁,导致其他进程或者进程重启后无法进入加锁区域。
先看加锁的实现代码:这里需要主要两个参数,一个是$timeout,这个是循环获取锁的等待时间,在这个时间内会一直尝试获取锁直到超时,如果为0,则表示获取锁失败后直接返回而不再等待(非阻塞);另一个重要参数的$expire,这个参数指当前锁的最大生存时间,以秒为单位的,它必须大于0,如果超过生存时间锁仍未被释放,则系统会自动强制释放。
有一步严谨的操作,那就是取得当前键的剩余时间,假如这个时间小于0,表示key上没有设置生存时间(key是不会不存在的,因为前面setnx会自动创建)如果出现这种状况,那就是进程的某个实例setnx成功后crash,导致紧跟着的expire没有被调用,这时可以直接设置expire并把锁纳为己用。如果没设置锁失败的等待时间或者已超过最大等待时间了,那就退出循环,反之则隔 $waitIntervalUs 后继续 请求。
public boolean lock(long timeout, int expireSecs) {
long nano = System.nanoTime();
timeout *= MILLI_NANO_CONVERSION;
String lockStart = String.valueOf(System.currentTimeMillis());
Jedis jedis = JedisUtil.getResource();
try {
while ((System.nanoTime() - nano) < timeout) {
if (jedis.setnx(this.key, lockStart) == ) {
jedis.expire(this.key, expireSecs);
this.locked = true;
return this.locked;
}
// 短暂休眠,避免出现活锁
Thread.sleep(, RANDOM.nextInt());
}
String expireStr = jedis.get(key);
Long now = System.currentTimeMillis();
String nowStr = String.valueOf(now);
if (!StringUtils.isNumeric(expireStr)){
return false;
}
//ttl小于0 表示key上没有设置生存时间(key是不会不存在的,因为前面setnx会自动创建)
//如果出现这种状况,那就是进程的某个实例setnx成功后crash 导致紧跟着的expire没有被调用
//这时可以直接设置expire并把锁纳为己用
//In Redis 2.6 or older, if the Key does not exists or does not have an associated expire, -1 is returned.
//In Redis 2.8 or newer, if the Key does not have an associated expire, -1 is returned or if the Key does not exists, -2 is returned.
long ttlValue = jedis.ttl(this.key);
if(ttlValue<){
jedis.setnx(key, nowStr);
jedis.expire(key, expireSecs);
return true;
}
Long expireLong = Long.parseLong(expireStr);
if (now - expireLong > expireSecs * ){
jedis.del(key);
jedis.setnx(key, nowStr);
jedis.expire(key, expireSecs);
return true;
}
} catch (Exception e) {
if (jedis != null) {
JedisUtil.returnBrokenResource(jedis);
}
throw new RuntimeException("Locking error", e);
} finally {
if (jedis != null) {
JedisUtil.returnResource(jedis);
}
}
return false;
}
使用Redis实现分布式锁的更多相关文章
- 基于redis 实现分布式锁的方案
在电商项目中,经常有秒杀这样的活动促销,在并发访问下,很容易出现上述问题.如果在库存操作上,加锁就可以避免库存卖超的问题.分布式锁使分布式系统之间同步访问共享资源的一种方式 基于redis实现分布式锁 ...
- 用Redis构建分布式锁-RedLock(真分布)
在不同进程需要互斥地访问共享资源时,分布式锁是一种非常有用的技术手段. 有很多三方库和文章描述如何用Redis实现一个分布式锁管理器,但是这些库实现的方式差别很大,而且很多简单的实现其实只需采用稍微增 ...
- 用Redis实现分布式锁 与 实现任务队列(转)
这一次总结和分享用Redis实现分布式锁 与 实现任务队列 这两大强大的功能.先扯点个人观点,之前我看了一篇博文说博客园的文章大部分都是分享代码,博文里强调说分享思路比分享代码更重要(貌似大概是这个意 ...
- 利用多写Redis实现分布式锁原理与实现分析(转)
利用多写Redis实现分布式锁原理与实现分析 一.关于分布式锁 关于分布式锁,可能绝大部分人都会或多或少涉及到. 我举二个例子:场景一:从前端界面发起一笔支付请求,如果前端没有做防重处理,那么可能 ...
- Redis实现分布式锁
http://redis.io/topics/distlock 在不同进程需要互斥地访问共享资源时,分布式锁是一种非常有用的技术手段. 有很多三方库和文章描述如何用Redis实现一个分布式锁管理器,但 ...
- 基于redis的分布式锁
<?php /** * 基于redis的分布式锁 * * 参考开源代码: * http://nleach.com/post/31299575840/redis-mutex-in-php * * ...
- Redis实现分布式锁与任务队列
Redis实现分布式锁 与 实现任务队列 这一次总结和分享用Redis实现分布式锁 与 实现任务队列 这两大强大的功能.先扯点个人观点,之前我看了一篇博文说博客园的文章大部分都是分享代码,博文里强调说 ...
- 基于Redis实现分布式锁(1)
转自:http://blog.csdn.net/ugg/article/details/41894947 背景在很多互联网产品应用中,有些场景需要加锁处理,比如:秒杀,全局递增ID,楼层生成等等.大部 ...
- redis咋么实现分布式锁,redis分布式锁的实现方式,redis做分布式锁 积极正义的少年
前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介 ...
随机推荐
- SVN使用小记
SVN(Subversion)是优秀的版本控制工具,之前在eclipse里面项目管理的时候,File-->Import-->SVN-->从SVN检出项目-->创建新的资源库位置 ...
- 手机自动化测试:Appium源码分析之跟踪代码分析七
手机自动化测试:Appium源码分析之跟踪代码分析七 poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.poptest推出手机自 ...
- 老李分享:《Linux Shell脚本攻略》 要点(一)
老李分享:<Linux Shell脚本攻略> 要点(一) 第一章:Shell起步基础 1.变量:在bash中,每一个变量的值都是字符串.无论你给变量赋值时,有没有使用引号,值都会以字符 ...
- Grafana中多租户设置
Grafana中通过设置不同的组织,以及将用户分配到不同组织,来做到多租户,类似门户的概念. Grafana默认是不允许非管理员用户创建新的组织的,这个可以通过修改配置文件以允许非管理员用户创建组织: ...
- ReactJS入门2:组件状态
React组件可以简单看做是包含props和states的函数. 上一节总结了创建新组建和数据属性的传递.本节主要讲解组件的状态. React认为UI是不同状态的展现.在React中,开发者只需更新组 ...
- poj 3070 Fibonacci (矩阵快速幂乘/模板)
题意:给你一个n,输出Fibonacci (n)%10000的结果 思路:裸矩阵快速幂乘,直接套模板 代码: #include <cstdio> #include <cstring& ...
- Python入门(一):PTVS写Python程序,调试模式下input()提示文字乱码问题
前两天写了Python入门(一),里面提到,使用VS2013+PTVS进行Python开发. 就在准备为第二篇写个demo的时候,发现了一个问题,各种解决无果,有些纠结 Python中输入函数是inp ...
- 安卓 ADB常见问题整理
以下都是ADB连接问题,可以通过尝试如下步骤,由简单度排序 1. 插拔下USB连接线 2. 关闭USB模式再打开 3. 执行以下命令 adb kill-server adb start-server ...
- python黑魔法之metaclass
最近了解了一下python的metaclass,在学习的过程中,把自己对metaclass的理解写出来和大家分享. 首先, metaclass 中文叫元类,这个元类怎么来理解呢.我们知道,在Pytho ...
- 封装Web Uploader 上传插件、My97DatePicker、百度 编辑器 的使用 (ASP.NET MVC)
Web Uploader: WebUploader是由Baidu WebFE(FEX)团队开发的一个简单的以HTML5为主,FLASH为辅的现代文件上传组件.在现代的浏览器里面能充分发挥HTML5的优 ...