Java調用SqlLoader將大文本導入數據庫


Java調用SqlLoader將大文本導入數據庫

業務場景:將一千萬條數據,大約500M的文本文檔的數據導入到數據庫

  分析:通過Java的IO流解析txt文本文檔,拼接動態sql實現insert入庫,可以實現,缺點如下

第一:IO流解析大文本文件對機器性能要求較高,測試大約消耗2G左右的內存

第二:拼接sql語句insert一千萬條數據大約需要2小時時間,長時間insert會鎖表,如果是核心業務表,例如訂單表,會造成大量用戶無法下單,影響數據庫的性能

第三:這種操作可擴展性不強,每次只能針對指定的表,指定的列操作

針對以上缺點,現在通過接口調用數據庫系統命令實現,通過可視化界面,選擇要導入的表,要導入那些字段,上傳指定的txt文本,會自動生成對應的模板文件,實現大批量數據高效率的導入到數據庫,通過可配置化即可實現,相對前一種思路擴展性較強,

具體接口如下

 1 package com.sun.sqlloader.api;
 2 /**
 3  * SqlLoader接口
 4  * @ClassName: ISqlLoader  
 5  * @author sunt  
 6  * @date 2017年11月15日 
 7  * @version V1.0
 8  */
 9 public interface ISqlLoader {
10 
11     /**
12      * 自動生成控制文件
13      * @Title: ctlFileWriter 
14      * @author sunt  
15      * @date 2017年11月15日
16      * @param fileRoute 數據文件地址路徑(文件所在磁盤目錄)
17      * @param fileName 數據文件名
18      * @param tableName 表名
19      * @param fieldName 要寫入表的字段
20      * @param ctlfileName 控制文件名
21      * @return void
22      */
23     void ctlFileWriter(String fileRoute,String fileName,String tableName,String fieldName,String ctlfileName);
24     
25     /**
26      * 執行系統dos命令
27      * @Title: Executive 
28      * @author sunt  
29      * @date 2017年11月15日
30      * @param user 數據庫的用戶名
31      * @param pwd 數據庫的密碼
32      * @param database 連接數據庫的地址
33      * @param fileRoute 文件路徑
34      * @param ctlfileName 控制文件名
35      * @param logfileName 日志文件名
36      * @return void
37      */
38     void Executive(String user,String pwd,String database,String fileRoute,String ctlfileName,String logfileName);
39 }
 1 package com.sun.sqlloader.api.impl;
 2 
 3 
 4 import java.io.BufferedReader;
 5 import java.io.FileWriter;
 6 import java.io.IOException;
 7 import java.io.InputStream;
 8 import java.io.InputStreamReader;
 9 import java.nio.charset.Charset;
10 import java.util.Date;
11 
12 import org.apache.log4j.Logger;
13 import org.springframework.stereotype.Service;
14 
15 import com.sun.sqlloader.api.ISqlLoader;
16 /**
17  * SqlLoader接口實現
18  * @ClassName: SqlLoaderImpl  
19  * @author sunt  
20  * @date 2017年11月15日 
21  * @version V1.0
22  */
23 @Service
24 public class SqlLoaderImpl implements ISqlLoader{
25 
26     private Logger logger = Logger.getLogger(SqlLoaderImpl.class);
27     
28     @Override
29     public void ctlFileWriter(String fileRoute, String fileName, String tableName, String fieldName,String ctlfileName) {
30         FileWriter fw = null;
31         String strctl = "OPTIONS (skip=0)" + // 0是從第一行開始  1是 從第二行
32         " LOAD DATA CHARACTERSET AL32UTF8 INFILE '"+fileRoute+""+fileName+"'" + //設置字符集編碼SELECT * FROM NLS_DATABASE_PARAMETERS WHERE PARAMETER = 'NLS_CHARACTERSET';
33         " APPEND INTO TABLE "+tableName+"" + ////覆蓋寫入
34         " FIELDS TERMINATED BY '\\|'" + //數據中每行記錄用","分隔 ,TERMINATED用於控制字段的分隔符,可以為多個字符。|需要轉譯
35         " OPTIONALLY  ENCLOSED BY \"'\"" + //源文件有引號 '',這里去掉    ''''"
36         " TRAILING NULLCOLS "+fieldName+"";  //表的字段沒有對應的值時允許為空  源數據沒有對應,寫入null
37         try {
38             fw = new FileWriter(fileRoute + "" + ctlfileName);
39             fw.write(strctl);
40         } catch (IOException e) {
41             e.printStackTrace();
42         } finally {
43             try {
44                 fw.flush();
45                 fw.close();
46             } catch (IOException e) {
47                 logger.error("生成控制器文件異常...");
48                 e.printStackTrace();
49             }
50         }
51     }
52 
53     @Override
54     public void Executive(String user, String pwd, String database, String fileRoute, String ctlfileName,String logfileName) {
55         InputStream ins = null;
56         //要執行的DOS命令  --數據庫  用戶名  密碼  user/password@database
57         String dos="sqlldr "+user+"/"+pwd+"@"+database+" control="+fileRoute+""+ctlfileName+" log="+fileRoute+""+logfileName;
58         logger.info("執行的dos命令:" + dos);
59         String[] cmd = new String[] { "cmd.exe", "/C", dos }; // 命令cmd /c dir:是執行完dir命令后關閉命令窗口cmd /k dir:是執行完dir命令后不關閉命令窗口。
60         try {
61             Process process = Runtime.getRuntime().exec(cmd);
62             ins = process.getInputStream(); // 獲取執行cmd命令后的信息
63 
64             BufferedReader reader = new BufferedReader(new InputStreamReader(ins,Charset.forName("GBK")));//解決dos下中文輸出亂碼
65             String line = null;
66             long startTime = new Date().getTime();
67             while ((line = reader.readLine()) != null) {
68                 logger.info("調用dos執行的結果==========>" + line); // 輸出
69             }
70             int exitValue = process.waitFor();
71             if (exitValue == 0) {
72                 logger.info("返回值:" + exitValue + "\n數據導入成功");
73                 logger.info("總共耗時:" + (new Date().getTime() - startTime) / 1000 + "秒");
74             } else {
75                 logger.info("返回值:" + exitValue + "\n數據導入失敗");
76             }
77 
78             process.getOutputStream().close(); // 關閉
79         } catch (Exception e) {
80             e.printStackTrace();
81         }
82     }
83     
84 }

生成測試數據的代碼

 1 package com.sun.sqlloader;
 2 
 3 import java.io.BufferedWriter;
 4 import java.io.File;
 5 import java.io.FileOutputStream;
 6 import java.io.IOException;
 7 import java.io.OutputStreamWriter;
 8 
 9 /**
10  * 循環將數據按照指定的格式寫入文本文件
11  * @ClassName: OperaFile  
12  * @author sunt  
13  * @date 2017年11月15日 
14  * @version V1.0
15  */
16 public class OperaFile {
17 
18     /**
19      * 寫數據到文件
20      * @Title: writeFile 
21      * @author sunt  
22      * @date 2017年11月15日
23      * @return void
24      */
25     public static void writeFile(String filePath) throws IOException {
26         File fout = new File(filePath);
27         FileOutputStream fos = new FileOutputStream(fout);
28         BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(fos));
29         for (Long i = 0L; i < 10000000; i++) {
30             bw.write(i + "|測試數據"+i+"|");
31             bw.newLine();
32         }
33         bw.close();
34     }
35 }

前台展示效果

只需要輸入:表名和字段名,上傳大文本文件提交即可

一千萬條數據測試結果如下:

執行結果:大約5分多鍾

數據庫結果:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM