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

PHP 讀取大文件(GB 級)高效方案對比:fseek、tail、stream_get_line 實測

     編輯:PHP 相關知識

PHP 讀取超大文件(800MB、500萬行)實戰:四種方案效能深度對比

讀取 GB 級日誌文件尾部幾行是經典場景。常用 file()、file_get_contents() 會撐爆記憶體(預設 memory_limit=16M)。本文實測四種方案,附執行耗時數據,助你選型。

測試環境

  • 文件:access.log(800MB,約 500 萬行)
  • 需求:返回最後 10 行
  • 機器:2GB 記憶體

方案對比總覽

方案核心思路耗時評價
1. file() 全讀入一次性載入記憶體116.96 秒機器假死 20 分鐘,**嚴禁生產使用**
2. shell_exec tail調用系統 tail -n 100.0034 秒最快、最簡單,依賴 Linux 環境、escapeshellarg 防注入
3. fseek 逐字元反向讀從 EOF 向前逐字元找換行符0.0095 秒純 PHP、跨平台,邏輯簡單但系統調用多
4. fseek 分塊反向讀 (4KB)每次讀 4KB 塊,統計換行符數0.0009 秒**推薦純 PHP 方案**,I/O 最少,效能極佳
5. fseek 指數退避法指數增長跳躍讀取,配合 fgets0.0003 秒最快純 PHP 算法,代碼較複雜
6. stream_get_line流式逐行讀取~20 秒 (50萬行)適合順序讀取大文件,非尾部查詢最優

核心代碼實現

方案 2:系統 tail 命令(最推薦 Linux 環境)

<?php
$file = 'access.log';
$file = escapeshellarg($file); // 防命令注入
$line = `tail -n 1 $file`; // 反引號執行命令
echo $line;

方案 4:分塊反向讀(通用純 PHP,推薦)

<?php
$fp = fopen($file, 'r');
$num = 10;
$chunk = 4096;
$fs = sprintf('%u', filesize($file));
$max = ($fs == PHP_INT_MAX) ? PHP_INT_MAX : filesize($file);

$readData = '';
for ($len = 0; $len < $max; $len += $chunk) {
    $seekSize = ($max - $len > $chunk) ? $chunk : $max - $len;
    fseek($fp, ($len + $seekSize) * -1, SEEK_END);
    $readData = fread($fp, $seekSize) . $readData; // 前置拼接
    if (substr_count($readData, "\n") >= $num + 1) {
        preg_match("!(.*?\n){" . $num . "}$!", $readData, $match);
        $data = $match[0];
        break;
    }
}
fclose($fp);
echo $data;

方案 5:指數退避法(極致效能)

<?php
function tail($fp, $n, $base = 5) {
    assert($n > 0);
    $pos = $n + 1;
    $lines = [];
    while (count($lines) <= $n) {
        try {
            fseek($fp, -$pos, SEEK_END);
        } catch (Exception $e) {
            fseek($fp, 0);
            break;
        }
        $pos *= $base;
        while (!feof($fp)) {
            array_unshift($lines, fgets($fp));
        }
    }
    return array_slice($lines, 0, $n);
}
var_dump(tail(fopen('access.log', 'r+'), 10));

方案 6:stream_get_line(順序讀取大文件首選)

<?php
$fp = fopen('./iis.log', 'r');
$logArray = [];
while (!feof($fp)) {
    $logArray[] = stream_get_line($fp, 65535, "\n");
}
// 處理 $logArray...

選型建議

  • Linux 環境、只需尾部幾行:直接 tail,最簡潔高效。
  • 跨平台、純 PHP、尾部查詢:方案 4(分塊反向讀),平衡效能與代碼可讀性。
  • 極致效能、願意維護複雜邏輯:方案 5(指數退避)。
  • 需順序處理全文件(如導入、分析):stream_get_line 或 fgets 逐行流式處理,嚴禁一次性載入。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved