2017-02-14 87 views
0

我正在從Java數據庫表讀取記錄並在讀取每條記錄後執行一些處理的Java應用程序。 注意:應用程序只從數據庫讀取數據,它不寫回任何內容。Java數據庫處理性能建議

我正在使用標準的jdbc驅動程序,連接,準備好的語句和結果集。 我需要獲取的記錄數量大約是500000(最大)。

我目前的執行情況:

我已聲明獲取大小爲500(查詢是從一個表一個簡單的SELECT語句) 在閱讀每個結果集的記錄,我添加的每個記錄到java集合是這樣的:

 List<HashMap<String,String>> userData=new ArrayList<HashMap<String,String>>(); 
     ResultSetMetaData resultSetMetData = resultSet.getMetaData(); 
     while(resultSet.next()){ 
      HashMap<String,String> recordMap = new HashMap<String,String>(); 
      for (int i = 1; i <= resultSetMetData.getColumnCount(); i++) { 
       String key = resultSetMetData.getColumnName(i); 
       String value = resultSet.getString(key); 
       recordMap.put(key, value); 
      } 
      userData.add(recordMap); 
     } 

將所有行添加到java集合後,我關閉連接。

我希望你的建議能夠以最有效的方式做到這一點。如果你認爲更好,我願意編寫多線程事件。

謝謝
腰帶

+0

如果將所有200000行保存在列表中,它將會是內存密集型的。你對數據做了什麼?它不可能是'SELECT'按您需要的格式輸入 –

+0

@ScaryWombat號我需要根據每條記錄做一些處理,如果在列表中添加所有行都是內存密集的,建議保持結果集打開很長時間,直到我處理所有記錄? –

+1

即使我說'記憶密集型',這對你來說可能不是問題。 g有什麼問題? –

回答

1

你也應該處理你的收藏。

1-如果您事先知道您的名單會很大,您可以使用ArrayList Capacity

默認情況下,ArrayList的初始容量爲10條記錄。當你的列表增長時,java會連續(小)地重新分配列表,從而失去很多時間。如果你這樣做,例如

List<HashMap<String,String>> userData=new ArrayList<HashMap<String,String>>(500); 

你將有一個initialCapacity 500,節省大量的重新分配,所以時間。

此外,方法ensureCapacity可以讓您的列表增加用戶定義的容量,這可能很有用。

因此,如果您事先知道結果的大小,或者至少您有一個想法,那麼您可以使用它。否則,如果之後只需要迭代列表中的內容,則可以考慮LinkedList,它具有恆定的添加操作時間(但不適用於位置訪問)。

2-也考慮到你的行數據結構,創建一個封裝你的結果集列的對象,而不是每一行的地圖(所以非常大量的地圖,每一個都需要一定的內存分配,所以資源和時間)將更加高效。

看一看在CollectionFramework,在ArrayListLinkedList文檔

UPDATE 這裏是一些 '骯髒' 基準測試

package com.test; 

import java.lang.reflect.Field; 
import java.util.ArrayList; 
import java.util.Date; 
import java.util.HashMap; 
import java.util.List; 

public class LoopTest { 

    public static void main(String[] args) { 
     LoopTest lt=new LoopTest(); 
     int records = 200000; 
     lt.execute(records, false); 
     lt.execute(records, true); 
     lt.executeObj(records, false); 
     lt.executeObj(records, true); 
     lt.executeReflect(records, false); 
     lt.executeReflect(records, true); 


    } 

    public void execute(int loops, boolean useCapacity){ 
     Date start=new Date(); 
     System.out.println("EXAMPLE WITH HASHMAP"); 
     System.out.println(start+ " time: "+start.getTime()+" Start for loops="+loops+ " and useCapacity="+useCapacity); 
     List<HashMap<String,String>> userData=null; 
     if(useCapacity) 
      userData=new ArrayList<HashMap<String,String>>(loops); 
     else 
      userData=new ArrayList<HashMap<String,String>>(); 

     for(int i=0;i<loops;i++){ 
      HashMap<String,String> recordMap = new HashMap<String,String>(); 
      for (int j = 1; j <= 10; j++) { 
       String key = j+""; 
       String value = j+" val"; 
       recordMap.put(key, value); 
      } 
      userData.add(recordMap); 
     } 
     Date end=new Date(); 
     System.out.println(end+ " time: "+end.getTime()+", elapsed="+(end.getTime()-start.getTime())+" end for loops="+loops+ " and useCapacity="+useCapacity); 
     System.out.println("-------------------------"); 

    } 

