我有一个csv文件,其范围可以从50k到超过10万行数据。
我目前正在使用Laravel w / Laravel Forge,MySQL和Maatwebsite Laravel Excel软件包。
这将由最终用户而不是我自己使用,因此我在刀片视图上创建了一个简单的表单,如下所示:
{!! Form::open(
array(
'route' => 'import.store',
'class' => 'form',
'id' => 'upload',
'novalidate' => 'novalidate',
'files' => true)) !!}
<div class="form-group">
<h3>CSV Product Import</h3>
{!! Form::file('upload_file', null, array('class' => 'file')) !!}
</div>
<div class="form-group">
{!! Form::submit('Upload Products', array('class' => 'btn btn-success')) !!}
</div>
{!! Form::close() !!}
然后,这会将文件成功存储在服务器上,现在我可以使用诸如foreach循环之类的方法遍历结果。
现在,这是我按时间顺序所面临的问题并修复/尝试:(1万行测试CSV文件)
如您所见,结果微不足道,接近50k,我什至不能接近10k。
我已阅读并查看了可能的建议,例如:
加载本地infile文件可能无法正常工作,因为我的标题列可能会因文件而异,这就是为什么我有逻辑来处理/遍历它们的原因。
导入前分割文件可以在10k以下,但可以在50k以上吗?那将是非常不切实际的。
存储在服务器上,然后让服务器拆分它并单独运行它们,而不会打扰最终用户?可能但甚至不确定如何在PHP中实现此目的,而只是简要了解一下。
还需要注意的是,我的队列工作程序设置为在10000秒内超时,这也是非常不切实际和不切实际的做法,但是似乎这是在内存受到冲击之前它将保持运行的唯一方法。
现在,我可以让步,只需将内存升级到1gb,但我觉得充其量最多可能会使我跳到20k行,然后再次失败。需要一些东西来快速有效地处理所有这些行。
最后,这是我的表结构的概览:
Inventory
+----+------------+-------------+-------+---------+
| id | profile_id | category_id | sku | title |
+----+------------+-------------+-------+---------+
| 1 | 50 | 51234 | mysku | mytitle |
+----+------------+-------------+-------+---------+
Profile
+----+---------------+
| id | name |
+----+---------------+
| 50 | myprofilename |
+----+---------------+
Category
+----+------------+--------+
| id | categoryId | name |
+----+------------+--------+
| 1 | 51234 | brakes |
+----+------------+--------+
Specifics
+----+---------------------+------------+-------+
| id | specificsCategoryId | categoryId | name |
+----+---------------------+------------+-------+
| 1 | 20 | 57357 | make |
| 2 | 20 | 57357 | model |
| 3 | 20 | 57357 | year |
+----+---------------------+------------+-------+
SpecificsValues
+----+-------------+-------+--------+
| id | inventoryId | name | value |
+----+-------------+-------+--------+
| 1 | 1 | make | honda |
| 2 | 1 | model | accord |
| 3 | 1 | year | 1998 |
+----+-------------+-------+--------+
Full CSV Sample
+----+------------+-------------+-------+---------+-------+--------+------+
| id | profile_id | category_id | sku | title | make | model | year |
+----+------------+-------------+-------+---------+-------+--------+------+
| 1 | 50 | 51234 | mysku | mytitle | honda | accord | 1998 |
+----+------------+-------------+-------+---------+-------+--------+------+
因此,我的逻辑工作流程的快速遍历将尽可能简单:
我希望有人可以与我分享一些我应该如何解决这一问题的见解,同时要牢记使用Laravel,而且这不是一个简单的上传,我需要处理并将其放入每行不同的相关表中,否则我会一次将所有数据加载到文件中。
谢谢!
您似乎已经弄清楚了解释CSV行并将其转换为在数据库中插入查询的逻辑,因此我将重点介绍内存耗尽问题。
当使用PHP处理大型文件时,将整个文件加载到内存的任何方法都将失败,变得难以忍受的缓慢或需要比Droplet更多的RAM。
所以我的建议是:
使用逐行读取文件 fgetcsv
$handle = fopen('file.csv', 'r');
if ($handle) {
while ($line = fgetcsv($handle)) {
// Process this line and save to database
}
}
这样,一次只将一行加载到内存中。然后,您可以对其进行处理,保存到数据库,然后用下一个覆盖。
保留单独的文件句柄以进行日志记录
您的服务器内存不足,因此将错误记录到阵列可能不是一个好主意,因为所有错误都将保留在其中。如果您的csv包含大量带有空skus和类别ID的条目,则可能会成为问题。
Laravel出来与箱体的独白,你可以尝试,以使其适应您的需求。但是,如果最终还是使用了过多资源或无法满足您的需求,则可以采用更简单的方法。
$log = fopen('log.txt', 'w');
if (some_condition) {
fwrite($log, $text . PHP_EOL);
}
然后,在脚本末尾,您可以将日志文件存储在任意位置。
禁用Laravel的查询日志
Laravel将所有查询存储在内存中,这可能对您的应用程序造成问题。幸运的是,您可以使用disableQueryLog方法释放一些宝贵的RAM。
DB::connection()->disableQueryLog();
必要时使用原始查询
我认为如果遵循这些技巧,您不太可能会再次耗尽内存,但是您总是可以牺牲Laravel的一些便利来提取最后的性能下降。
如果您了解使用SQL的方式,则可以对数据库执行原始查询。
编辑:
至于超时问题,无论如何,您都应该按照注释中的建议将此代码作为排队的任务运行。插入这么多行将花费一些时间(特别是如果您有很多索引),并且用户不应长时间盯着无响应的页面。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句