一、分布式锁的作用:

redis写入时不带锁定功能,为防止多个进程同时进行一个操作,出现意想不到的结果,so...对缓存进行插入更新操作时自定义加锁功能。

二、Redis的NX后缀命令

  Redis有一系列的命令,其特点是以NX结尾,NX的意思可以理解为 NOT EXISTS(不存在),SETNX命令 (SET IF NOT EXISTS) 可以理解为如果不存在则插入,Redis分布式锁的实现主要就是使用SETNX命令。

三、实现原理

在进程请求执行操作前进行判断,加锁是否成功,加锁成功允许执行下步操作;

如果不成功,则判断锁的值(时间戳)是否大于当前时间,如果大于当前时间,则获取锁失败不允许执行下步操作;

如果锁的值(时间戳)小于当前时间,并且GETSET命令获取到的锁的旧值依然小于当前时间,则获取锁成功允许执行下步操作;

如果锁的值(时间戳)小于当前时间,并且GETSET命令获取到的锁的旧值大于当前时间,则获取锁失败不允许执行下步操作;

四、$redis->setnx() 设置锁

$expire = 10;//有效期10秒
$key = 'lock';//key
$value = time() + $expire;//锁的值 = Unix时间戳 + 锁的有效期
$lock = $redis->setnx($key, $value);
//判断是否上锁成功,成功则执行下步操作
if(!empty($lock))
{
//下步操作...
}

如果返回 1 ,则表示当前进程获得锁,并获得了当前插入/更新缓存的操作权限。

如果返回 0,表示锁已被其他进程获取,这是进程可以返回结果或者等待当前锁失效再请求。

五、解决死锁

  如果只用SETNX命令设置锁的话,如果当持有锁的进程崩溃或删除锁失败时,其他进程将无法获取到锁,问题就大了。

解决方法是在获取锁失败的同时获取锁的值,并将值与当前时间进行对比,如果值小于当前时间说明锁以过期失效,进程可运用Redis的DEL命令删除该锁。

$expire = 10;//有效期10秒
$key = 'lock';//key
$value = time() + $expire;//锁的值 = Unix时间戳 + 锁的有效期
$status = true;
while($status)
{
$lock = $redis->setnx($key, $value);
if(empty($lock))
{
$value = $redis->get($key);
if($value < time())
{
$redis->del($key);
}
}else{
$status = false;
//下步操作....
}
}

  但是,简单粗暴的用DEL命令删除锁再SETNX命令上锁也会出现问题。比如,进程1获得锁后崩溃或删除锁失败,这时进程2检测到锁存在当已过期,用DEL命令删除锁并用SETNX命令设置锁,进程3也检测到锁过期,也用DEL命令删除锁也用SETNX命令设置了锁,这时进程2和进程3同时获得了锁。问题大了!

  为了解决这个问题,这里用到了Redis的GETSET命令,GETSET命令在给锁设置新值的同时返回锁的旧值,这里利用了GETSET命令同时获取和赋值的特性,在此期间其他进程无法修改锁的值。

  例如:

    进程1获得锁后操作超时/崩溃/删除锁失败,

    进程2检测到锁已存在,但获取锁的值对比当前时间发现锁已过期,

    进程2通过GETSET命令重新给锁赋予新的值,并获取到的锁的旧值,再次对比锁的旧值与当前时间,如果锁的旧值依然小于当前时间的话,这时进程2就可以忽略进程1余留下的废锁进行下步操作了。

    进程2完成下步操作后返回前应该删除锁,但在删除锁时可以先检测锁是否还未过期,未过期才做删除操作,已过期的就没必要在去删除锁了,因为很有可能其他进程检测到锁过期时已经去获取锁了。

    这里要说明的是,如果有其他进程在进程2之前获取到锁,那么进程2将获取锁失败,但是进程2在用GETSET获取锁的旧值时也赋予了锁新的值,改写了其他进程赋予锁的超时值。看到这大家可能会有疑问了,进程2没获取到锁怎么能改变锁的值呢?是的,进程2改变了锁的原有值,但这一点小小的时间误差带来的影响是可以忽略。

以下是Redis实现分布式锁的完整PHP代码:

