php大數(shù)據(jù)導出

概述:

最近公司項目要求把數(shù)據(jù)除了頁面輸出也希望有導出功能永乌,雖然之前也做過幾個導出功能吨述,但這次數(shù)據(jù)量相對比較大,差不多一天數(shù)據(jù)就20W條歧寺,要求導7天或者30天燥狰,那么數(shù)據(jù)量就輕松破百萬了甚至破千萬,因此開發(fā)的過程中發(fā)現(xiàn)了一些大數(shù)據(jù)導出的坑斜筐,在此跟大家分享一下龙致,互相學習。

準備:

1顷链、PHP設置坑:

set_time_limit – 設置腳本最大執(zhí)行時間:

此配置一般PHP默認是30秒净当,如果你是數(shù)據(jù)小的,可能就不會發(fā)現(xiàn)有該設置問題蕴潦,但如果你數(shù)據(jù)達到了百萬級導出,往往30秒是不夠的俘闯,因此你需要在你的腳本中添加 set_time_limit(0)潭苞,讓該腳本沒有執(zhí)行時間現(xiàn)在

memory_limit – PHP的內(nèi)存限定:

此配置一般php默認是128M,如果之前做過小數(shù)據(jù)的朋友可能也會動過這個配置就能解決許多問題,或許有人想真朗,你大數(shù)據(jù)也把這個調(diào)大不就行了嗎此疹?那么真的是too young too native了,你本地能設置1G或者無限制或許真的沒問題,但是正式場蝗碎,你這么搞遲早會出事的湖笨,一個PHP程序占那么大的內(nèi)存的空間,如果你叫你公司運維幫忙調(diào)一下配置蹦骑,估計運維一定很不情愿慈省,服務器硬件這么搞也是太奢侈了。所以說眠菇,我們要盡量避免調(diào)大該設置边败。

2、excel坑:

既然是導出數(shù)據(jù)捎废,大伙們當然馬上想到了excel格式了笑窜,多方便查看數(shù)據(jù)呀,然而萬萬沒想到excel也是有脾氣的呀登疗!

表數(shù)據(jù)限制:
1排截、Excel 2003及以下的版本。一張表最大支持65536行數(shù)據(jù)辐益,256列断傲。
2、Excel 2007-2010版本荷腊。一張表最大支持1048576行艳悔,16384列。

也就是說你想幾百萬條輕輕松松一次性導入一張EXCEL表是不行的女仰,你起碼需要進行數(shù)據(jù)分割猜年,保證數(shù)據(jù)不能超過104W一張表。

PHPexcel內(nèi)存溢出:
既然數(shù)據(jù)限制在104W疾忍,那么數(shù)據(jù)分割就數(shù)據(jù)分割唄乔外,于是你嘗試50W一次導入表,然而PHPexcel內(nèi)部有函數(shù)報內(nèi)存溢出錯誤一罩,然后你就不斷的調(diào)小數(shù)據(jù)量杨幼,直到5W一次導入你都會發(fā)現(xiàn)有內(nèi)存溢出錯誤。這是為什么呢聂渊,雖然你分割數(shù)據(jù)來導入多個數(shù)據(jù)表差购,但是最后PHPexcel內(nèi)部還是一次性把所有表數(shù)據(jù)放進一個變量中來創(chuàng)建文件……額,這幾百萬數(shù)據(jù)一個變量存儲汉嗽,你想內(nèi)存不溢出欲逃,還真有點困難。
(后來看了一些文章發(fā)現(xiàn)PHPExcel也有解決方案饼暑,PHPExcel_Settings::setCacheStorageMethod方法更改緩沖方式來減小內(nèi)存的使用)

3稳析、csv坑:

EXCEL這么麻煩洗做,我不用還不行嗎?我用csv文件儲存彰居,既不限制數(shù)量诚纸,還能直接用EXCEL來查看,又能以后把文件導入數(shù)據(jù)庫陈惰,一舉幾得豈不是美哉畦徘?咦,少俠好想法奴潘!但是CSV也有坑哦旧烧!

輸出buffer過多:
當你用PHP原生函數(shù)putcsv()其實就使用到了輸出緩存buffer,如果你把幾百萬的數(shù)據(jù)一直用這個函數(shù)輸出画髓,會導致輸出緩存太大而報錯的掘剪,因此我們每隔一定量的時候,必須進行將輸出緩存中的內(nèi)容取出來奈虾,設置為等待輸出狀態(tài)夺谁。具體操作是:

1、ob_flush();
2肉微、flush();

具體說明介紹:PHP flush()與ob_flush()的區(qū)別詳解

EXCEL查看CSV文件數(shù)量限制:
大多數(shù)人看csv文件都是直接用EXCEL打開的匾鸥。額,這不就是回到EXCEL坑中了嗎碉纳?EXCEL有數(shù)據(jù)顯示限制呀勿负,你幾百萬數(shù)據(jù)只給你看104W而已。什么劳曹?你不管奴愉?那是他們打開方式不對而已?不好不好铁孵,我們解決也不難呀锭硼,我們也把數(shù)據(jù)分割一下就好了,再分開csv文件保存蜕劝,反正你不分割數(shù)據(jù)變量也會內(nèi)存溢出檀头。

4、總結(jié)做法

分析完上面那些坑岖沛,那么我們的解決方案來了暑始,假設數(shù)據(jù)量是幾百萬。

1婴削、那么我們要從數(shù)據(jù)庫中讀取要進行數(shù)據(jù)量分批讀取蒋荚,以防變量內(nèi)存溢出,

2馆蠕、我們選擇數(shù)據(jù)保存文件格式是csv文件期升,以方便導出之后的閱讀、導入數(shù)據(jù)庫等操作互躬。

