我有一个csv文件,其范围可以从50k到超过100k行数据。使用Laravel在MySQL中导入大型CSV文件
我目前使用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循环之类的东西遍历结果。
现在,这里是我面临的时间顺序和修复/企图的问题: (10K行测试CSV文件)
- [问题] PHP超时。
- [remedy]将其更改为通过作业命令异步运行。
- [结果]进口多达1500行。
- [问题]服务器内存不足。
- [补救]增加了1GB的交换驱动器。
- [结果]最多可导入3000行。
- [问题]服务器内存不足。
- [补救]打开每个块的250行分块结果。
- [结果]最多可导入5000行。
- [问题]服务器内存不足。
- [修正]删除了一些转置/连接表逻辑。
- [结果]进口多达7000行。
正如你所看到的结果是边际和远不及50k,我几乎可以使它接近10k。
我读过了,看着可行的建议,如:
- 使用原始查询运行LOAD DATA LOCAL INFILE。
- 导入前分割文件。
- 在服务器上存储,然后将服务器分割成文件并使用cron处理它们。
- 作为最后的手段将我的512mb DO溶滴升级到1GB。
与LOAD DATA LOCAL 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 |
+----+------------+-------------+-------+---------+-------+--------+------+
所以我的逻辑流程尽可能简单的快速运行,通过将是:
- 加载文件到Maatwebsite/Laravel -Excel并通过分块循环
- 检查迭代如果CATEGORY_ID和SKU是空否则忽略并记录错误到一个数组。
- 查找category_id并从它使用的所有相关表中拉出所有相关的列字段,然后如果没有null插入数据库。
- 使用文件中可用字段的更多逻辑来生成自定义标题。
- 冲洗并重复。
- 最后将错误数组导出到文件中,并将其记录到数据库中以供下载,以便在最后查看错误。
我希望有人能和我一起上,我应该如何解决这个同时牢记使用Laravel的一些可能的想法分享一些见解,也认为它不是一个简单的上传我需要处理并投入不同的相关表每行其他我会加载数据infile它一次。
谢谢!
所有的csv文件都被插入到同一个表中吗?如果是这种情况,我不明白为什么使用'load data local infile'会是一个问题 - 有些列只是'NULL'。您可以使用Python(通过'exec()')通过PHP子进程执行,以便在上载到服务器之后但在将其插入表之前根据需要解析文件。 – Terry
@Terry它只是一个CSV文件,但如上所述插入到多个表中,为什么我无法轻松使用本地infile的加载数据。此外,每个文件的数据更改取决于涉及哪些categoryid,这些列将具有不同的列。也因为这个变量,现在很难指定每个字段的数据类型。 – dmotors
如果它只是一个CSV文件,然后使用Maatwebsite Laravel Excel包和PHPExcel是矫枉过正,虽然Maatwebsite Laravel Excel包(我相信)提供访问PHPExcel chunking函数来加载文件 –