<?php
/**
* 实现Redis分布锁
*/ $key = 'test'; //要更新信息的缓存KEY
$lockKey = 'lock:'.$key; //设置锁KEY
$lockExpire = 10; //设置锁的有效期为10秒 //获取缓存信息
$result = $redis->get($key);
//判断缓存中是否有数据
if(empty($result))
{
$status = TRUE;
while ($status)
{
//设置锁值为当前时间戳 + 有效期
$lockValue = time() + $lockExpire;
/**
* 创建锁
* 试图以$lockKey为key创建一个缓存,value值为当前时间戳
* 由于setnx()函数只有在不存在当前key的缓存时才会创建成功
* 所以,用此函数就可以判断当前执行的操作是否已经有其他进程在执行了
* @var [type]
*/
$lock = $redis->setnx($lockKey, $lockValue);
/**
* 满足两个条件中的一个即可进行操作
* 1、上面一步创建锁成功;
* 2、 1)判断锁的值(时间戳)是否小于当前时间 $redis->get()
* 2)同时给锁设置新值成功 $redis->getset()
*/
if(!empty($lock) || ($redis->get($lockKey) < time() && $redis->getSet($lockKey, $lockValue) < time() ))
{
//给锁设置生存时间
$redis->expire($lockKey, $lockExpire);
//******************************
//此处执行插入、更新缓存操作...
//****************************** //以上程序走完删除锁
//检测锁是否过期,过期锁没必要删除
if($redis->ttl($lockKey))
$redis->del($lockKey);
$status = FALSE;
}else{
/**
* 如果存在有效锁这里做相应处理
* 等待当前操作完成再执行此次请求
* 直接返回
*/
sleep(2);//等待2秒后再尝试执行操作
}
}
}

实现分布式锁用到的Redis命令介绍:

setnx(key, value)

将key的值设为value,当且仅当key不存在。

若给定的key已经存在,则SETNX不做任何动作。

SETNX是”SET if Not eXists”(如果不存在,则SET)的简写。

返回值:

设置成功,返回1。

设置失败,返回0。

get(key)

返回key所关联的字符串值。

如果key不存在则返回特殊值nil。

假如key储存的值不是字符串类型,返回一个错误,因为GET只能用于处理字符串值。

返回值:

key的值。

如果key不存在,返回nil。

 getset(key, value)

将给定key的值设为value,并返回key的旧值。

当key存在但不是字符串类型时,返回一个错误。

返回值:

返回给定key的旧值(old value)。

当key没有旧值时,返回nil。

 expire(key, seconds)

为给定key设置生存时间。

当key过期时,它会被自动删除。

在Redis中,带有生存时间的key被称作“易失的”(volatile)。

在低于2.1.3版本的Redis中,已存在的生存时间不可覆盖。

