0 解決問題最佳途徑:直接找官方
先說明的是,有問題直接去找官方文檔,而不應該去百度搜索,您很容易體驗到,搜索引擎很難快速找到真正對您有價值的解決方案,而官方文檔是最快捷的途徑。
本篇也是基于官方文檔和自己的實踐得出的一些經驗,適合初學者,本人也是初學者。
測試的插入數據只有353條,但是您依然能夠體會到性能的提升。
1 【40s】使用云數據庫Mysql的體驗
下面是使用JDBC和INSERT INTO
語句逐條插入信息。
/*** 將訂單信息存入表 Performance_First** @param SalesOrder 銷售提交批量訂單* @param id 當前登錄賬戶的id*/
private static void InsertSalesOrder(int id, String name, int[] SalesOrder) throws SQLException {Connection connection = null;PreparedStatement insertSalesOrder = null;String insertSalesOrderSQL = "insert into Performance_First " +"values (?,?,0,now(),?,0)";long startTime = 0;try {connection = DatabaseOperation.CreateDatabaseConnection();assert connection != null;insertSalesOrder = connection.prepareStatement(insertSalesOrderSQL);startTime = System.currentTimeMillis();// TODO 記錄時間// 插入訂單for (int TemporarySalesOrder : SalesOrder) {insertSalesOrder.setInt(1, id);insertSalesOrder.setString(2, name);insertSalesOrder.setInt(3, TemporarySalesOrder);insertSalesOrder.execute();// 應該加點插入成功的標記}} catch (Exception e) {e.printStackTrace();} finally {DatabaseOperation.CloseDatabase(connection, insertSalesOrder);}long endTime = System.currentTimeMillis();System.out.println("程序運行時間:" + (endTime - startTime) + "ms");
}
測試數據是353條,居然需要40s,我的天,難以忍受!
于是使用bing搜索半天,也沒有找到什么結果,之后看見了官方文檔,很快找到完美的解決方案。
首先,進入Mysql中文文檔,找到第7章 優化(可惜沒有搜索功能……)。
我們找到INSERT語句的速度。
入一個記錄需要的時間由下列因素組成,其中的數字表示大約比例:
連接:(3)
發送查詢給服務器:(2)
分析查詢:(2)
插入記錄:(1x記錄大小)
插入索引:(1x索引)
關閉:(1)
這不考慮打開表的初始開銷,每個并發運行的查詢打開。
好了,現在明白了,使用云數據庫,連接和發送就占用了大部分時間,所以,使用本地數據庫試一試?
2 【2s】提高性能:使用本地數據庫
這部分依然使用INSERT INTO
語句,速度果然提高了很多,比較不需要聯網發送數據了。
不過,我們還想再快一點,此時,你需要知道,你不知道你不知道的知識,如果官方文檔找不到蛛絲馬跡,又沒有搜索,那就只能先去搜索引擎搜一下,看看我們需要解決那個位置的問題。
不過,幸運的是,官方文檔還真有。
當從一個文本文件裝載一個表時,使用LOAD DATA INFILE。這通常比使用很多INSERT語句快20倍。參見LOAD DATA INFILE語法。
3 【20ms】進一步提高性能:使用LOAD DATA INFILE
而不是INSERT INTO
我們獲取了LOAD DATA INFILE
的語法原型,然后使用我們需要的部分就可以了。
3.1 使用什么選項
我們的需求是
- 將txt文件導入到數據庫表的一部分columns
- 將其他的columns設置為指定值
根據語法原型,我們需要的語法格式是
LOAD DATA INFILE [file_name]
INTO TABLE [table_name](column1,column2,……)
SET column5=expr,column6=expr……
JDBC是這樣的(關鍵代碼)
String sql = "LOAD DATA INFILE ? " +"INTO TABLE Performance_First(SalesOrder) " +"SET ID=?,Name=?,QueryStatus=0,SubmitTime=now(),CalculateStatus=0";
try (Connection connection = DatabaseOperation.CreateDatabaseConnection(); // 連接數據庫
) {assert connection != null;startTime = System.currentTimeMillis();try (PreparedStatement p = connection.prepareStatement(sql);) {p.setString(1, filePath);p.setInt(2, id);p.setString(3, name);p.execute();}
}
3.2 文件路徑:客戶端與服務器
如果指定了LOCAL,則被認為與連接的客戶端有關:
· 如果指定了LOCAL,則文件會被客戶主機上的客戶端讀取,并被發送到服務器。文件會被給予一個完整的路徑名稱,以指定確切的位置。如果給定的是一個相對的路徑名稱,則此名稱會被理解為相對于啟動客戶端時所在的目錄。
- 如果LOCAL沒有被指定,則文件必須位于服務器主機上,并且被服務器直接讀取。
當在服務器主機上為文件定位時,服務器使用以下規則:- 如果給定了一個絕對的路徑名稱,則服務器使用此路徑名稱。
- 如果給定了帶有一個或多個引導組件的相對路徑名稱,則服務器會搜索相對于服務器數據目錄的文件。
- 如果給定了一個不帶引導組件的文件名稱,則服務器會在默認數據庫的數據庫目錄中尋找文件。
注意,這些規則意味著名為./myfile.txt的文件會從服務器數據目錄中被讀取,而名為myfile.txt的同樣的文件會從默認數據庫的數據庫目錄中讀取。例如,下面的LOAD DATA語句會從db1數據庫目錄中讀取文件data.txt,因為db1是當前數據庫。即使語句明確把文件載入到db2數據庫中的表里,也會從db1目錄中讀取。mysql> USE db1; mysql> LOAD DATA INFILE 'data.txt' INTO TABLE db2.my_table;
注意,使用正斜杠指定Windows路徑名稱,而不是使用反斜杠。如果您使用反斜杠,您必須使用兩個。
出于安全原因,當讀取位于服務器中的文本文件時,文件必須位于數據庫目錄中,或者是全體可讀的。另外,要對服務器文件使用LOAD DATA INFILE,您必須擁有FILE權限。
我們選擇直接使用服務器本地的文件,而不是客戶端遠程上傳,因此不需要加LOCAL
。
此外,數據庫的文件會有默認的DATA
文件夾,如果使用./
則會認為是此文件夾,使用JDBC導入文件夾的時候,需要
- 獲取java工程的路徑
- 文件路徑 = Java工程路徑 + 文件名
注意加上后綴,并且,不要使用中文。
3.3 注意
- 不能包含中文:文件的路徑和文件名都不能包含中文
- 您可能會遇到一些報錯,這是因為權限不足,默認情況下,只有指定文件夾中的文件可以被導入,這個問題請如果您遇到,請自行查閱資料解決。