有這麼一個需求:生成文件的時候,由於多用戶都有權限進行生成,防止並發下,導致生成的結果出現錯誤,需要對生成的過程進行加鎖,只容許一個用戶在一個時間內進行操作,這個時候就需要用到鎖了,將這個操作過程鎖起來。在用了cache的時候,cache失效可能導致瞬間的多數並發請求穿透到數據庫此時也可以得需要用鎖在同一並發的過程中將這個操作鎖定。
針對以上的2種情況,現在的解決方法是對處理過程進行鎖機制,通過PHP實現如下:
用到了Eaccelerator的內存鎖和文件鎖,原理:判斷系統中是否安了EAccelerator如果有則使用內存鎖,如果不存在,則進行文件鎖。根據帶入的key的不同可以實現多個鎖直接的並行處理,類似Innodb的行級鎖。
具體類如下:
<?php /** * CacheLock 進程鎖,主要用來進行cache失效時的單進程cache獲取,防止過多的SQL請求穿透到數據庫 * 用於解決PHP在並發時候的鎖控制,通過文件/eaccelerator進行進程間鎖定 * 如果沒有使用eaccelerator則進行進行文件鎖處理,會做對應目錄下產生對應粒度的鎖 * 使用了eaccelerator則在內存中處理,性能相對較高 * 不同的鎖之間並行執行,類似mysql innodb的行級鎖 * 本類在sunli的phplock的基礎上做了少許修改 http://code.google.com/p/phplock * @author yangxinqi * */ class CacheLock { //文件鎖存放路徑 private $path = null; //文件句柄 private $fp = null; //鎖粒度,設置越大粒度越小 private $hashNum = 100; //cache key private $name; //是否存在eaccelerator標志 private $eAccelerator = false; /** * 構造函數 * 傳入鎖的存放路徑,及cache key的名稱,這樣可以進行並發 * @param string $path 鎖的存放目錄,以"/"結尾 * @param string $name cache key */ public function __construct($name,$path='lock\\') { //判斷是否存在eAccelerator,這裡啟用了eAccelerator之後可以進行內存鎖提高效率 $this->eAccelerator = function_exists("eaccelerator_lock"); if(!$this->eAccelerator) { $this->path = $path.($this->_mycrc32($name) % $this->hashNum).'.txt'; } $this->name = $name; } /** * crc32 * crc32封裝 * @param int $string * @return int */ private function _mycrc32($string) { $crc = abs (crc32($string)); if ($crc & 0x80000000) { $crc ^= 0xffffffff; $crc += 1; } return $crc; } /** * 加鎖 * Enter description here ... */ public function lock() { //如果無法開啟ea內存鎖,則開啟文件鎖 if(!$this->eAccelerator) { //配置目錄權限可寫 $this->fp = fopen($this->path, 'w+'); if($this->fp === false) { return false; } return flock($this->fp, LOCK_EX); }else{ return eaccelerator_lock($this->name); } } /** * 解鎖 * Enter description here ... */ public function unlock() { if(!$this->eAccelerator) { if($this->fp !== false) { flock($this->fp, LOCK_UN); clearstatcache(); } //進行關閉 fclose($this->fp); }else{ return eaccelerator_unlock($this->name); } } } ?>
使用如下:
$lock = new CacheLock('key_name'); $lock->lock(); //logic here $lock->unlock(); //使用過程中需要注意下文件鎖所在路徑需要有寫權限.