设为首页 收藏本站
查看: 1021|回复: 0

[经验分享] Redis实现分布式锁 php

[复制链接]

尚未签到

发表于 2015-7-21 08:37:24 | 显示全部楼层 |阅读模式
  一、分布式锁的作用:
  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代码:

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-88860-1-1.html 上篇帖子: redis在PHP中的基本使用案例 下篇帖子: mac os x用macport安装redis
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表