    public void executeObj(int loops, boolean useCapacity){ 
     System.out.println("EXAMPLE WITH CLASSIC OBJECT"); 
     Date start=new Date(); 
     System.out.println(start+ " time: "+start.getTime()+" Start for loops="+loops+ ", object and useCapacity="+useCapacity); 
     List<TestObj> userData=null; 
     if(useCapacity) 
      userData=new ArrayList<TestObj>(loops); 
     else 
      userData=new ArrayList<TestObj>(); 

     for (int i=0;i<loops;i++){ 
      TestObj testObj = new TestObj(); 
      testObj.test1="1"; 
      testObj.test2="1"; 
      testObj.test3="1"; 
      testObj.test4="1"; 
      testObj.test5="1"; 
      testObj.test6="1"; 
      testObj.test7="1"; 
      testObj.test8="1"; 
      testObj.test9="1"; 
      testObj.test10="1"; 
      testObj.test11="1"; 
      testObj.test12="1"; 
      testObj.test13="1"; 
      testObj.test14="1"; 
      testObj.test15="1"; 
      testObj.test16="1"; 
      testObj.test17="1"; 
      testObj.test18="1"; 
      testObj.test19="1"; 
      testObj.test20="1"; 
      userData.add(testObj); 
     } 
     Date end=new Date(); 
     System.out.println(end+ " time: "+end.getTime()+", elapsed="+(end.getTime()-start.getTime())+" end for loops="+loops+ ", object and useCapacity="+useCapacity); 
     System.out.println("-------------------------"); 

    } 

