6

各レコードに無制限のカスタム フィールド (EAV モデル、これは関係ありません) を含めることができ、各フィールドに無制限のオプションを含めることができ、各オプションに無制限の値を含めることができる mysql テーブルがあります。
現在、これらすべてのカスタム フィールドを値とともにエクスポートするエクスポート ツールを構築しようとしています。つまり、各フィールドの名前 => 値のペアです。それは重要な部分ではありません。単一のレコードに対する多数の mysql クエリについて話していること、およびエクスポートのサイズがかなり大きくなることを強調するためにここにいます。

メイン テーブルの行ごとに、約 100 の個別の SQL クエリを実行して、フィールド、フィールド オプション、およびフィールド オプションの値を取得する必要があります。これらのクエリはすべて適切なインデックスを使用しているため非常に高速ですが、それでも 1 つのレコードに対して 100 件のクエリについて話しているため、最初はメイン テーブルに約 50,000 件のレコードがあると予想されます。

今、私がしていることは次のとおりです。

set_time_limit(0);
ini_set('memory_limit', '1G');
ini_set("auto_detect_line_endings", true);

$count = $export->count();
$date = date('Y-m-d-H-i-s');
$fileName = CHtml::encode($export->name) .'-'. $date . '.csv';

$processAtOnce = 100;
$rounds = round($count / $processAtOnce);

header("Content-disposition: attachment; filename={$fileName}");
header("Content-Type: text/csv");

$headerSet = false;
for ($i = 0; $i < $rounds; ++$i) {

    $limit = $processAtOnce;
    $offset = $i * $processAtOnce;
    $rows = $export->find($limit, $offset);

    if (empty($rows)) {
        continue;
    }

    $outStream = fopen('php://output', 'w');

    if (!$headerSet) {
        fputcsv($outStream, array_keys($rows[0]), ',', '"');    
        $headerSet = true;
    }

    foreach ($rows as $row) {
        fputcsv($outStream, array_values($row), ',', '"');
    }

    echo fgets($outStream);

    fclose($outStream);
}

基本的に、私はすべてのレコードを数え、エクスポートのためにそれらを「ページ分割」し、ページを実行して、一度に多くのSQL結果をロードしないようにします。
これが有効なアプローチであるかどうか疑問に思っていますか?何かご意見は?

私の代替手段は、すべてのレコードを数え、それらを「ページ」に分割し、各ページに対して ajax リクエスト (前のリクエストが正常に行われた後に呼び出される再帰関数) を実行することです。ajax リクエストを実行するときは、おそらく 1k レコードを一度に処理し (これらの 1k も上記の例のように分割され、内部で 10 回実行されて 100 の結果が得られます)、それらを一時ディレクトリ (part-1.csv など) に書き込みます。 part-2.csv) を作成し、最後にすべてのレコードが処理されたら、すべての csv パーツを含むフォルダーからアーカイブを作成し、ブラウザーに強制的にダウンロードさせてからサーバーから削除します (window.location.href 内から最後の ajax 呼び出し)。
これは上記の良い代替手段ですか?

私の目標はメモリ使用量を制限することです。そのため、2番目のアプローチがより役立つと思います。

ご意見をお聞かせください。
ありがとう。

4

3 に答える 3

7

私の最終的なアプローチは 2 番目のアプローチです。多くのテストを行った後、私の場合、エクスポート全体を完了する時間が長くても、2 番目のアプローチの方がメモリ使用量の点で優れていると結論付けました。 GUI は、エクスポートに関するライブ統計で更新され、エクスポートが完了するのを待っている間、全体的に優れたユーザー エクスペリエンスを提供します。

これらは私が行った手順です:
1) ページをロードし、サーバーに最初の ajax リクエストを行います。
2) サーバーは、一度に 100 レコードのバッチで最初の 1000 レコードを読み取り、mysql から一度に多くの結果が返されるのを回避します。
3) 結果は part-x.csv としてファイルに書き込まれます。ここで、x は ajax によって送信された要求番号です。
4) ファイルに追加するレコードがなくなると、最後の ajax 呼び出しでアーカイブが作成され、part-x.csv ファイルを含むフォルダーが削除されます。次に、サーバーは「download」という名前のjsonパラメーターを返します。これには、PHPを介してファイルをダウンロードするためのURLが含まれます(fopen + fread + flush + fclose、その後にアーカイブファイルのリンクを解除します)
5)「download」パラメーターを使用して、ブラウザーa を実行しwindow.location.href = json.download、ファイルを強制的にダウンロードします。

私は知っています、それはこのようなより多くの作業ですが、私が言ったように、最終結果は私が最初にした方法で一度にすべてをロードするよりも優れているようです.

于 2013-09-29T21:37:00.017 に答える