日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

公司目前有一個需求,需要對一個日增量在20萬+數據量的數據表中的數據進行可自定義條件篩選的導出數據,該功能需要對多個部門進行開發使用,要保證功能可用的前提下,盡量優化體驗。

首先介紹一下當前可利用的資源:

1、MySQL - 一主庫雙從庫。

2、分布式服務器集群,選擇其中一臺中型機作為腳本執行載體。

3、文件系統 - 可以支持上傳大數據量文件。

4、編程語言php。

技術難點:

1、數據太大,對服務器配置要求較高,導出過程中涉及數據的處理(例如各種ID轉換名稱等操作,我們這次需求這種太多了~~非常的坑)對內存消耗很大,其次涉及到文件壓縮,因此對CPU要求較高。

2、因為是跨系統部署,如果走接口,數據量隨隨便便上百M,傳輸速度太慢(項目是對外網開放的,然后數據只允許內網訪問),那么該如何解決?

3、數據安全性較高,需要對所有導出進行記錄,那么如何保證數據安全?

| 技術方案

第一步:設計數據庫,對所有導出任務進行實時記錄,也可以采用redis,為了方便數據的持久化,我最終采用了mysql數據庫的方案。表結構具體包括:ID、用戶ID、用戶名、發起請求時間、導出具體的參數(包括各個維度的參數選擇等,具體根據自身業務而定),任務是否正在處理標識(防止任務多次被處理),導出是否成功標識(可以與前一個用一個字段區分),刪除標識等(假刪除,便于記錄用戶實際操作日志)。

第二步:前臺界面編寫,具體包括參數選擇、導出記錄列表等,作用:觸發導出任務創建,記錄于導出表中,狀態:待處理。

第三步:編寫導出腳本對任務進行監控并處理,如果有導出任務自動對其執行導出操作。

這里有一個小問題:為什么不在前臺觸發任務的時候直接執行導出,而是有單獨的腳本來執行導出呢?這就是現實業務導致的,因為我們對外開放的機器中有一些是配置很低的,為了保證導出的成功率,我們需要一臺配置較高的機器來獨立執行導出任務。

| 導出流程

具體流程參考下圖

PHP導出百萬條數據方法

 

| 代碼實現

這里主要著重介紹一下導出腳本的代碼,其他步驟的代碼根據自己的業務自行編寫就可以了。

注意:因為數據量過大~一次性導出可想而知是不合理的,所以我使用了分頁導出的形式~

首先查詢數據總條數、然后通過每頁導出的條數來計算具體導出的頁數~

# 獲取數據總條數
$dataCount = Data_ExportModel::getExportZipTotalCount($params);
$dataCount = $dataCount[0]['count_num'];
# csv
# 輸出Excel文件頭,可把user.csv換成你要的文件名
$mark = '/tmp/export';
$stepLen = 20000;//每次只從數據庫取100000條以防變量緩存太大
# 每隔$limit行,刷新一下輸出buffer,不要太大,也不要太小
$limit = 20000;
$maxFileCount = 1000000;
# buffer計數器
$cnt = 0;
$head = self::initColumnDataV2(); // 表頭部分根據自身業務自行調整
$fileNameArr = array();
$salesStatisticsData = array();
$startLimitId = 0;

首次導出的每頁條數我定的10萬條,后來發現對內存消耗過大,改成了兩萬條,這樣的導出速度會慢一點,建議五萬條比較適中一點。

for ($j = 0; $j < ceil($dataCount / $maxFileCount); $j++) {
 $startSelect = ceil($maxFileCount / $stepLen)*$j;
 $fileCsvName = $mark . '_'.$j*$maxFileCount.'_' . ($j+1)*$maxFileCount . '.csv';
 $fp = fopen($fileCsvName, 'w'); //生成臨時文件
 $fileNameArr[] = $fileCsvName;
 # 將數據通過fputcsv寫到文件句柄
 fputcsv($fp, $head);
 for ($i = 0; $i < 50; $i++) { // 單個文件支持100萬數據條數
 $startNum = $j*$maxFileCount + $i*$limit;
 if ($startNum > $dataCount) {
 break; // 跳出循環
 }
 # 查詢數據
 $dataSource = Data_ExportModel::getExportZipTotalInfo($params, $startNum, $stepLen, $startLimitId);
 $endMicroTime = microtime(true);
 printf("n[%s -> %s] Begin Time : %s, End Time : %s, Total Count : %s, CostTime: %s.n", __CLASS__, __FUNCTION__, $params['begin_date'], $params['end_date'], count($dataSource), ($endMicroTime - $startMicroTime));
 if (empty($dataSource)) {
 continue;
 }
 $endMicroTime = microtime(true);
 foreach ($dataSource as $_key => $_data) {
 $cnt++;
 if ($limit == $cnt) {
 # 刷新一下輸出buffer,防止由于數據過多造成問題
 ob_flush();
 flush();
 $cnt = 0;
 }
 # 數據處理部分,根據自身業務自行定義,注意中文轉碼
 $salesStatisticsData['name'] = iconv('utf-8', 'GB18030', $salesStatisticsData['c_name']);
 fputcsv($fp, $salesStatisticsData);
 }
 }
 fclose($fp); # 每生成一個文件關閉
}
# 進行多文件壓縮
$zip = new ZipArchive();
$number = rand(1000,9999);
$filename = $mark."_".$params['begin_date']."_".$params['end_date'] ."_".$number. ".zip";
$zip->open($filename, ZipArchive::CREATE); //打開壓縮包
foreach ($fileNameArr as $file) {
 $zip->addFile($file, basename($file)); //向壓縮包中添加文件
}
$zip->close(); //關閉壓縮包
if (!file_exists($filename)) {
 // 首次執行檢查生成的壓縮文件是否存在失敗,進行二次嘗試。。。
 $endMicroTime = microtime(true);
 # 進行二次多文件壓縮
 $number = rand(1000,9999);
 $filename = $mark."_".$params['begin_date']."_".$params['end_date'] ."_".$number. ".zip";
 if (file_exists($filename)) {
 unlink($filename);
 }
 $zip->open($filename, ZipArchive::CREATE); //打開壓縮包
 foreach ($fileNameArr as $file) {
 $zip->addFile($file, basename($file)); //向壓縮包中添加文件
 }
 $zip->close(); //關閉壓縮包
}
if (file_exists($filename)) {
 $content = file_get_contents($filename);
 // 解決讀取文件偶爾出現失敗的問題,第一讀出為空則嘗試第二次讀取
 $forNum = 0;
 while (!$content) {
 $forNum++;
 @$content = file_get_contents($filename);
 if ($forNum > 10) {
 break; // 防止出現異常情況導致死循環,最多重試10次
 }
 }
} else {
 $endMicroTime = microtime(true);
 # 刪除臨時文件,防止占用空間
 foreach ($fileNameArr as $file) {
 if (is_file($file)) {
 unlink($file);
 }
 }
 // 記錄錯誤日志并且報警
 return false;
}
# 刪除臨時文件,防止占用空間
foreach ($fileNameArr as $file) {
 if (is_file($file)) {
 unlink($file);
 }
}

最后將生成好的文件存入文件系統,上傳成功之后反轉導出狀態,前臺檢測到導出成功自動進行下載即可。

分享到:
標簽:導出 數據 PHP
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定