PHP文件锁类防止并发

对于商品抢购等并发场景下,可能会出现超卖的现象,这时就需要解决并发所带来的这些问题了。在PHP语言中并没有原生的提供并发的解决方案,因此就需要借助其他方式来实现并发控制。

方案一:使用文件锁排它锁
方案二:使用MySQL数据库提供的悲观锁
方案三:使用队列
方案四:使用Redis/Memcached

下面做种介绍下方案一,文件锁:

  • flock函数用于获取文件的锁,这个锁同时只能被一个线程获取到,其它没有获取到锁的线程要么阻塞,要么获取失败。flock()函数锁定或释放文件 若成功,则返回 true。若失败,则返回 false
  • flock($fp,lock,block); block 若设置为true 则当进行锁定时阻挡其他进程

LOCK_SH 建立共享锁定。多个进程可同时对同一个文件作共享锁定。
LOCK_EX 建立互斥锁定。一个文件同时只有一个互斥锁定。
LOCK_UN 解除文件锁定状态。
LOCK_NB 无法建立锁定时,不阻断。通常与LOCK_SH或LOCK_EX 做OR(|)组合。

文件锁一个简单的封装类如下:

/**
 * 用于解决PHP在并发时候的锁控制,不同的锁之间并行执行,类似mysql innodb的行级锁
 */
class FileLock
{
    //文件锁存放路径
    private $path = '';
    //文件句柄
    private $fp = '';
    //锁文件
    private $lockFile = '';
    /**
     * 构造函数
     * @param string $path 锁的存放目录
     * @param string $name 锁 KEY
     */
    public function __construct($name, $path = '')
    {
        if (empty($path)) {
            $this->path = dirname(__FILE__) . '/';
        } else {
            $this->path = $path;
        }

        $this->lockFile = $this->path . md5($name) . '.lock';
    }

    /**
     * 加锁
     */
    public function lock()
    {
        $this->fp = fopen($this->lockFile, 'a+');
        if ($this->fp === false) {
            return false;
        }
        //LOCK_EX 获取独占锁
        //LOCK_NB 无法建立锁定时,不阻塞
        return flock($this->fp, LOCK_EX | LOCK_NB);
    }

    /**
     * 解锁
     */
    public function unlock()
    {
        if ($this->fp !== false) {
            @flock($this->fp, LOCK_UN);
            clearstatcache();
        }
        @fclose($this->fp);
        @unlink($this->lockFile);
    }
}

文件锁类使用示例:

$userid   = 21;
$article_id = 108;

//对业务请求加锁
$lock    = new FileLock($userid . $article_id);
$lockResult = $lock->lock();

if (!$lockResult) {
    echo '当前请求速度过快,请稍后访问!';
    $lock->unlock();
    exit;
}

/*
正常的业务逻辑处理
 */

//业务逻辑处理完毕解锁
$lock->unlock();

Refer:
http://nearby.wang/s_64.html

你可能感兴趣的:(PHP文件锁类防止并发)