人力検索はてな
モバイル版を表示しています。PC版はこちら
i-mobile

PHPで、CSVファイル数が大体4万ファイルくらい
ある場合に、CSVのファイルからレコードを
取得する際、以下のソースを書いてます。

$time_dirという配列の中に、ディレクトリ名の絶対パスをもった
値が入っている。
その絶対パスのディレクトリ内にある、CSVファイルを取得して、
$data_listという配列に、格納する。
この時に、24000ファイルを読み込むときに大体、1分くらい処理が
掛かるのですが、改善方法などあるでしょうか?
$data_list = array();
foreach($time_dir as $key => $val){
if (!($dir = opendir("$val"))) {
die;
}
while ($file_name_csv = readdir($dir)) {
if (ereg('.csv', $file_name_csv)) {
$fp = fopen("$val/$file_name_csv", 'r');
$line = fgets($fp);
// 読み込んだ行を出力する
list($wirte_time,$test_id, $test_num,$test_date,$ins_date) = explode("\t",$line);
$data_list[] = array('test_id' => $test_id,'test_num' => $test_num,'test_date' => $test_date);
// ファイルをクローズする
fclose($fp);
}
}
closedir($dir);

●質問者: hopefully
●カテゴリ:就職・転職 コンピュータ
✍キーワード:AS CSV Dir FP Key
○ 状態 :終了
└ 回答数 : 4/4件

▽最新の回答へ

1 ● Mook
●23ポイント

http://q.hatena.ne.jp/1158058845

と同じ件の質問かと思いますが、こちらの方が情報が詳細なので、以降、こちらで回答いたします。


まず、PHP のように動的に情報を扱う仕組みで、40000件もの多くのファイルを扱うという方法そのものに問題があるように感じます。


データベースを利用したり、事前にファイルを一つに統合したりするということはできないのでしょうか。


4万件のファイルが動的に変化するというのであれば、データベースの利用を検討されたほうが良いと思いますが、データが変わらないというのであれば、1ファイル(あるいは意味のある分類が可能であれば、数ファイル)に統合してはどうかと思います。


OS が Linux か Windows かといった情報もあると、そのための具体的な回答もできるのですが。

◎質問者からの返答

OS が Linux です。

データベースは利用出来ません。

事前にファイルを結合って事は

tarとかで結合するんでしょうか?


2 ● Mook
●23ポイント

Linux であれば、簡単なスクリプトで結合できると思います。

情報は、ファイル名には依存していないと考えてよいのでしょうか。


tar では、csv ファイルとして機能しなくなってしまうので、コマンドプロンプトで

for file in (`ls *.csv`)

do

cat $file >> addcsv.txt

done

mv addcsv.txt data.csv

のような感じで一つのファイルになります。

シェルによって若干記述が異なるので動かない場合、お使いのシェルがわかればアドバイスもできます。

◎質問者からの返答

これをPHPのスクリプトで書くとどうなるんでしょうか?


3 ● Mook
●22ポイント

まず、処理を行う PHP とデータとしての CSV を分離して考えた方が良いと思います。


上記は、Linux 上のシェルで行う処理で、これによってdata.csv というファイルができます。これを使用して、PHP側では、

<?php

echo "<table border=\"1\">\n";

echo "<tr>\n";

echo "<td>ID</td><td>Number</td><td>Data</td>\n";

echo "</tr>\n";

$fp = fopen($time_dir."/data.csv", 'r');

while( list($id, $num, $data) = fgetcsv( $fp, 100, "," ) ) {

    echo "<tr>\n";

    echo "<td>$id</td>\n";

    echo "<td>$num</td>\n";

    echo "<td>$data</td>\n";

    echo "</tr>\n";

}

echo "</table>";

fclose($fp);

?>

のような感じでしょうか。


スクリプト中では、このファイルだけを検索することになるので、ファイル I/O の点で改善が見込めます。

これで不十分な場合は、Linux の grep コマンド等を使用して、読み込む前にデータを処理した方が良いかもしれません。

(あまりお勧めしませんが。)

◎質問者からの返答

なるほど、遅くなってしまい、申し訳ありません。

ありがとうございます。


4 ● a_b_y
●22ポイント

ディスクIOがボトルネックであるならば、1ファイルあたり約100バイト×数万ファイル程度のサイズであれば、 Linux なら tmpfs を使う手があります。

http://www.atmarkit.co.jp/flinux/rensai/linuxtips/277usetmpfs.ht... などに倣って (このページ色々と物議を醸したこともありますが今回の件には問題ないので参照します) 例えば

# mount -t tmpfs -o size=24m tmpfs /cvsdir

としてから /cvsdir に全てのファイルをコピーし、PHP からはそちらのファイルを読み込むようにする方法です。これなら(スワップアウトしない限り)ディスクIOは発生しないので入出力に関してはかなりの高速化が期待できます。もちろんこの場合にも、データを多数のファイルに分けておくより少数のファイルに統合しておく方がより高速になります。ここまでの流れで考えると、IOに関してこれ以上に劇的な高速化の方法はないと思います。一つの欠点は、今後もデータの容量が増大しメモリが足りなくなってしまうと、この方法は破綻することでしょうか。

注意点は、tmpfsについて御存知ならこれは蛇足ですが、tmpfsは揮発性なので再起動などをすると内容が消えてしまうことです。ですから、サービス起動時にHDDからtmpfsにコピーを行ない、サービス終了時にはtmpfsの内容をHDDに退避させる必要があります。

◎質問者からの返答

やはり多いファイル数を扱うには

サーバ側のコマンド等で一旦まとめたり

しないといけないんですね。

ありがとうございます。

関連質問


●質問をもっと探す●



0.人力検索はてなトップ
8.このページを友達に紹介
9.このページの先頭へ
対応機種一覧
お問い合わせ
ヘルプ/お知らせ
ログイン
無料ユーザー登録
はてなトップ