    public void executeReflect(int loops, boolean useCapacity){ 
     System.out.println("EXAMPLE WITH REFLECTION"); 
     Date start=new Date(); 
     System.out.println(start+ " time: "+start.getTime()+" Start for loops="+loops+ ", object and useCapacity="+useCapacity); 
     List<TestObj> userData=null; 
     if(useCapacity) 
      userData=new ArrayList<TestObj>(loops); 
     else 
      userData=new ArrayList<TestObj>(); 



     for (int i=0;i<loops;i++){ 
      try{ 
       Class<?> objClass=Class.forName("com.test.TestObj"); 
       Object myObj=objClass.newInstance(); 


      for(int j=1;j<=20;j++){ 

       Field f=objClass.getDeclaredField("test"+j); 
       f.set(myObj, "1"); 
      } 
      userData.add((TestObj)myObj); 

      } catch (ClassNotFoundException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } catch (InstantiationException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } catch (IllegalAccessException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } catch (NoSuchFieldException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } catch (SecurityException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 



     } 
     Date end=new Date(); 
     System.out.println(end+ " time: "+end.getTime()+", elapsed="+(end.getTime()-start.getTime())+" end for loops="+loops+ ", reflect and useCapacity="+useCapacity); 
     System.out.println("-------------------------"); 

    } 


} 

TestObj是包含一個名爲只有公共領域的對象test1的... test25 。爲了簡單和快速,我將它們公諸於世,在現實世界中,您會讓它們變得私密。它可以被更多的工程,但我心中已經迅速地完成它..

這裏闡述

EXAMPLE WITH HASHMAP, useCapacity=false, loops=200000 
Wed Feb 15 06:04:48 CET 2017 time: 1487135088903 Start for loops=200000 and useCapacity=false 
Wed Feb 15 06:04:55 CET 2017 time: 1487135095922, elapsed=7019 end for loops=200000 and useCapacity=false 
------------------------- 
EXAMPLE WITH HASHMAP, useCapacity=true, loops=200000 
Wed Feb 15 06:04:55 CET 2017 time: 1487135095922 Start for loops=200000 and useCapacity=true 
Wed Feb 15 06:05:01 CET 2017 time: 1487135101073, elapsed=5151 end for loops=200000 and useCapacity=true 
------------------------- 
EXAMPLE WITH CLASSIC OBJECT, useCapacity=false, loops=200000 
Wed Feb 15 06:05:01 CET 2017 time: 1487135101073 Start for loops=200000, object and useCapacity=false 
Wed Feb 15 06:05:01 CET 2017 time: 1487135101254, elapsed=181 end for loops=200000, object and useCapacity=false 
------------------------- 
EXAMPLE WITH CLASSIC OBJECT, useCapacity=true, loops=200000 
Wed Feb 15 06:05:01 CET 2017 time: 1487135101254 Start for loops=200000, object and useCapacity=true 
Wed Feb 15 06:05:01 CET 2017 time: 1487135101274, elapsed=20 end for loops=200000, object and useCapacity=true 
------------------------- 
EXAMPLE WITH REFLECTION, useCapacity=false, loops=200000 
Wed Feb 15 06:05:01 CET 2017 time: 1487135101274 Start for loops=200000, object and useCapacity=false 
Wed Feb 15 06:05:05 CET 2017 time: 1487135105562, elapsed=4288 end for loops=200000, reflect and useCapacity=false 
------------------------- 
EXAMPLE WITH REFLECTION, useCapacity=true, loops=200000 
Wed Feb 15 06:05:05 CET 2017 time: 1487135105562 Start for loops=200000, object and useCapacity=true 
Wed Feb 15 06:05:09 CET 2017 time: 1487135109711, elapsed=4149 end for loops=200000, reflect and useCapacity=true 
------------------------- 

的輸出正如您將看到使用量帶來約7秒至5.1秒HashMap的例子。

以傳統方式使用對象,使用陣列中的容量可以達到181毫秒(!)甚至20毫秒(!!)。

由於使用容量(4288 vs 4149),反射性能大約在4秒左右。

請注意,基準測試的確切時間因執行而異。但總的來說,時間順序總是一樣的。

關於在'塊'中獲取記錄是內存一致性的一個很好的解決方案,特別是如果您的闡述可能很長時間的話。經常長時間運行的查詢可能會出現「快照太舊」的錯誤,這可能會產生問題。而且它也不確定性能會如何降低。 在過去,我必須處理類似的問題,一個非常好的解決方案是在源表中放置一個帶有索引的字段「chunk_id」,這樣可以輕鬆地重複查詢下一行的查詢,並給我比檢索表格的所有內容更有效率。一般來說,你只需要一種方法來識別來自組1,組2,......組n的記錄組(當然在那裏放置索引)

PS反射的例子很簡單,但它是隻是爲了演示如何操作,你可以使用很多功能,包括方法等

+0

感謝您的回答。關於點2.我可以創建一個對象來封裝我的結果集列。問題。目前我從數據庫中獲取的列是可配置的,如果我有一個對象,我失去了<非硬編碼或映射列中的代碼>的可能性> –

+0

在這種情況下,我相信你不能這樣做,但這取決於當然,你的表有多少變化呢? – Massimo

+0

其實它永遠不會改變,只有配置映射可能會改變,簡而言之,我需要將表列映射到一些內部字段,這是配置我會失去這種靈活性,如果我使用對象 –

-1

的這個問題的答案取決於相比API您的SQL查詢的相對速度叫你需要,你有多少內存相比尺寸和沿您的記錄數。

不知道這一點,很難知道。

如果API調用是相對緩慢的,你有足夠的內存:

  1. 有多個線程,你調
  2. 查詢數據庫,並通過一個發送每行一個創建一個ThreadPoolExecutor通過執行

執行如果查詢是比較慢的,或者你有較少的內存:

  1. 分區SELECT語句進去,說8個查詢,每個返回數據的不同部分
  2. 創建8個線程
  3. 每個線程運行查詢和調用API的每一行由Oracle
返回
+0

Downvoter(或其他人)照顧評論? –