对于商品抢购等并发场景下,可能会出现超卖的现象,这时就需要解决并发所带来的这些问题了。在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