使用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实现分布式锁.虽然网上已经有各种介 ...
随机推荐
- iOS性能之其他
本篇文章是个引用,因为这些技术我都只是研究过,但是并没有在项目中使用,也没有深入研究,所以只能当做一个笔记了 网络请求 现在大多数的网络请求都是使用的json格式(相信没有APP再使用XML格式了吧) ...
- apache和nginx原理上的不同之处
今天群里提到面试时问到apache和nginx原理有什么不同,一时还真没想起,想到的只是他们的优缺点,便搜索了下.记录学习下.顺便记录下优缺点吧. 原理不同之处: 为什么Nginx的性能要比Apach ...
- javaWEB与cookie
Cookie1. Http协议与Cookie(了解) * Cookie是HTTP协议制定的!先由服务器保存Cookie到浏览器,再下次浏览器请求服务器时把上一次请求得到Cookie再归还给服务器 ...
- Spark踩坑记——共享变量
[TOC] 前言 Spark踩坑记--初试 Spark踩坑记--数据库(Hbase+Mysql) Spark踩坑记--Spark Streaming+kafka应用及调优 在前面总结的几篇spark踩 ...
- POPTEST 测试开发 免费培训课程报名
poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询qq:908821478,咨询电话010-845052 ...
- thinkphp3.2.x多图上传并且生成多张缩略图
html部分 <!DOCTYPE html><html><head><meta http-equiv="Content-Type" con ...
- Android安全开发之启动私有组件漏洞浅谈
0x00 私有组件浅谈 android应用中,如果某个组件对外导出,那么这个组件就是一个攻击面.很有可能就存在很多问题,因为攻击者可以以各种方式对该组件进行测试攻击.但是开发者不一定所有的安全问题都能 ...
- Service详解
/** * 后台执行的定时任务 */ public class LongRunningService extends Service { @Override public IBinder onBind ...
- 常用SQL_官方文档使用
SQL语句基础理论 SQL是操作和检索关系型数据库的标准语言,标准SQL语句可用于操作关系型数据库. 5大主要类型: ①DQL(Data Query Language,数据查询语言)语句,主要由于se ...
- [UWP]了解模板化控件(4):TemplatePart
1. TemplatePart TemplatePart(部件)是指ControlTemplate中的命名元素.控件逻辑预期这些部分存在于ControlTemplate中,并且使用protected ...