从2.1.3版本开始,key的生存时间可以被更新,也可以被PERSIST命令移除。(详情参见 http://redis.io/topics/expire)。

返回值:

设置成功返回1。

当key不存在或者不能为key设置生存时间时(比如在低于2.1.3中你尝试更新key的生存时间),返回0。

    ttl(key)

返回给定key的剩余生存时间(time to live)(以秒为单位)。

返回值:

key的剩余生存时间(以秒为单位)。

当key不存在或没有设置生存时间时,返回-1 。

del(key)

移除给定的一个或多个key。

返回值:

被移除key的数量。

Redis实现分布式锁 php的更多相关文章

  1. 基于redis 实现分布式锁的方案

    在电商项目中,经常有秒杀这样的活动促销,在并发访问下,很容易出现上述问题.如果在库存操作上,加锁就可以避免库存卖超的问题.分布式锁使分布式系统之间同步访问共享资源的一种方式 基于redis实现分布式锁 ...

  2. 用Redis构建分布式锁-RedLock(真分布)

    在不同进程需要互斥地访问共享资源时,分布式锁是一种非常有用的技术手段. 有很多三方库和文章描述如何用Redis实现一个分布式锁管理器,但是这些库实现的方式差别很大,而且很多简单的实现其实只需采用稍微增 ...

  3. 用Redis实现分布式锁 与 实现任务队列(转)

    这一次总结和分享用Redis实现分布式锁 与 实现任务队列 这两大强大的功能.先扯点个人观点,之前我看了一篇博文说博客园的文章大部分都是分享代码,博文里强调说分享思路比分享代码更重要(貌似大概是这个意 ...

  4. 利用多写Redis实现分布式锁原理与实现分析(转)

    利用多写Redis实现分布式锁原理与实现分析   一.关于分布式锁 关于分布式锁,可能绝大部分人都会或多或少涉及到. 我举二个例子:场景一:从前端界面发起一笔支付请求,如果前端没有做防重处理,那么可能 ...

  5. Redis实现分布式锁

    http://redis.io/topics/distlock 在不同进程需要互斥地访问共享资源时,分布式锁是一种非常有用的技术手段. 有很多三方库和文章描述如何用Redis实现一个分布式锁管理器,但 ...

  6. 基于redis的分布式锁

    <?php /** * 基于redis的分布式锁 * * 参考开源代码: * http://nleach.com/post/31299575840/redis-mutex-in-php * * ...

  7. Redis实现分布式锁与任务队列

    Redis实现分布式锁 与 实现任务队列 这一次总结和分享用Redis实现分布式锁 与 实现任务队列 这两大强大的功能.先扯点个人观点,之前我看了一篇博文说博客园的文章大部分都是分享代码,博文里强调说 ...

  8. 使用Redis实现分布式锁

    在天猫.京东.苏宁等等电商网站上有很多秒杀活动,例如在某一个时刻抢购一个原价1999现在秒杀价只要999的手机时,会迎来一个用户请求的高峰期,可能会有几十万几百万的并发量,来抢这个手机,在高并发的情形 ...

  9. 基于Redis实现分布式锁(1)

    转自:http://blog.csdn.net/ugg/article/details/41894947 背景在很多互联网产品应用中,有些场景需要加锁处理,比如:秒杀,全局递增ID,楼层生成等等.大部 ...

  10. redis咋么实现分布式锁,redis分布式锁的实现方式,redis做分布式锁 积极正义的少年

    前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介 ...

随机推荐

  1. HDU1083 :Courses(二分图匹配)

    Cources Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total S ...

  2. saltstack:multi-master configuration

    官方手册地址:http://docs.saltstack.com/topics/tutorials/multimaster.html 总结起来,有以下几步: Create a redundant ma ...

  3. 题解【luogu P2421 bzoj P1407 [NOI2002]荒岛野人】

    洛谷题目链接 bzoj题目链接 题目大意:给定\(n\)组\(C_i, P_i, L_i\),求最小的\(M\)使得对于任意的\(i,j (1 \leq i, j \leq n)\) \[C_i + ...

  4. 理解JavaScript的prototype和__proto__

    首先,要明确几个点: 1.在JS里,万物皆对象. 方法(Function)是对象,方法的原型(Function.prototype)是对象.因此,它们都会具有对象共有的特点.即:对象具有属性__pro ...

  5. ZooKeeper概述(三)

    ZooKeeper:分布式应用的分布协调服务 ZooKeeper是一个为分布式应用提供的分布的开源的协调服务.它暴露一组简单的原子操作,分布式系统可以在这之上为同步,配置管理,和组和命名实现更高级的服 ...

  6. 【20161109】noip模拟赛

    1.Game [题目描述] 明明和亮亮在玩一个游戏.桌面上一行有n个格子,一些格子中放着棋子.明明和亮亮轮流选择如下方式中的一种移动棋子(图示中o表示棋子,*表示空着的格子): 1) 当一枚棋子的右边 ...

  7. 【HNOI】 c tree-dp

    [题目描述]给定一个n个节点的树,每个节点有两个属性值a[i],b[i],我们可以在树中选取一个连通块G,这个连通块的值为(Σa[x])(Σb[x]) x∈G,求所有连通块的值的和,输出答案对1000 ...

  8. vue双向数据绑定的原理-object.defineProperty() 用法

    有关双向数据绑定的原理 关于数据双向绑定的理解:利用了 Object.defineProperty() 这个方法重新给对象定义了新属性,在操作新属性分别为为获取属性值(调用get方法)和设置属性值(调 ...

  9. C# 关于调用微信接口的代码

    调用微信接口前需要准备的内容. 1.微信公众平台的appid 2.微信公众平台的secret 3..获取tokenid 4.获取ticket 5.生成签名的随机串 6.生成签名的时间戳 7.生成签名 ...

  10. php中使用static方法

    <?php class Char{ public static $number = 0; public static $name; function __construct($what){ se ...