天天看點

PHP批量導出20萬條資料解決消耗記憶體和時間問題

在項目中使用php批量導出資料到Excel中,大量資料2w,5w,10w資料就要面臨問題

1、導出時間變得很慢,少則1分鐘,多則好幾分鐘,資料量一旦上來,還可能面臨導不出來的困窘(這種導出效率正常人都會受不了,更何況是要求苛刻的使用者)。

2、導出消耗大量的記憶體資源,即使把php記憶體使用設定為無限大,當多個使用者同時使用導出功能導出大量資料的時候,伺服器記憶體使用就會直線往上升,報警,甚至當機,嚴重影響其他業務

需要注意EXCEL單表隻能顯示104W資料

解決思路大概是:

1、從資料庫中讀取要進行資料量分批讀取,以防變量記憶體溢出

2、選擇資料儲存檔案格式是csv檔案,以友善導出之後的閱讀、導入資料庫等操作

3、以防不友善excel讀取csv檔案,需要104W之前就得把資料分割進行多個csv檔案儲存

4、多個csv檔案輸出給使用者下載下傳是不友好的,還需要把多個csv檔案進行壓縮,最後提供給一個ZIP格式的壓縮包給使用者下載下傳就好。

直接上代碼

<?php
/*
 *     
 * @param string $sql 需要導出的資料SQL
 * @param string $mark 生成檔案的名字字首
 * @param bool $is_multiple 是否要生成多個CSV檔案
 * @param int $limit 每隔$limit行,重新整理輸出buffer,以及每個CSV檔案行數限制
 *
 */
function putCsv($sql, $mark, $is_multiple=0, $limit=100000)
{
    set_time_limit(0);
    header('Content-Type: application/vnd.ms-excel;charset=utf-8');
    header('Content-Disposition: attachment;filename="' . $mark . '"');
    header('Cache-Control: max-age=0');

    // 每隔$limit行,重新整理一下輸出buffer,也可以控制多csv檔案生成的行數限制
    // buffer計數器
    $file_num = 0;  //檔案名計數器
    $row_num = 0;
    $fileNameArr = array();
    // 逐行取出資料,不浪費記憶體
    $fp = fopen($mark .'_'.$file_num .'.csv', 'w'); //生成臨時檔案
    $fileNameArr[] = $mark .'_'.$file_num .'.csv';
    fwrite($fp, chr(0xEF).chr(0xBB).chr(0xBF));//轉碼,防止亂碼
   foreach (query($sql) as $a) {
        $row_num++;
        if ($limit <= $row_num) {
            //重新整理一下輸出buffer,防止由于資料過多造成問題
            if(ob_get_level()>0)  ob_flush(); flush();
            $row_num = 0;
            $file_num++;
            if($is_multiple>0){
                fclose($fp);  //每生成一個檔案關閉
                $fp = fopen($mark .'_'.$file_num .'.csv', 'w');
                $fileNameArr[] = $mark .'_'.$file_num .'.csv';
                fwrite($fp, chr(0xEF).chr(0xBB).chr(0xBF));//轉碼,防止亂碼
            }
        }
        fputcsv($fp, $a);
    }
    fclose($fp);  //每生成一個檔案關閉
    getZip('test',$fileNameArr); //生成zip檔案
}
$sql = 'SELECT id FROM `tablename`  where id < 1200000';
$mark = 'test';//生成檔案字首
putCsv($sql,$mark);

?>
           

謝謝,ALL!