本片部落格記錄了一次實際開發中的需要使用PHPExcel導入導出大量資料(20萬)的解決過程。
複盤優化自己的項目,好處在于,一旦已找到好的方法,好的代碼,你就不可能再使用差的代碼
場景描述:開發環境:普通電腦+win+php+mysql+apache。開發一個人力資源管理系統,涉及機關,部門,人員和之間的對應關系。在本項目開發中,需要導入20萬條人員的資訊,導出100萬條使用者資訊。
Ques1:如何使用phpexcel導入20萬條使用者資料
1,優化邏輯代碼
對于20萬條資料顯然無法進行測試,我們先使用1萬條資料進行測試
先看一下縮略版的邏輯代碼
foreach(...){
$arr = db('department')->where('departCode',$strs[1])->where('unitCode',$strs[2])->find();
if(empty($arr)){
db('department')->insert([
'departCode' => $strs[0],
'name' => $strs[1],
'unitCode' => $strs[2],
]);
}else{
return $this->error("上傳失敗,已存在該部門",'department/lst');
}
}
2,減少sql的執行
第一次上傳了 7000 條資料,時間24秒左右。
分析在導入資料時,并不是直接全部導入,還需要進行一些必要的邏輯判斷,是以每一條資料都需要判斷和插入,插入肯定沒有辦法避免,可以看出判斷語句是在插入前判斷表内是否有重複的資料,避免插入重複的資料,那如果将判斷的sql去掉,能不能減少運作時間呢?
測試後結果在17秒左右。
[外鍊圖檔轉存失敗(img-1U76vGxi-1564537982061)(en-resource://database/488:1)]
證明省去了7000條sql查詢語句還是有用的,是以我們可以去掉這個查重的判斷,在最後資料出入完畢時,再對重複資料進行處理和删除。
按照這個思路,我們可以把 foreach中的插入的sql語句,放在外面,在循環中把資料拼接起來,在循環外去執行插入多條資料的一條sql。
代碼為
$data = array();
foreach(...){
$temp = [
'departCode' => $strs[0],
'name' => $strs[1],
'unitCode' => $strs[2],
];
array_push($data,$temp);
}
db('department')->insertAll($data);
來看一下運作時間竟然不到一秒。
[外鍊圖檔轉存失敗(img-DzUS3NnK-1564537982063)(en-resource://database/486:1)]
要執行的sql次數等于 = 循環内sql數 x 資料量,對于大量資料,mysql處理會很吃力。是以要避免過多sql,優化插入的sql。盡量把資料交給程式去處理,減少sql語句才是關鍵。
3,保護記憶體
那我們就用20萬條資料來測試一下結果。
(需要注意幾個點:以下幾個預設配置都會讓你在導入大量資料時出錯
- php預設上傳檔案大小限制2M,(php.ini: upload_max_filesize)
- 預設腳本使用最大記憶體限制128M,(php.ini: memory_limit)
- mysql資料庫支援最大位元組為4194304byte,(資料庫配置:max_allowed_packet)
-
excel03版(字尾.xls)以前最大行數六萬五,之後版本(.xlsx)是104萬。
是以20萬條資料,我的配置是,使用新版excel,php上傳檔案大小調高到20M,最大記憶體500M,mysql最大位元組100M)
[外鍊圖檔轉存失敗(img-t5huTAwG-1564537982064)(en-resource://database/492:1)]
雖然用了40s,但是至少程式可以運作了,如果資料量繼續增大,有可能進行運算的記憶體很有可能不夠用,是以,另一方面,應該從保護記憶體的方面思考。在程式中設定斷點檢視運作時間,phpexcel加載這20萬條資料花了15s,一行行的讀花了接近40s,等于說全部時間還是花費在phpexcel本身執行上面,不知道有什麼好的優化方法。
Ques2:如何使用phpexcel導出100萬條使用者資料
導出六萬條資料
[外鍊圖檔轉存失敗(img-Ano6yvjn-1564537982064)(en-resource://database/494:1)]
20萬條資料
發現時間超過100s無法運作
1,使用csv檔案格式導出
csv 與 excel相比,好處比較多,比如更安全,文本格式不會産生錯誤,程式設計語言處理速度更快等
[外鍊圖檔轉存失敗(img-l3rDZ3c3-1564537982066)(en-resource://database/496:1)]
20萬條資料17s完成了
2,分批生成檔案,最後下載下傳壓縮包
我們将100萬條資料分為五份,每次隻從資料庫中取出20萬條資料,然後寫入csv檔案中,最後将這五個檔案壓縮打包下載下傳
[外鍊圖檔轉存失敗(img-L5mmH0bR-1564537982066)(en-resource://database/498:1)]
100萬條資料能在15s内完成
生成單個檔案的話11s就可以完成