首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 並發檔案讀寫衝突解決方案:flock、臨時檔、隨機分流、佇列

     編輯:PHP 相關知識

問題場景

高併發下多進程同時讀寫同一檔案 (如日誌、計數器、快取),若無同步機制會導致資料遺失、內容損毀。

方案一:flock() + 超時重試 (適用單機、低中併發)

function write_with_lock($file, $data, $timeoutMs = 100) {
    $fp = fopen($file, 'a+');
    if (!$fp) return false;
    
    $start = microtime(true);
    while (!flock($fp, LOCK_EX)) {
        if ((microtime(true) - $start) * 1000 > $timeoutMs) {
            fclose($fp);
            return false; // 逾時
        }
        usleep(mt_rand(100, 500)); // 隨機退讓
    }
    
    fwrite($fp, $data . "\n");
    flock($fp, LOCK_UN);
    fclose($fp);
    return true;
}

注意:NFS/網路檔案系統 flock() 不可靠;Windows 下 LOCK_EX 行為與 Linux 不同。

方案二:臨時檔 + 原子性 rename (無鎖、高可靠)

function atomic_write($file, $data) {
    $tmp = $file . '.tmp.' . bin2hex(random_bytes(8));
    $fp = fopen($tmp, 'w');
    if (!$fp) return false;
    fwrite($fp, $data);
    fclose($fp);
    
    // rename 在同一檔案系統上是原子操作
    if (!rename($tmp, $file)) {
        @unlink($tmp);
        return false;
    }
    return true;
}

// 讀取時無需鎖,直接讀 (可能讀到舊版本,但不會壞檔)
$data = file_get_contents($file);

原理:寫入暫存檔 → rename() 原子覆蓋。讀者永遠看到完整舊版或完整新版,不會看到半寫入內容。

方案三:隨機分流 (極高併發寫入、日誌類)

function sharded_write($baseDir, $data, $shards = 100) {
    $shard = mt_rand(1, $shards);
    $file = "$baseDir/log_$shard.txt";
    return file_put_contents($file, $data . "\n", FILE_APPEND | LOCK_EX);
}

// 讀取/分析時合併所有分片
function merge_shards($baseDir, $shards = 100) {
    $all = '';
    for ($i=1; $i<=$shards; $i++) {
        $f = "$baseDir/log_$i.txt";
        if (is_file($f)) $all .= file_get_contents($f);
    }
    return $all;
}

優點:寫入無排隊、極高吞吐。缺點:讀取/分析需合併、即時性稍弱。

方案四:訊息佇列 (生產者/消費者模式、跨機器)

// 生產者:推入 Redis List / RabbitMQ / Kafka
$redis->rpush('log_queue', json_encode($logData));

// 消費者:單一進程序列化寫入
while (true) {
    $item = $redis->lpop('log_queue');
    if (!$item) { sleep(1); continue; }
    $data = json_decode($item, true);
    file_put_contents('/var/log/app.log', format($data) . "\n", FILE_APPEND);
}

優點:完全解耦、可水平擴展、支援重試/死信佇列/監控。缺點:架構複雜度提高、引入外部依賴。

方案選擇指南

場景推薦方案理由
單機、低併發、簡單邏輯flock()零依賴、程式碼少
單機、需資料一致性、中等併發臨時檔 + rename無鎖、ACID、效能佳
高併發寫入、日誌/統計、可延遲讀取隨機分片寫入 O(1)、無鎖競爭
分散式、多服務、需持久/重試/順序Message Queue架構級解耦、可觀測性

進階:Linux inotify + rsync 同步

若需跨伺服器即時同步檔案,可用 inotifywait 監聽變更觸發 rsync,或使用 lsyncd (Lua 腳本封裝 inotify+rsync)。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved