PHP DIY 系列------应用篇:1. 分布式锁


含义

为了防止分布式系统中的多个进程之间相互干扰,我们需要一种分布式协调技术来对这些进程进行调度。而这个分布式协调技术的核心就是来实现这个分布式锁。

分析

  • 在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行
  • 高可用的获取锁与释放锁
  • 高性能的获取锁与释放锁
  • 具备可重入特性(可理解为重新进入,由多于一个任务并发使用,而不必担心数据错误)
  • 具备锁失效机制,防止死锁
  • 具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败

实现方式

常见的有:

  • 基于Redis
  • 基于mysql
  • 基于Zookeeper

具体实现

考虑到我们使用的PHP语言,简单结合Redis来实现一下分布式锁。

我们来画个简单的原理图:

image

原理十分简单,那么我们如何实现呢?

这里我们用到两个知识点:

  • Redis的setnx方法
  • php的register
function processLock($redis, $key, $ttl = 5)
{
    $ret = $redis->setnx($key, 1);
    if ($ret) {
        $redis->expire($key, $ttl);

        register_shutdown_function(function () use ($redis, $key){
            $redis->del($key);
            });
    }

    return $ret;
}

demo里的$redis是一个redis实例,我们在前面已经实现过,不多说明。

加锁

我们使用的setnx命令,senx表示“SET if Not eXists”,如果key不存在,则set,如果存在,则不set

redis> SETNX mykey "Hello"
(integer) 1
redis> SETNX mykey "World"
(integer) 0
redis> GET mykey
"Hello"

解锁

使用的是del,即删除这个key。这里结合了 register_shutdown_function — 注册一个会在php中止时执行的函数,设置了之后我们就可以不必手动去解锁

锁超时

这里给锁加了一个expire过期时间5秒,目的是防止使用这个方法之后的执行了“神奇”代码抛出了语法错误,导致register_shutdown_function里的代码没有执行,虽然请求结束,但是加锁后,没有解锁,影响了后续请求。

问题

  • 加入setnx之后expire之前程序异常了怎么办?
php
本作品采用《CC 协议》,转载必须注明作者和本文链接
分享开发知识,欢迎交流。公众号:开源到
附言 1  ·  4年前

setnx的同时设置过期时间:

$redis->rawCommand('set', 'sai', 1, 'EX', 60, 'NX');
讨论数量: 1

感谢楼主分享哦 :grin:

4年前 评论
已下线 (楼主) 4年前

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