我有一个 mysql 表,其中每条记录可以有无限的自定义字段(EAV 模型,没关系),每个字段可以有无限的选项,每个选项可以有无限的值。
现在我正在尝试构建一个导出工具,它将导出所有这些自定义字段及其值,即:每个字段的名称 => 值对。这不是重要的部分,这里只是为了强调我们正在讨论针对单个记录的大量 mysql 查询,并且导出的大小将非常大。
对于我的主表中的每一行,我必须执行大约 100 个单独的 sql 查询来获取字段、字段选项和字段选项值。这些查询非常快,因为它们都使用了正确的索引,但我们仍然在谈论针对单个记录的 100 个查询,我希望我的主表中只有大约 50k 条记录才能开始。
现在,我要做的是:
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调用)。
这是上述的一个很好的选择吗?
请注意,我的目标是限制内存使用量,这就是为什么我认为第二种方法对我有更多帮助的原因。
请让我知道你在想什么。
谢谢。