码迷,mamicode.com
首页 > 其他好文 > 详细

redis 分布式锁

时间:2020-09-17 16:21:36      阅读:31      评论:0      收藏:0      [点我收藏+]

标签:获取   mysqld   退出   sqli   情况   相互   系统   插入   服务   

redis分布式
1.redis是单线程操作
2.分布式会出现的问题,死锁
3.redis分布式(集群)。多台服务器里面都有多个单机redis。然后这些redis之间相互链接。还有查看各个单台服务器之间是否链接成功,也就是心跳检测
4.在数据方面,他们之间有个锁的问题,叫redis分布式锁

## 常规流程

$redisDb = new Redis(127.0.0.1:6379);
// 获取redis里面的数据
$res = $redisDb->LRANGE();
if(!$res) {
    // 如果数据不存在,则去mysql里面获取
    $mysqlDb = mysqli_connect();
    $res = $mysqlDb->select(*);
    // 获取玩了之后插入redis
    foreach ($res as $k => $v) {
        $redisDb->lPush($v);
    }
}
return $res;

这里会出现一个问题,在获取redis数据的时候,出现抢锁的情况

改动后的流程

// 设置redis锁
$expire = 10;//有效期10秒
$key = lock;//key
$value = time() + $expire;//锁的值 = Unix时间戳 + 锁的有效期

$redisDb = new Redis(127.0.0.1:6379);
// 获取数据之前,先获取锁
$status = true;
while ($status) {
    $lock = $redisDb->setnx($key,$value);
    if($lock) {
        // 如果锁存在且在有效期内,则循环继续获取
        $value = $redisDb->get($key);
        if($value < time()) {
            // 如果在有效期外则删除当前锁,下次进来的时候就直接获取数据且退出循环
            $redisDb->del($key);
        }
    }else{
        $status = false;
        // 获取redis里面的数据
        $res = $redisDb->LRANGE();
        if(!$res) {
            // 如果数据不存在,则去mysql里面获取
            $mysqlDb = mysqli_connect();
            $res = $mysqlDb->select(*);
            // 获取玩了之后插入redis
            foreach ($res as $k => $v) {
                $redisDb->lPush($v);
            }
        }
    }
}

这里原本就可以是最终的版本,但是看到一些言论说。假设进程1del锁之前崩了。那锁会一直存在,进程2和进程3会同时或得到锁。

 

### 最终版

// 设置redis锁
$expire = 10;//有效期10秒
$key = lock;//key
$value = time() + $expire;//锁的值 = Unix时间戳 + 锁的有效期

$redisDb = new Redis(127.0.0.1:6379);
// 获取redis里面的数据
$res = $redisDb->LRANGE();
if(!$res) {
    $status = true;
    while ($status) {
        //设置锁值为当前时间戳 + 有效期
        $lockValue = time() + $expire;
        /**
         * 创建锁
         * 试图以$lockKey为key创建一个缓存,value值为当前时间戳
         * 由于setnx()函数只有在不存在当前key的缓存时才会创建成功
         * 所以,用此函数就可以判断当前执行的操作是否已经有其他进程在执行了
         * @var [type]
         */
        $lock = $redisDb->setnx($key, $lockValue);
        /**
         * 满足两个条件中的一个即可进行操作
         * 1、上面一步创建锁成功;
         * 2、   1)判断锁的值(时间戳)是否小于当前时间    $redis->get()
         *      2)同时给锁设置新值成功    $redis->getset()
         */
        /**
         * ($redisDb->get($key) < time() && $redisDb->getSet($key, $lockValue) < time() )
         * 这个判断的意思是
         * 先判断第一个进程设定的锁是否过期。($redisDb->get($key) < time()
         * 获取到的第一个进程的锁的值再给他设定一个新的值。看他的旧值是否过期   $redisDb->getSet($key, $lockValue) < time()
         */
        if(!empty($lock) || ($redisDb->get($key) < time() && $redisDb->getSet($key, $lockValue) < time() )) {
            //给锁设置生存时间。setnx是系统上给key第一个锁的过期时间,系统不崩,锁会删除。expire是redis自动给key的一个过期时间,系统即使崩了,锁还是会删除
            $redisDb->expire($key, $expire);
            //***********这里执行业务操作
            // 如果数据不存在,则去mysql里面获取
            $mysqlDb = mysqli_connect();
            $res = $mysqlDb->select(*);
            // 获取玩了之后插入redis
            foreach ($res as $k => $v) {
                $redisDb->lPush($v);
            }
            //***********并吧数据插入数据库
            //以上程序走完删除锁
            //检测锁是否过期,过期锁没必要删除
            if($redisDb->ttl($key))
                $redisDb->del($key);
            $status = FALSE;

        } else {
            // 如果锁存在,则表示有其他进程在处理,这里等待
            sleep(1);
        }

    }

}
return $res;

 

redis 分布式锁

标签:获取   mysqld   退出   sqli   情况   相互   系统   插入   服务   

原文地址:https://www.cnblogs.com/chenrunxuan/p/13621799.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!