您好,登錄后才能下訂單哦!
需求是這樣的:現(xiàn)在需要測(cè)試一個(gè)內(nèi)存數(shù)據(jù)庫(kù)的入庫(kù)性能,要求測(cè)試每線程準(zhǔn)備一個(gè)文件,10個(gè)線程入庫(kù)總計(jì)100w記錄數(shù)的單表入庫(kù)性能。
知識(shí)點(diǎn):jdbc + 多線程 + 批處理 + 文件讀取
先來(lái)看看我的代碼結(jié)構(gòu)
說(shuō)明:
files: 存放即將要讀取的文件。
lib: 存放第三方的jar文件,例如數(shù)據(jù)庫(kù)驅(qū)動(dòng)包。
MemSqlTestMain: 這是工程的入口,就是主程序。
DBUtil: 這個(gè)類(lèi)是數(shù)據(jù)庫(kù)幫助類(lèi),主要讀取數(shù)據(jù)庫(kù)配置信息獲取連接關(guān)閉連接等操作。
InsertUtil: 主要做的是讀取數(shù)據(jù)文件生成sql并批量入庫(kù)的一個(gè)類(lèi)。
TableDataInfo: 主要對(duì)要插入的數(shù)據(jù)表的對(duì)象的一個(gè)類(lèi)。
XMLUtil: 讀取XML配置文件
config.xml: 配置要插入的表信息以及文件的路徑等信息
dbconfig.properties: 主要對(duì)數(shù)據(jù)庫(kù)的連接信息進(jìn)行存儲(chǔ),包括URL,用戶名密碼等等。
話不多說(shuō)直接上代碼:
import java.util.ArrayList; /** * @param * @author wu.lin * @description 程序入口,啟用線程讀取文件并入庫(kù) * @create 2016年09月01日 15:12 * @throws */ public class MemSqlTestMain { public static void main(String[] args) { //通過(guò)讀取配置文件讀取要插入數(shù)據(jù)的表名 String tableName = XMLUtil.getTableName(); System.out.println(tableName); //通過(guò)配置文件讀取數(shù)據(jù)存放的文件的路徑 ArrayList<String> fileNameList = XMLUtil.getFileNameList(); int len = fileNameList.size(); //針對(duì)每一個(gè)文件開(kāi)啟一個(gè)進(jìn)程去執(zhí)行讀取并入庫(kù)的操作 for (int i = 0; i < len; i++) { String fileName = fileNameList.get(i); System.out.println(fileName); new Thread(new InsertUtil(fileName, tableName)).start(); } } }
import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.InputStreamReader; import java.sql.*; /** * @param * @author wu.lin * @description InsertUtil是一個(gè)線程類(lèi),主要讀取數(shù)據(jù)文件組裝Sql并執(zhí)行入庫(kù)操作 * @create 2016年09月01日 14:10 * @throws */ public class InsertUtil implements Runnable { //文件路徑 private String filePath; //表名 private String tableName; //.cvs文件數(shù)據(jù)以","分隔 private static String DELIMITERS = ","; //獲取數(shù)據(jù)庫(kù)幫助類(lèi) DBUtil dbutil = DBUtil.getInstance(); public InsertUtil() {} public InsertUtil(String filePath, String tableName) { this.filePath = filePath; this.tableName = tableName; } public static String getDELIMITERS() { return DELIMITERS; } public static void setDELIMITERS(String delimiters) { DELIMITERS = delimiters; } public String getFilePath() { return filePath; } public void setFilePath(String filePath) { this.filePath = filePath; } //讀取文件并且批處理入庫(kù)的方法 public boolean insertDB(String tablename, long rc, String filePath) { if(filePath == null || "".equals(filePath)) { System.out.println("文件路徑為空"); return false; } if (rc < 1) { rc = 100; } Connection conn = null; boolean flag = false; Statement pre = null; String sql = ""; TableDataInfo tableInfo = new TableDataInfo(); try { if(conn == null) { conn = dbutil.getConnection(); } pre = conn.createStatement(); conn.setAutoCommit(false); int colCount = tableInfo.getTableColNums(tablename, conn); int rowCount = 0; File file = new File(filePath); BufferedReader buf = null; buf = new BufferedReader(new InputStreamReader(new FileInputStream(file))); String line_record = buf.readLine(); long startTime = System.currentTimeMillis(); //開(kāi)始計(jì)時(shí) while (line_record != null) { // 解析每一條記錄 sql = "INSERT INTO " + tablename + " VALUES('"; String[] fields = line_record.split(DELIMITERS); //對(duì)Insert語(yǔ)句的合法性進(jìn)行判斷 if(fields.length != colCount){ System.out.println("要插入的數(shù)據(jù)列數(shù)和表的數(shù)據(jù)列不相匹配,停止執(zhí)行"); break; } for (int i = 0; i < fields.length; i++) { sql += fields[i]; if (i < fields.length - 1) { sql += "','"; } } sql += "');"; // 在控制臺(tái)輸出SQL語(yǔ)句 // System.out.println(sql); //執(zhí)行SQL語(yǔ)句 pre.addBatch(sql); rowCount++; line_record = buf.readLine(); if (rowCount >= rc) { break; } } pre.executeBatch(); conn.setAutoCommit(true); pre.close(); System.out.println("共寫(xiě)入行數(shù):" + rowCount); long endTime = System.currentTimeMillis(); //停止計(jì)時(shí) System.out.println("執(zhí)行時(shí)間為:" + (endTime - startTime) + " ms"); } catch (Exception e) { flag = false; try { //回滾 if(conn != null) { conn.rollback(); } } catch (SQLException e1) { e1.printStackTrace(); } e.printStackTrace(); } finally { dbutil.close(null, pre, conn); } return flag; } public void run() { this.insertDB(tableName, 500000, filePath); } }
import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; /** * @param * @author wu.lin * @description 數(shù)據(jù)庫(kù)表實(shí)體 * @create 2016年09月01日 14:19 * @throws */ public class TableDataInfo { DBUtil dbutil = DBUtil.getInstance(); /** * * @param m_TableName * @param m_Connection * @return 該表的列數(shù) */ public int getTableColNums(String m_TableName, Connection m_Connection) { int colCount = 0; try { if (m_Connection == null) { m_Connection = dbutil.getConnection(); } DatabaseMetaData m_DBMetaData = m_Connection.getMetaData(); ResultSet tableRet = m_DBMetaData.getTables(null, "%", m_TableName, new String[] { "TABLE" }); while (tableRet.next()) { System.out.println("Table name is:" + tableRet.getString("TABLE_NAME")); } String columnName; String columnType; ResultSet colRet = m_DBMetaData.getColumns(null, "%", m_TableName,"%"); while (colRet.next()) { columnName = colRet.getString("COLUMN_NAME"); columnType = colRet.getString("TYPE_NAME"); int dataSize = colRet.getInt("COLUMN_SIZE"); int digits = colRet.getInt("DECIMAL_DIGITS"); int nullable = colRet.getInt("NULLABLE"); String nullFlag; if (nullable == 1) { nullFlag = "Null"; } else { nullFlag = "Not Null"; } System.out.println(columnName + " " + columnType + "(" + dataSize + "," + digits + ") " + nullFlag); colCount++; } } catch (SQLException e) { e.printStackTrace(); } System.out.println("The number of column is: " + colCount); return colCount; } }
接下來(lái)就剩下讀取配置文件的代碼了,先來(lái)看看配置文件內(nèi)容(這里配置了數(shù)據(jù)庫(kù)配置文件路徑表名以及文件存放的相對(duì)路徑):
<?xml version="1.0" encoding="utf-8" ?> <config> <db_file>src/dbconfig.properties</db_file> <tableName>memtest</tableName> <files> <filePath>files/memtest.csv</filePath> <filePath>files/memtest_1.csv</filePath> <filePath>files/memtest_2.csv</filePath> <filePath>files/memtest_3.csv</filePath> <filePath>files/memtest_4.csv</filePath> <filePath>files/memtest_5.csv</filePath> <filePath>files/memtest_6.csv</filePath> <filePath>files/memtest_7.csv</filePath> <filePath>files/memtest_8.csv</filePath> <filePath>files/memtest_9.csv</filePath> <filePath>files/memtest_10.csv</filePath> </files> </config>
接下來(lái)是讀取這個(gè)配置文件的內(nèi)容,比較簡(jiǎn)單,所以只貼部分代碼:
import javax.xml.parsers.*; import org.w3c.dom.*; import java.io.*; import java.util.ArrayList; /** * @param * @author wu.lin * @description 讀取配置信息 * @create 2016年09月01日 15:45 * @throws */ public class XMLUtil { //該方法用于從XML配置文件中提取要插入的表名稱,并返回該表名稱 public static String getTableName() { return getXmlProperties("tableName"); } public static String getDatabaseUrl() { return getXmlProperties("dataBaseUrl"); } public static String getDbFilePath() { return getXmlProperties("db_file"); } private static String getXmlProperties(String proName) { try { Document doc = getDoc(); //獲取包含品牌名稱的文本節(jié)點(diǎn) NodeList nl = doc.getElementsByTagName(proName); Node classNode=nl.item(0).getFirstChild(); String tableName=classNode.getNodeValue().trim(); return tableName; } catch(Exception e) { e.printStackTrace(); return null; } } private static Document getDoc() throws Exception { //創(chuàng)建文檔對(duì)象 DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = dFactory.newDocumentBuilder(); Document doc; doc = builder.parse(new File("src/config.xml")); return doc; } }
數(shù)據(jù)庫(kù)配置信息文檔:
db.used=mysql # driver class oracle.jdbc.driver_class=oracle.jdbc.driver.OracleDriver # URL oracle.jdbc.url=jdbc:oracle:thin:@localhost:1521:ORCL # username oracle.jdbc.username=scott # pwd oracle.jdbc.pwd=tiger #mysql connect config mysql.jdbc.driver_class=com.mysql.jdbc.Driver mysql.jdbc.url=jdbc:mysql://localhost:3306/mysqldb mysql.jdbc.username=root mysql.jdbc.pwd=
最后是數(shù)據(jù)庫(kù)幫助類(lèi),比較常見(jiàn):
import java.io.FileInputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; /** * @param * @author wu.lin * @description 數(shù)據(jù)庫(kù)幫助類(lèi) * @create 2016年09月01日 18:56 * @throws */ public class DBUtil { private static Properties env = new Properties(); private static DBUtil dbutil; private static String dbname; private static String driverClass_key; private static String url_key; private static String username_key; private static String pwd_key; private DBUtil(){} // 單例模式 public static synchronized DBUtil getInstance() { if (null == dbutil) { dbutil = new DBUtil(); } return dbutil; } /** * 得到數(shù)據(jù)庫(kù)連接 * @return */ public Connection getConnection() { Connection conn = null; try { env.load(new FileInputStream(XMLUtil.getDbFilePath())); dbname = env.getProperty("db.used").toLowerCase(); driverClass_key = dbname + ".jdbc.driver_class"; url_key = dbname + ".jdbc.url"; username_key = dbname + ".jdbc.username"; pwd_key = dbname + ".jdbc.pwd"; //加載連接數(shù)據(jù)庫(kù)的驅(qū)動(dòng)程序類(lèi)文件 Class.forName(env.getProperty(driverClass_key)); conn = createConnection(); } catch (Exception e) { e.printStackTrace(); } return conn; } private Connection createConnection() throws SQLException { Connection conn = null; if ("oracle".equals(dbname)) { conn = DriverManager.getConnection(env.getProperty(url_key), env.getProperty(username_key), env.getProperty(pwd_key)); } if ("sqlserver".equals(dbname)) { conn = DriverManager.getConnection(env.getProperty(url_key), env.getProperty(username_key), env.getProperty(pwd_key)); } if ("mysql".equals(dbname)) { // 其他數(shù)據(jù)庫(kù)的連接語(yǔ)法 String url = env.getProperty(url_key); String username = env.getProperty(username_key); String pwd = env.getProperty(pwd_key); if(username != null && !"".equals(username)) { url += ("?user=" + username); if(pwd != null && !"".equals(pwd)) { url += ("&password=" + pwd); } } conn = DriverManager.getConnection(url); } return conn; } //提供jdbc關(guān)閉連接的方法 public void close(ResultSet rs,Statement st,Connection conn){ try { if(rs!=null) rs.close(); if(st!=null) st.close(); if(conn!=null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }
最后的工作便是在文件目錄存放相應(yīng)的數(shù)據(jù)文件,然后通過(guò)配置文件配置好文件名、表名以及數(shù)據(jù)庫(kù)連接的基本信息后,運(yùn)行程序入口,便可以將程序跑起來(lái)啦。但是在這個(gè)過(guò)程中也遇到一些小問(wèn)題,比如,我這邊只有一個(gè)100w條數(shù)據(jù)的.csv格式的文件,但是要求讀取十個(gè)文件,在這個(gè)時(shí)候我用到了一個(gè)小工具:
大家知道.csv格式的文件也可以用Excel軟件打開(kāi),所以在這里轉(zhuǎn)換一下用Excel分割器把文件分成十份,就完美的解決問(wèn)題啦。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。