2012-05-10 15 views
0

DBに接続して重複したエントリをチェックし、重複を削除し元のレコードの同じテキストの数を更新するプログラムがあります。JDBCクエリの最適化

私の問題は、100000レコードでは時間がかかり、プログラムは 'heapspace required'エラーで終了するということです。

タスクをすばやく実行するためにこのプログラムを最適化するにはどうすればよいですか?

public class Main { 
    // serveropsdb.properties file location = /opt/serverops/scripts/serverops.properties 
    private final static String PROPERTIES_FILENAME=File.separatorChar+"opt"+File.separatorChar+"serverops"+File.separatorChar+"scripts"+File.separatorChar+"serveropsdb.properties"; 
    private final static String DATE_FORMAT = "yyyy/MM/dd HH:mm:ss"; 
    private final static String PROPERTY_NAME_STARTTIME= "start.time"; 
    private final static String PROPERTY_NAME_ENDTIME= "end.time"; 
    private final static String PROPERTY_NAME_DB_TABLENAME= "db.tablename"; 
    private final static String PROPERTY_NAME_DB_USERNAME= "db.username"; 
    private final static String PROPERTY_NAME_DB_PASSWORD= "db.password"; 
    private final static String PROPERTY_NAME_DB_NAME= "db.name"; 

    public static void main(String[] args) { 
    System.out.println("DB consolidation started"); 

    Properties properties = new Properties(); 
    try { //loading serverops.properties file 
     properties.load(new FileInputStream(PROPERTIES_FILENAME)); 
    } catch (IOException e) { 
     System.out.println("Error loading "+PROPERTIES_FILENAME+" properties!"); 
     e.printStackTrace(); 
    } 

    try { // loading jdbc driver 
     Class.forName("com.mysql.jdbc.Driver"); 
    } catch (ClassNotFoundException e) { 
     System.out.println(" JDBC Driver not found"); 
     e.printStackTrace(); 
     return; 
    } 

    System.out.println("MySQL JDBC Driver found! Connecting to Database"); 
    Connection connection = null; 

    ResultSet resultSet = null; 
    ResultSet findResultSet = null; 

    PreparedStatement allStatement, findStatement, updateStatement; 

    //date formate used yyyy/MM/dd HH:mm:ss 
    DateTimeFormatter dateFromatter= DateTimeFormat.forPattern(DATE_FORMAT); 

    DateTime startDate = dateFromatter.parseDateTime(properties.getProperty(PROPERTY_NAME_STARTTIME)); 
    DateTime endDate= dateFromatter.parseDateTime(properties.getProperty(PROPERTY_NAME_ENDTIME)); 

    Timestamp t1 = new Timestamp(startDate.getMillis()); 
    Timestamp t2 = new Timestamp(endDate.getMillis()); 

    StringBuilder sql;  
    String toBeRemovedIndex ; 
    int updateNumEntries; 
    String toBeRemovedLog; 
    String toBeRemovedHostname; 
    boolean updateDB=false; 

    try { // connecting to DB with credentials from the properties file 
     connection = DriverManager.getConnection( 
     "jdbc:mysql://localhost:3306/"+properties.getProperty(PROPERTY_NAME_DB_NAME), 
     properties.getProperty(PROPERTY_NAME_DB_USERNAME), 
     properties.getProperty(PROPERTY_NAME_DB_PASSWORD)); 

    // getting all record between start time and end time 
     sql = new StringBuilder("SELECT * FROM" 
          + properties.getProperty(PROPERTY_NAME_DB_TABLENAME) 
          + " WHERE last_seen BETWEEN ? AND ?"); 

     allStatement = connection.prepareStatement(sql.toString()); 
     allStatement.setTimestamp(1, t1); 
     allStatement.setTimestamp(2,t2); 
     resultSet = allStatement.executeQuery(); 

     while (resultSet.next()) { 
     toBeRemovedIndex = resultSet.getString(1); 
     updateNumEntries = resultSet.getInt(2); 
     toBeRemovedLog = resultSet.getString(3); 
     toBeRemovedHostname = resultSet.getString(5); 

     // selecting the duplicate entries with logmessage , id and hostname 
     sql = new StringBuilder("SELECT * FROM " 
           + properties.getProperty(PROPERTY_NAME_DB_TABLENAME) 
           + " where log_message=? and id <> ? and hostname = ?" ); 

     findStatement = connection.prepareStatement(sql.toString()); 
     findStatement.setString(1, toBeRemovedLog); 
     findStatement.setString(2, toBeRemovedIndex); 
     findStatement.setString(3, toBeRemovedHostname); 

     findResultSet = findStatement.executeQuery(); 
     String newId=""; 
     while(findResultSet.next()) { 
      newId = findResultSet.getString(1); 
      updateNumEntries +=findResultSet.getInt(2); 
      updateDB = true; 
     } 

     if(updateDB) { // if duplicate entry found - deleting it from the db 
      sql = new StringBuilder("DELETE FROM" 
           + properties.getProperty(PROPERTY_NAME_DB_TABLENAME) 
           + " where id = ?"); 
      updateStatement = connection.prepareStatement(sql.toString()); 
      updateStatement.setString(1, toBeRemovedIndex ); 
      updateStatement.executeUpdate(); 

      // updating similar entry with number of records 
      sql = new StringBuilder("Update " 
           + properties.getProperty(PROPERTY_NAME_DB_TABLENAME) 
           + " set number_of_entries = ? where id = ? "); 

      updateStatement = connection.prepareStatement(sql.toString()); 
      updateStatement.setLong(1, updateNumEntries); 
      updateStatement.setString(2, newId); 
      updateStatement.executeUpdate(); 
      updateDB = false; 
      updateStatement=null; 
     } 
     } 
    } catch (SQLException e) { 
     System.out.println("Connection Failed! Check output console"); 
     e.printStackTrace(); 
     return; 
    } 

    DateTime now = new DateTime(); 

    if(connection != null) { 
     try { 
     connection.close(); 
     } catch (SQLException e) { 
     e.printStackTrace(); 
     } 
    } 

    System.out.println("Current time = " 
         + now.toString() 
         + " DB consoladiation done successfully between " 
         + t1.toString() + " and "+ t2.toString()); 
    } 
} 

