對于商品搶購等并發(fā)場景下谎碍,可能會出現(xiàn)超賣的現(xiàn)象,這時就需要解決并發(fā)所帶來的這些問題了们颜。在PHP語言中并沒有原生的提供并發(fā)的解決方案厨姚,因此就需要借助其他方式來實現(xiàn)并發(fā)控制衅澈。
方案一:使用文件鎖排它鎖
方案二:使用MySQL數(shù)據(jù)庫提供的悲觀鎖
方案三:使用隊列
方案四:使用Redis/Memcached
下面做種介紹下方案一,文件鎖:
- flock函數(shù)用于獲取文件的鎖谬墙,這個鎖同時只能被一個線程獲取到今布,其它沒有獲取到鎖的線程要么阻塞,要么獲取失敗芭梯。flock()函數(shù)鎖定或釋放文件 若成功险耀,則返回 true。若失敗玖喘,則返回 false
- flock($fp,lock,block); block 若設(shè)置為true 則當(dāng)進行鎖定時阻擋其他進程
LOCK_SH 建立共享鎖定。多個進程可同時對同一個文件作共享鎖定蘑志。
LOCK_EX 建立互斥鎖定累奈。一個文件同時只有一個互斥鎖定贬派。
LOCK_UN 解除文件鎖定狀態(tài)。
LOCK_NB 無法建立鎖定時澎媒,不阻斷搞乏。通常與LOCK_SH或LOCK_EX 做OR(|)組合。
文件鎖一個簡單的封裝類如下:
/**
* 用于解決PHP在并發(fā)時候的鎖控制戒努,不同的鎖之間并行執(zhí)行请敦,類似mysql innodb的行級鎖
*/
class FileLock
{
//文件鎖存放路徑
private $path = '';
//文件句柄
private $fp = '';
//鎖文件
private $lockFile = '';
/**
* 構(gòu)造函數(shù)
* @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;
//對業(yè)務(wù)請求加鎖
$lock = new FileLock($userid . $article_id);
$lockResult = $lock->lock();
if (!$lockResult) {
echo '當(dāng)前請求速度過快储玫,請稍后訪問侍筛!';
$lock->unlock();
exit;
}
/*
正常的業(yè)務(wù)邏輯處理
*/
//業(yè)務(wù)邏輯處理完畢解鎖
$lock->unlock();
Refer:
http://nearby.wang/s_64.html