大量データを扱うバッチ処理や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件ずつ処理する」設計をベースに組み立てることが最も効果的なアプローチです。