表構造:

CREATE TABLE IF NOT EXISTS `syslog_parsed` (
    `id` char(36) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, 
    `number_of_entries` int(11) NOT NULL, 
    `log_message` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, 
    `last_seen` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 
    `hostname` varchar(200) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, 
    KEY `ID` (`id`) 
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1; 
+0

ステートメントと結果セットを不要にしたときに、ステートメントと結果セットを「クローズ」すると、違いがあるかどうか試してください。 – Thilo

+0

下の私の回答を参照してください。うまくいけば、それは正しい方向に向かっています。 –

答えて

2

あなたの基本的な問題は、あなたがループしながら、入れ子になっているということです。内部ループは、100k項目の表に対してSQL問合せを実行します。これは、100,000のSQL呼び出しを含む100,000^2の操作のオーダーがあることを意味します。さらに、あなたの呼び出しは閉じられていないので、Javaは結果セットをメモリに保持します。つまり、heapsizeエラーです。

あなたのコードの多くは、過剰なものであり、SQL文で省略することができます。 (これ以上のコードが唯一の重複に対処する必要がありますように、これは、はるかに高速に実行する必要があり

String sql = "SELECT id FROM (" 
      + " SELECT id, log_message, hostname FROM syslog_parsed " 
      + " WHERE last_seen BETWEEN ? AND ? " 
      + " GROUP BY id, log_message, hostname " 
      + ") x "//creates a subtable with grouped items 
      + "GROUP BY id " 
      + "HAVING count(*) > 1"//only returns rows that appear more than once 

PreparedStatement stmt = connection.prepareStatement(sql.toString()); 
stmt.setTimestamp(1, t1); 
stmt.setTimestamp(2, t2); 
resultSet = stmt.executeQuery(); 

while (resultSet.next()) { 
    //Copy result 
    //Delete all of that result 
    //Add back in one such result 
} 

:これはかなり右ではないかもしれないので、あなたのデータベースに対してそれをテストする必要があり、それはあなたの重要な要素を提供しますおそらく50kレコードよりはるかに少ないかもしれません)。これは重複をSQLのものにする作業を軽減します。これはこの種のタスクでは非常に優れています。

将来の注意:あなたのコードを書式化してください。そうでなければ、読みにくいです。また、コードが実際に重要なものに焦点を当ててみることもできます。

+0

ポインタのためにありがとう - 脳の凍結 - 私はそれが私の心に来ているはずです - 私はそれがJDBCの不適切なプログラミングのためだと思った :) 私はそれを試してみようとあなたに連絡してください教えてください –

+0

問題。あなたがしなければ答えを受け入れてください! ;-) –