為什麼需要文件鎖?
多進程/多執行緒同時 fwrite 同一檔案會導致資料交錯、遺失。文件鎖將並發轉為串行,保證「事務」完整性。
flock() 基本語法
bool flock(resource $handle, int $operation [, int &$wouldblock])
| 操作常數 | 說明 |
LOCK_SH | 共享鎖(讀鎖),多進程可同時持有 |
LOCK_EX | 獨佔鎖(寫鎖),同一時間僅一進程持有 |
LOCK_UN | 釋放鎖 |
LOCK_NB | 非阻塞模式(配合 LOCK_EX/LOCK_SH 使用),無法取得鎖立即返回 false |
完整範例:安全追加寫入
$fp = fopen('test.txt', 'a'); // 追加模式,建檔若不存在
$count = 10;
if (flock($fp, LOCK_EX)) { // 阻塞等待獨佔鎖
for ($i = 1; $i < $count; $i++) {
fwrite($fp, "text2_$i\r\n");
echo "test2 " . date('H:i:s') . "\n";
sleep(1); // 模擬業務處理
echo "test2 " . date('H:i:s') . "\n";
}
flock($fp, LOCK_UN); // 釋放鎖
} else {
echo "Couldn't lock the file!\n";
}
fclose($fp);
進階:非阻塞嘗試與超時
$fp = fopen('lock.txt', 'c+'); // 開啟/建立,指標在開頭
$timeout = 5; // 秒
$start = time();
while (time() - $start < $timeout) {
if (flock($fp, LOCK_EX | LOCK_NB)) {
// 取得鎖,執行關鍵區
ftruncate($fp, 0);
fwrite($fp, "pid=" . getmypid() . "\ntime=" . time());
flock($fp, LOCK_UN);
break;
}
usleep(100000); // 100ms 重試
}
fclose($fp);
最佳實踐與陷阱
- 鎖檔案與資料檔案分離:建立專用
.lock 檔,避免截斷資料檔。
- fopen 模式:
a 追加寫入鎖定尾部;c+ 讀寫不截斷,適合計數器。
- NFS/網路檔案系統:
flock 可能不可靠,改用資料庫行鎖(SELECT ... FOR UPDATE)或 Redis 分散式鎖(Redlock)。
- 進程異常退出:PHP 進程結束會自動釋放鎖,但
fclose() 前務必 flock($fp, LOCK_UN) 以利他進程立即取得。
- Windows:
flock 在 Windows 上為強制鎖,行為與 Linux 建議鎖略有不同,測試時需注意。
替代方案比較
| 方案 | 優點 | 缺點 |
| flock() | 無額外依賴、跨平台、簡單 | 單機、NFS 不可靠、無超時機制 |
| 資料庫行鎖 | 支援叢集、事務整合 | 需資料庫連線、效能較低 |
| Redis SETNX | 高效能、支援分散式、可設 TTL 自動釋放 | 需 Redis 服務、單點故障需 Sentinel/Cluster |
| Swoole Lock | 記憶體級、支援讀寫鎖、信號量 | 需 Swoole 擴充套件、單機 |
copyright ©
萬盛學電腦網 all rights reserved