Redis 实现分布锁

<?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秒后再尝试执行操作
        }
    }
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 4

加个异常处理是不是好点

3年前 评论

这个实现有问题,直接看 laravel 的源码

public function acquire()
{
    if ($this->seconds > 0) {
        return $this->redis->set($this->name, $this->owner, 'EX', $this->seconds, 'NX') == true;
    } else {
        return $this->redis->setnx($this->name, $this->owner) === 1;
    }
}
3年前 评论
kakasaber (楼主) 3年前

你这要是锁住了,猪都可以飞上天,去了解下原子操作吧

3年前 评论

如果 SETNX 命令成功了,但是程序发生了不可预知的错误导致 EXPIRE 命令没有执行,就造成死锁了。这段代码没有保证原子性呀!可以试试使用 Lua 脚本来保证命令原子性。如果 Redis 版本在 2.6.12 以上可灵活使用 SET 命令来保证原子性。

还有一个小疏忽,如果 A 拿到了锁并顺利进行业务逻辑运行。但是运行过久,导致锁超时释放了。这个时候 B 拿到了锁进行业务逻辑处理。此时 A 又完成了逻辑处理并进行锁释放操作。这个时候 A 会把 B 的锁给释放了。

3年前 评论
myvsmyx 3年前
乘风破浪 3年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!