我有一個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函數來加載文件 –