使用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实现分布式锁.虽然网上已经有各种介 ...
随机推荐
- dellR720重启找不到启动引导项,手动选择也无用。
机器重启后显示 no boot device available.(如下图)检查bios中设置也是没问题的,因为装完系统后根本没动过什么.F11手动选择启动项也还是会跳到这里来. 这台机子做的Raid ...
- [SinGuLaRiTy] ZKW线段树
[SinGuLaRiTy-1007] Copyrights (c) SinGuLaRiTy 2017. All Rights Reserved. 关于ZKW线段树 Zkw线段树是清华大学张昆玮发明非递 ...
- poptest老李谈分布式与集群 2
集群分类 Linux集群主要分成三大类( 高可用集群, 负载均衡集群,科学计算集群) 高可用集群( High Availability Cluster)负载均衡集群(Load Balance Clus ...
- Wpf学习之路……
Wpf学习之路-- Wpf是 .net中一门做winform的技术,和传统的winform的区别在于: 1. 原来的winform就是拖控件,而wpf的控件都死自己拿标记语言(xaml ...
- web开发与IC卡读卡器
前段时间有个项目在客户端web下使用IC卡读卡器,试了很多种方案都觉得麻烦,最后在网上找了个现成的方案,采用了YW-605HA读卡器,厂家就不说了,免得说做广告.开发起来也挺简单. 他们将IC卡读卡器 ...
- iOS开发tips-神奇的UITableView
概述 UITableView是iOS开发中使用频率最高的UI控件,在前面的文章中对于UITableView的具体用法有详细的描述,今天主要看一些UITableView开发中的常见一些坑,这些坑或许不深 ...
- struts1实现简单的登录功能(附源码)
环境:MyEclipse 14 ...
- (二)一起学 Java Collections Framework 源码之 AbstractCollection
. . . . . 目录 (一)一起学 Java Collections Framework 源码之 概述(未完成) (二)一起学 Java Collections Framework 源码之 Abs ...
- 简谈-Python一些常用的爬虫技巧
第一种:基本的网页抓取 get方法 import urllib2url = "链接response = urllib2.urlopen(url)print response.read() p ...
- 在ASP dot Net Core MVC中用Controllers调用你的Asp dotnet Core Web API 实现CRUD到远程数据库中,构建你的分布式应用(附Git地址)
本文所有的东西都是在dot Net Core 1.1环境+VS2017保证测试通过. 本文接着上次文章接着写的,不了解上篇文章的可能看着有点吃力.我尽量让大家都能看懂.这是上篇文章的连接http:// ...