萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 網路程式設計 >> PHP 程式設計

PHP 文件鎖 flock() 實現併發寫入同步:原理、範例與最佳實踐

為什麼需要文件鎖?

多進程/多執行緒同時 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 建議鎖略有不同,測試時需注意。

替代方案比較

copyright © 萬盛學電腦網 all rights reserved
方案優點缺點
flock()無額外依賴、跨平台、簡單單機、NFS 不可靠、無超時機制
資料庫行鎖支援叢集、事務整合需資料庫連線、效能較低
Redis SETNX高效能、支援分散式、可設 TTL 自動釋放需 Redis 服務、單點故障需 Sentinel/Cluster
Swoole Lock記憶體級、支援讀寫鎖、信號量需 Swoole 擴充套件、單機