java大量檔案排序顯示速度優化總結(資料庫排序)
工作中要做一個讀取某個資料夾下的所有檔案並按檔案修改時間倒序排序檔名分頁顯示在列表中的功能,其中檔名中包含檔案修改的時間(like:core_csc_npp_20140121153526_11057.log),如果資料夾下的檔案少,查詢顯示速度肯定很快,但是如果資料夾下有上萬個檔案(列如10萬),把10萬個檔名排序並顯示,就很慢,之前試過把檔案陣列傳到Arrays.sort(file[],new LastModifiedFileComparator())方法裡並在LastModifiedFileComparator里根據檔案modified時間轉成long再判斷排序,速度很慢,10萬個檔案顯示排序顯示需要1分鐘多,後來通過先把所有檔案的modified時間(long型)提取出來放到數組裡再調Arrays.sort(long[]),對long[]排序了,
再根據時間查檔名,可是結果還是很糟糕,時間不是花在Array.sort方法上,而是花在根據時間查檔名上了。原因是用了兩個for迴圈去迴圈遍歷 10萬個檔案兩個for迴圈去查詢就要迴圈100億次!!程式直接卡死,怎麼辦呢,後來想資料庫裡的排序很快吧,把檔名和時間的map對映對插入資料庫裡,然後對時間order by檔名不就排出來了嗎,立馬行動,通過在mysql裡建立臨時表(連線關閉後自動drop掉了),由於插入資料量大,普通的insert 語句速度太慢,辦法就是一條sql語句插入多條資料,像這樣:INSERT
INTO`sorttemp`(`filename`,`modifiedtime`)VALUES('core_csc_npp_20140121154354_43015.log','20140225115022'),'core_csc_npp_20140121154354_14487.log','20140225115004'),...把map所有的key value都拼成字串最後統一executeUpdate,經過這樣改進,1萬個檔案排序顯示速度1秒左右,算比較完美,但是如果檔案在幾萬以上,sql語句會有長度限制(大約所有字元大小不能超過1M),所以還要對sql語句進行分割,比如10萬個檔案就把拼成的sql字串分成十部分分別插入,每部分執行不超過1萬個檔案的插入。具體分割的方法,有點繁瑣(按什麼分割,要考慮執行效率,我用的StringBuilder),由於是大批量的字串處理,很容易出現java.lang.OutOfMemoryError:
Java heap space的情況,程式碼的執行效率就很重要了。最後完善後經測試 10萬個檔案下 查詢顯示速度大約10秒(後臺插入資料庫並排序花了10秒),1萬個檔案速度大約1秒,較完美的解決了此問題。程式碼貼出來:
* @param file 要排序顯示的檔案(資料夾)
* @author bosszhang
* @return List(排序後的檔名)
* @date 2014年2月25日
*/
public static List<String> sortInDB(File file){
List<String> names=new ArrayList<String>();
File[] files = file.listFiles();
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
//用於存放所有的檔名和它的modified時間的對映
Map<String,Long> nameMapTime=new HashMap<String,Long>();
for(int i=0;i<files.length;i++){
nameMapTime.put(files[i].getName(), Long.parseLong(formatter.format(new Date(files[i].lastModified()))));
}
//存入資料庫進行排序
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
Class.forName("com.mysql.jdbc.Driver");
String host = Configures.getString("dbfw.dao.dbconn_url");
String url = host.substring(0, host.indexOf("?"));
String user = Configures.getString("dbfw.dao.dbconn_username");
String password = Configures.getString("dbfw.dao.dbconn_password");
conn = DriverManager.getConnection(url, user, password);
stmt = conn.createStatement();
//建立臨時表,連線關閉後 表自動刪除
String sql = "create temporary table `sorttemp`(`id` int(11) auto_increment not null primary key,`filename` varchar(50),`modifiedtime` varchar(15))";
stmt.executeUpdate(sql);
try {
Iterator iter = nameMapTime.entrySet().iterator();
List<String> alllist=new ArrayList<String>(2000);//所有('core_csc_npp_20140121154354_2304.log','20140225134012')這樣的集合
StringBuilder insertSql= new StringBuilder();
while (iter.hasNext()){
Map.Entry entry = (Map.Entry) iter.next();
String key = (String)entry.getKey();
Long val = (Long)entry.getValue();
alllist.add(",('"+key+"','"+val+"')");
}
alllist.add(0, "INSERT INTO sorttemp(filename,modifiedtime) VALUES "+alllist.get(1).substring(1));
//如果檔案數大於10000,insert into這條SQL語句本身就會受到限制,所以分成多個部分分別執行
int length=files.length;
if(length>10000){
//
String[] totalArr=insertSql.toString().split("\\,\\(");//對於大批量的字串String.split方法效率非常低,所以不用
//
Pattern pattern=Pattern.compile("\\,\\(");
//
String []totalArr=pattern.split(insertSql);
StringBuilder sb=new StringBuilder(10000);
List<StringBuilder> sblist=new ArrayList<StringBuilder>(5);//每部分的字串集合
String lengthStr=String.valueOf(length);
int num=Integer.parseInt(lengthStr.substring(0, lengthStr.length()-4))+1;
for(int i=1;i<=num;i++){
for(int j=(i-1)*10000;j<i*10000;j++){
if(j<length){
sb.append(alllist.get(j));
}
}
if(null!=sb&&!"".equals(sb.toString())){
sblist.add(sb);
}
sb=new StringBuilder();
}
for(int i=0;i<sblist.size();i++){
StringBuilder strb=sblist.get(i);
if(i>0){
//
StringBuilder strb=sblist.get(i).deleteCharAt(len-1);//
//
char[] srcArr=strb.toString().toCharArray();
//
char[] desArr=new char[srcArr.length-1];
//
System.arraycopy(srcArr, 0, desArr, 0, srcArr.length-1);
//去掉第一個字元,deleteCharAt效率低,所以用System.arraycopy
char[] srcArr=strb.toString().toCharArray();
char[] desArr=new char[srcArr.length-1];
System.arraycopy(srcArr, 1, desArr, 0, srcArr.length-1);
String result=new String(desArr);
stmt.executeUpdate("INSERT INTO sorttemp(filename,modifiedtime) VALUES "+result);
}else{
stmt.executeUpdate(strb.toString());
}
}
}else{
int len=alllist.size();
for(int i=0;i<len;i++){
insertSql.append(alllist.get(i));
}
stmt.executeUpdate(insertSql.toString());
}
}catch (Exception e) {
System.out.println("---------insert into table:sorttemp error----------:\n");
e.printStackTrace();
}
String query="select filename from sorttemp order by modifiedtime desc";
rs = stmt.executeQuery(query);
while(rs.next()){
names.add(rs.getString(1));
}
//刪除表sorttemp
// String delSql="DROP TABLE sorttemp";
// stmt.executeUpdate(delSql);
} catch (ClassNotFoundException e) {
System.out.println("驅動載入錯誤");
}catch (SQLException ex) {
System.err.println("SQLException:"+ex.getMessage());
}finally {
try{
if(rs != null) {
rs.close();
rs = null;
}
if(stmt != null) {
stmt.close();
stmt = null;
}
if(conn != null) {
conn.close();
conn = null;
}
}catch(SQLException e) {
System.err.println("SQLException:"+e.getMessage());
}
}
return names;
}