3播赁、以防不方便excel讀取csv文件,我們需要104W之前就得把數(shù)據(jù)分割進行多個csv文件保存

4吼渡、多個csv文件輸出給用戶下載是不友好的容为,我們還需要把多個csv文件進行壓縮,最后提供給一個ZIP格式的壓縮包給用戶下載就好寺酪。

代碼:
//導出說明:因為EXCEL單表只能顯示104W數(shù)據(jù)坎背,同時使用PHPEXCEL容易因為數(shù)據(jù)量太大而導致占用內(nèi)存過大,
//因此寄雀,數(shù)據(jù)的輸出用csv文件的格式輸出得滤,但是csv文件用EXCEL軟件讀取同樣會存在只能顯示104W的情況,所以將數(shù)據(jù)分割保存在多個csv文件中盒犹,并且最后壓縮成zip文件提供下載

function putCsv(array $head, $data, $mark = 'attack_ip_info', $fileName = "test.csv")
{
  set_time*limit(0);
  $sqlCount = $data->count();
  // 輸出Excel文件頭懂更,可把user.csv換成你要的文件名
  header('Content-Type: application/vnd.ms-excel;charset=utf-8');
  header('Content-Disposition: attachment;filename="' . $fileName . '"');
  header('Cache-Control: max-age=0');
  $sqlLimit = 100000;//每次只從數(shù)據(jù)庫取100000條以防變量緩存太大
  // 每隔$limit行,刷新一下輸出buffer急膀,不要太大沮协,也不要太小
  $limit = 100000;
  // buffer計數(shù)器
  $cnt = 0;
  $fileNameArr = array();
  // 逐行取出數(shù)據(jù),不浪費內(nèi)存
  for ($i = 0; $i < ceil($sqlCount / $sqlLimit); $i++) {
      $fp = fopen($mark . '*' . $i . '.csv', 'w'); //生成臨時文件
      // chmod('attack_ip*info*' . $i . '.csv',777);//修改可執(zhí)行權(quán)限
      $fileNameArr[] = $mark . '_' . $i . '.csv';
      // 將數(shù)據(jù)通過fputcsv寫到文件句柄
      fputcsv($fp, $head);
      $dataArr = $data->offset($i * $sqlLimit)->limit($sqlLimit)->get()->toArray();
      foreach ($dataArr as $a) {
            $cnt++;
            if ($limit == $cnt) {
            //刷新一下輸出buffer卓嫂,防止由于數(shù)據(jù)過多造成問題
            ob_flush();
            flush();
            $cnt = 0;
            }
            fputcsv($fp, $a);
      }
      fclose($fp); //每生成一個文件關(guān)閉
  }
  //進行多個文件壓縮
  $zip = new ZipArchive();
  $filename = $mark . ".zip";
  $zip->open($filename, ZipArchive::CREATE); //打開壓縮包
  foreach ($fileNameArr as $file) {
      $zip->addFile($file, basename($file)); //向壓縮包中添加文件
  }
  $zip->close(); //關(guān)閉壓縮包
  foreach ($fileNameArr as $file) {
      unlink($file); //刪除csv臨時文件
  }
  //輸出壓縮文件提供下載
  header("Cache-Control: max-age=0");
  header("Content-Description: File Transfer");
  header('Content-disposition: attachment; filename=' . basename($filename)); // 文件名
  header("Content-Type: application/zip"); // zip格式的
  header("Content-Transfer-Encoding: binary"); //
  header('Content-Length: ' . filesize($filename)); //
  @readfile($filename);//輸出文件;
  unlink($filename); //刪除壓縮包臨時文件
}

總結(jié):
其實上面代碼還是有優(yōu)化的空間的慷暂,比如說用異常捕捉,以防因為某些錯誤而導致生成了一些臨時文件又沒有正常刪除晨雳,還有PHPexcel的緩存設置也許能解決內(nèi)存溢出問題行瑞,可以生成一個EXCEL文件多個工作表的形式,這樣對于文件閱讀者來說更友好悍募。

原文來源:https://blog.csdn.net/tim_phper/article/details/77581071

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蘑辑,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子坠宴,更是在濱河造成了極大的恐慌洋魂,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件喜鼓,死亡現(xiàn)場離奇詭異副砍,居然都是意外死亡,警方通過查閱死者的電腦和手機庄岖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門豁翎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人隅忿,你說我怎么就攤上這事心剥“钭穑” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵优烧,是天一觀的道長蝉揍。 經(jīng)常有香客問我,道長畦娄,這世上最難降的妖魔是什么又沾? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮熙卡,結(jié)果婚禮上杖刷,老公的妹妹穿的比我還像新娘。我一直安慰自己驳癌,他們只是感情好滑燃,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著喂柒,像睡著了一般不瓶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上灾杰,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天蚊丐,我揣著相機與錄音,去河邊找鬼艳吠。 笑死麦备,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的昭娩。 我是一名探鬼主播凛篙,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼栏渺!你這毒婦竟也來了呛梆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤磕诊,失蹤者是張志新(化名)和其女友劉穎填物,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體霎终,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡滞磺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了莱褒。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片击困。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖广凸,靈堂內(nèi)的尸體忽然破棺而出阅茶,到底是詐尸還是另有隱情蛛枚,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布脸哀,位于F島的核電站坤候,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏企蹭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一智末、第九天 我趴在偏房一處隱蔽的房頂上張望谅摄。 院中可真熱鬧,春花似錦系馆、人聲如沸送漠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽闽寡。三九已至,卻和暖如春尼酿,著一層夾襖步出監(jiān)牢的瞬間爷狈,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工裳擎, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留涎永,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓鹿响,卻偏偏與公主長得像羡微,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子惶我,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容