【PHP】JSONやCSVファイルを10万件単位で高速処理するためのストリーム活用術

【PHP】JSONやCSVファイルを10万件単位で高速処理するためのストリーム活用術 PHP

大量データを扱うバッチ処理やAPI連携では、メモリの消費を抑えつつ、高速かつ安定してファイルを処理することが求められます。特に10万件を超えるようなCSVやJSONファイルを読み込む際には、file_get_contents()json_decode()などの一括読み込み処理では限界があります。

この記事では、PHPでCSV・JSONファイルをストリーム(逐次処理)で扱う方法とその利点、実装例までを詳しく解説します。

一括処理の限界とストリーム処理の必要性

以下のような処理は、小さなデータでは問題ありませんが、大規模データには不向きです。

// 非推奨:一括で読み込む
$data = file_get_contents('data.json');
$array = json_decode($data, true);

一括処理では、メモリ上に全データを展開するため、大量データではPHPのメモリ上限に達しやすく、処理が中断される原因となります。

そのため、1行ずつ、または1要素ずつ処理する「ストリーム型の処理」が必須です。

CSVファイルをストリームで読み込む方法

fopen()fgetcsv()を使うことで、CSVを1行ずつ処理できます。

$handle = fopen('large-data.csv', 'r');

if ($handle !== false) {
  while (($row = fgetcsv($handle)) !== false) {
    // 1行ごとに処理(例:データベース登録など)
  }
  fclose($handle);
}

ポイント

  • メモリ使用量は一定で推移するため、ファイルサイズに関係なく安定して処理できる
  • ループ内で必要な処理だけを行うことで、高速なスクリプトを構築可能

JSONファイルのストリーム処理はやや工夫が必要

標準のPHPには「JSONのストリーム処理機能」はありません。大規模JSON(例:連想配列の配列)の場合は、json_decode()を避け、自作のイテレータ処理を行う必要があります。

例:改行区切りJSON(NDJSON)を1行ずつ処理

$handle = fopen('large-data.ndjson', 'r');

if ($handle !== false) {
  while (($line = fgets($handle)) !== false) {
    $item = json_decode($line, true);
    if (json_last_error() === JSON_ERROR_NONE) {
      // 各オブジェクトに対して処理を実行
    }
  }
  fclose($handle);
}

NDJSON形式とは?

  • 各行が1つのJSONオブジェクトになっている形式
  • データストリームに適しており、APIやバッチ処理でも採用例多数

JSONファイルのストリーム処理に使えるライブラリ

JSONを本格的にストリーム処理したい場合、外部ライブラリの活用も選択肢になります。

例:pcrov/jsonreader(SAX型JSONパーサ)

composer require pcrov/jsonreader
use pcrov\JsonReader\JsonReader;

$reader = new JsonReader();
$reader->open('large-data.json');

while ($reader->read()) {
  if ($reader->type() === JsonReader::OBJECT) {
    // 必要なノードに達したら処理を実行
  }
}

XMLでのSAXパーサのように逐次的にノードを読み進める方式で、大容量のJSONでも安定した処理が可能になります。

ファイル書き込みもストリームで制御可能

読み込みだけでなく、出力もストリームを活用することでメモリ消費を抑えることができます。

$handle = fopen('output.csv', 'w');

foreach ($data as $row) {
  fputcsv($handle, $row);
}

fclose($handle);

大量データのCSV出力を必要とする管理画面機能やレポート出力などでも役立ちます。

メモリ使用量を比較してみる

echo "開始時のメモリ: " . memory_get_usage() . " バイト\n";

// 一括処理 → 高負荷
//$json = file_get_contents('large.json');
//$data = json_decode($json, true);

// ストリーム処理
$handle = fopen('large-data.ndjson', 'r');
while (fgets($handle)) {}
fclose($handle);

echo "処理後のメモリ: " . memory_get_usage() . " バイト\n";

数MB〜GB単位のファイルでは、ストリーム処理に切り替えるだけで数十MB以上のメモリ削減が可能です。

まとめ

10万件以上のCSV・JSONを扱う場合、ストリーム処理は必須技術です。

  • CSVはfgetcsv()で逐次処理可能
  • JSONはNDJSON形式やライブラリ活用が有効
  • 書き込みもfputcsv()などで安定処理
  • メモリ削減に加え、処理中断リスクも低下

PHPで大規模データを安全・高速に処理したい場合は、「1件ずつ処理する」設計をベースに組み立てることが最も効果的なアプローチです。