2012-03-03 68 views
0

嘿,我真的需要幫助,而且我很沮喪。Jdbc Oracle批量更新對某些表的性能不佳

我正在研究這個項目,這是關於將數據從一個數據庫遷移到另一個數據庫,都是Oracle。我正在做的是從源數據庫獲取表並在目標數據庫創建表。然後從源表中獲取數據以批量插入到目標數據庫中。

給予更多的細節;

我這樣做是通過從源表中獲取16個rowid範圍並將數據副本的選擇查詢分配給8個服務器,每個爲2個。 (實際上,我將測試服務器分配給4臺機器,每臺4臺)。然後在我選擇每個線程插入到表中的數據。

讓一個主模塊在一臺服務器上工作,該服務器在目標處創建表並決定將rowid範圍發送給在其他機器上工作的從模塊。但對於一些表格來說,它確實很慢。我不知道爲什麼。現在我有兩張桌子,一張桌子的速度非常快。另一個速度很慢。從相同的數據庫和相同的表空間和數據文件,到另一個數據庫,相同的表空間和相同的數據文件。

一個我可以快速複製13936.4375 MB的大小與107.910.833行,它在5分鐘內結束。這是該表

-- Create table 
create table CMP_SUBS_RESPONSE_INS 
(
    RESPONSE_TP_SK   NUMBER(16), 
    CHURN_REASON_TP_SK  NUMBER(16), 
    CONTRACT_SK    NUMBER(16), 
    OFFER_SK     NUMBER(16), 
    CHANNEL_SK    NUMBER(16), 
    CMP_RUN_SK    NUMBER(16), 
    CAMPAIGN_CELLS_SK   NUMBER(16), 
    SUBS_RESPONSE_SK   NUMBER(16), 
    SUBS_RESPONSE_NK   VARCHAR2(50), 
    SUBS_RESPONSE_NK2   NUMBER, 
    CRC_CALCULATION_FLAG  VARCHAR2(2), 
    SOURCE_SYSTEM_SK   NUMBER(16), 
    INITIAL_ETL_DATE   DATE, 
    MODIFICATION_SYSTIME  DATE, 
    UPDATE_ETL_DATE   DATE, 
    START_DATE    DATE, 
    END_DATE     DATE, 
    FULFILLMENT_FLAG   VARCHAR2(1), 
    CHURN_SUBREASON_TP_SK  NUMBER(16), 
    LMC_REASON_CATEGORY_SK NUMBER(16), 
    LMC_REASON_TIMEPERIOD_SK NUMBER(16), 
    LMC_REASON_PAYMENTTYPE_SK NUMBER(16) 
) 
tablespace USERS_BTS 
    pctfree 10 
    initrans 1 
    maxtrans 255 
    storage 
    (
    initial 64K 
    next 1M 
    minextents 1 
    maxextents unlimited 
);  

---target 

create table BR_TEST2 
(
    RESPONSE_TP_SK   NUMBER(16), 
    CHURN_REASON_TP_SK  NUMBER(16), 
    CONTRACT_SK    NUMBER(16), 
    OFFER_SK     NUMBER(16), 
    CHANNEL_SK    NUMBER(16), 
    CMP_RUN_SK    NUMBER(16), 
    CAMPAIGN_CELLS_SK   NUMBER(16), 
    SUBS_RESPONSE_SK   NUMBER(16), 
    SUBS_RESPONSE_NK   VARCHAR2(50), 
    SUBS_RESPONSE_NK2   NUMBER, 
    CRC_CALCULATION_FLAG  VARCHAR2(2), 
    SOURCE_SYSTEM_SK   NUMBER(16), 
    INITIAL_ETL_DATE   DATE, 
    MODIFICATION_SYSTIME  DATE, 
    UPDATE_ETL_DATE   DATE, 
    START_DATE    DATE, 
    END_DATE     DATE, 
    FULFILLMENT_FLAG   VARCHAR2(1), 
    CHURN_SUBREASON_TP_SK  NUMBER(16), 
    LMC_REASON_CATEGORY_SK NUMBER(16), 
    LMC_REASON_TIMEPERIOD_SK NUMBER(16), 
    LMC_REASON_PAYMENTTYPE_SK NUMBER(16) 
) 
tablespace PUB_A_BTS 
    pctfree 10 
    initrans 1 
    maxtrans 255 
    storage 
    (
    initial 64K 
    next 1M 
    minextents 1 
    maxextents unlimited 
); 

並呈tooo多的時間來完成一個DDL的大小11519 MB,其中約37百萬行,這需要超過2-3小時,也許更多(實際上我總是感到沮喪和死亡的過程,從未見過結尾)

create table DGG1 
(
    YEAR_MONTH    VARCHAR2(250), 
    SUBSCRIBER_ID   NUMBER, 
    EXT_DATE    TIMESTAMP(6), 
    CALC_MONTHS   VARCHAR2(250), 
    LAST_3_MONTH_FLAG  VARCHAR2(250), 
    AVEA1_YTL    NUMBER, 
    AVEA1_SAVING_YTL  NUMBER, 
    AVEA1_SAVING_PER  NUMBER, 
    AVEA2_YTL    NUMBER, 
    AVEA2_SAVING_YTL  NUMBER, 
    AVEA2_SAVING_PER  NUMBER, 
    AVEA3_YTL    NUMBER, 
    AVEA3_SAVING_YTL  NUMBER, 
    AVEA3_SAVING_PER  NUMBER, 
    AVEA4_YTL    NUMBER, 
    AVEA4_SAVING_YTL  NUMBER, 
    AVEA4_SAVING_PER  NUMBER, 
    AVEA5_YTL    NUMBER, 
    AVEA5_SAVING_YTL  NUMBER, 
    AVEA5_SAVING_PER  NUMBER, 
    AVEA6_YTL    NUMBER, 
    AVEA6_SAVING_YTL  NUMBER, 
    AVEA6_SAVING_PER  NUMBER, 
    AVEA7_YTL    NUMBER, 
    AVEA7_SAVING_YTL  NUMBER, 
    AVEA7_SAVING_PER  NUMBER, 
    AVEA8_YTL    NUMBER, 
    AVEA8_SAVING_YTL  NUMBER, 
    AVEA8_SAVING_PER  NUMBER, 
    AVEA9_YTL    NUMBER, 
    AVEA9_SAVING_YTL  NUMBER, 
    AVEA9_SAVING_PER  NUMBER, 
    AVEA10_YTL    NUMBER, 
    AVEA10_SAVING_YTL  NUMBER, 
    AVEA10_SAVING_PER  NUMBER, 
    TELSIM1_YTL   NUMBER, 
    TELSIM1_SAVING_YTL  NUMBER, 
    TELSIM1_SAVING_PER  NUMBER, 
    TELSIM2_YTL   NUMBER, 
    TELSIM2_SAVING_YTL  NUMBER, 
    TELSIM2_SAVING_PER  NUMBER, 
    TELSIM3_YTL   NUMBER, 
    TELSIM3_SAVING_YTL  NUMBER, 
    TELSIM3_SAVING_PER  NUMBER, 
    TELSIM4_YTL   NUMBER, 
    TELSIM4_SAVING_YTL  NUMBER, 
    TELSIM4_SAVING_PER  NUMBER, 
    TELSIM5_YTL   NUMBER, 
    TELSIM5_SAVING_YTL  NUMBER, 
    TELSIM5_SAVING_PER  NUMBER, 
    TELSIM6_YTL   NUMBER, 
    TELSIM6_SAVING_YTL  NUMBER, 
    TELSIM6_SAVING_PER  NUMBER, 
    TELSIM7_YTL   NUMBER, 
    TELSIM7_SAVING_YTL  NUMBER, 
    TELSIM7_SAVING_PER  NUMBER, 
    TELSIM8_YTL   NUMBER, 
    TELSIM8_SAVING_YTL  NUMBER, 
    TELSIM8_SAVING_PER  NUMBER, 
    TELSIM9_YTL   NUMBER, 
    TELSIM9_SAVING_YTL  NUMBER, 
    TELSIM9_SAVING_PER  NUMBER, 
    TELSIM10_YTL   NUMBER, 
    TELSIM10_SAVING_YTL NUMBER, 
    TELSIM10_SAVING_PER NUMBER, 
    ETT_DATE    TIMESTAMP(6), 
    RUN_ID     VARCHAR2(250), 
    CO_ID     NUMBER, 
    UNIQUE_PARTY_ID  NUMBER, 
    UNOPTIMISEABLE_CHARGES NUMBER, 
    OTHER_CHARGES   NUMBER 
) 
tablespace USERS_BTS 
    pctfree 10 
    initrans 1 
    maxtrans 255 
    storage 
    (
    initial 64k 
    next 1m 
    minextents 1 
    maxextents unlimited 
); 

----target 

create table dds_etl.br_test 
(
    YEAR_MONTH    VARCHAR2(250), 
    SUBSCRIBER_ID   NUMBER, 
    EXT_DATE    TIMESTAMP(6), 
    CALC_MONTHS   VARCHAR2(250), 
    LAST_3_MONTH_FLAG  VARCHAR2(250), 
    AVEA1_YTL    NUMBER, 
    AVEA1_SAVING_YTL  NUMBER, 
    AVEA1_SAVING_PER  NUMBER, 
    AVEA2_YTL    NUMBER, 
    AVEA2_SAVING_YTL  NUMBER, 
    AVEA2_SAVING_PER  NUMBER, 
    AVEA3_YTL    NUMBER, 
    AVEA3_SAVING_YTL  NUMBER, 
    AVEA3_SAVING_PER  NUMBER, 
    AVEA4_YTL    NUMBER, 
    AVEA4_SAVING_YTL  NUMBER, 
    AVEA4_SAVING_PER  NUMBER, 
    AVEA5_YTL    NUMBER, 
    AVEA5_SAVING_YTL  NUMBER, 
    AVEA5_SAVING_PER  NUMBER, 
    AVEA6_YTL    NUMBER, 
    AVEA6_SAVING_YTL  NUMBER, 
    AVEA6_SAVING_PER  NUMBER, 
    AVEA7_YTL    NUMBER, 
    AVEA7_SAVING_YTL  NUMBER, 
    AVEA7_SAVING_PER  NUMBER, 
    AVEA8_YTL    NUMBER, 
    AVEA8_SAVING_YTL  NUMBER, 
    AVEA8_SAVING_PER  NUMBER, 
    AVEA9_YTL    NUMBER, 
    AVEA9_SAVING_YTL  NUMBER, 
    AVEA9_SAVING_PER  NUMBER, 
    AVEA10_YTL    NUMBER, 
    AVEA10_SAVING_YTL  NUMBER, 
    AVEA10_SAVING_PER  NUMBER, 
    TELSIM1_YTL   NUMBER, 
    TELSIM1_SAVING_YTL  NUMBER, 
    TELSIM1_SAVING_PER  NUMBER, 
    TELSIM2_YTL   NUMBER, 
    TELSIM2_SAVING_YTL  NUMBER, 
    TELSIM2_SAVING_PER  NUMBER, 
    TELSIM3_YTL   NUMBER, 
    TELSIM3_SAVING_YTL  NUMBER, 
    TELSIM3_SAVING_PER  NUMBER, 
    TELSIM4_YTL   NUMBER, 
    TELSIM4_SAVING_YTL  NUMBER, 
    TELSIM4_SAVING_PER  NUMBER, 
    TELSIM5_YTL   NUMBER, 
    TELSIM5_SAVING_YTL  NUMBER, 
    TELSIM5_SAVING_PER  NUMBER, 
    TELSIM6_YTL   NUMBER, 
    TELSIM6_SAVING_YTL  NUMBER, 
    TELSIM6_SAVING_PER  NUMBER, 
    TELSIM7_YTL   NUMBER, 
    TELSIM7_SAVING_YTL  NUMBER, 
    TELSIM7_SAVING_PER  NUMBER, 
    TELSIM8_YTL   NUMBER, 
    TELSIM8_SAVING_YTL  NUMBER, 
    TELSIM8_SAVING_PER  NUMBER, 
    TELSIM9_YTL   NUMBER, 
    TELSIM9_SAVING_YTL  NUMBER, 
    TELSIM9_SAVING_PER  NUMBER, 
    TELSIM10_YTL   NUMBER, 
    TELSIM10_SAVING_YTL NUMBER, 
    TELSIM10_SAVING_PER NUMBER, 
    ETT_DATE    TIMESTAMP(6), 
    RUN_ID     VARCHAR2(250), 
    CO_ID     NUMBER, 
    UNIQUE_PARTY_ID  NUMBER, 
    UNOPTIMISEABLE_CHARGES NUMBER, 
    OTHER_CHARGES   NUMBER 
) 
tablespace PUB_A_BTS 
    pctfree 10 
    initrans 1 
    maxtrans 255 
    storage 
    (
    initial 64k 
    next 1m 
    minextents 1 
    maxextents unlimited 
); 

在任何表中沒有約束或索引。甚至沒有空的檢查,因爲你可以看到。

現在我編寫的java代碼在下面,我確實使用jvisualvm進行了採樣,而對於慢速的代碼,99%的cpu時間花費在executeBatch方法上,更具體地說是oracle.net.ns.Packet.receive ()方法在executeBatch的子樹中。對於快速的它仍然是最耗時的一點,但約45%。

我註釋掉了寫入機制,只是執行getObject方法,並且它在100秒內結束加載表。所以從源數據庫中讀取似乎不是問題出在哪裏。

所以我認爲db需要太多時間來執行批量插入。當列數增加時,我認爲它會變慢,並且我只從緩慢複製的表中選擇了20列,但它仍然很慢並掛在executeBatch上。然後我認爲這是因爲我沒有使用正確的getXXX()方法來處理列類型,並且將代碼從getObject中適當地更改爲get方法所需的任何方法。但仍然太慢了。

然後我認爲db需要太多的時間來分配新的空間。所以我創建了15GB的初始表,以確保在執行批處理時不會花時間分配空間。然後再次沒有奏效。

而且每當一個表得到複製太慢,我看到服務器上的CPU活動我正在運行從模塊(執行實際數據副本的代碼,我張貼在下面)真的很低。而對於那些速度很快,CPU使用率很高的用戶。

我嘗試了不同的批量和抓取大小,沒有做太多的幫助。

那麼誰能告訴我我在這裏做錯了什麼?我用一個漂亮的工具複製同一張桌子,它在大約10分鐘內快速完成工作。

這顯然是關於某些特定類型的表。但我在這裏找不到問題。我得到了最新的jdbc驅動程序(ojdbc6),以確保它不是問題,但它仍然是一樣的。

我得到的查詢結果從源數據庫

public ResultSet getQueryResultset(Connection con, String query) throws SQLException { 
     OraclePreparedStatement preparedStatement = null; 
     preparedStatement = con.prepareStatement(query); 
     preparedStatement.setRowPrefetch(DTSConstants.FETCH_SIZE);//1000 
     return preparedStatement.executeQuery(); 
    } 

目標連接是自動提交虛假

targetConnection.setAutoCommit(false); 

我如何準備插入

OraclePreparedStatement preparedStatement = null; 
preparedStatement = connection.prepareStatement(insertScript); 

,並在這裏,我將數據寫入目標

private int write(ResultSet resultSet, OraclePreparedStatement preparedStatement, long taskID) throws SQLException, 
      DTSException, MLException, ParseException { 
     int statementCounter = 0; 
     int rowsAffected = 0; 
     int columnCount = columnNames.length; 
     while (resultSet.next()) { 
      setColumnsAndAddBatch(resultSet, preparedStatement, columnCount); 
      statementCounter++; 
      rowsAffected++; 
      if (statementCounter >= DTSConstants.BATCH_SIZE) { /1000 
       preparedStatement.executeBatch(); 
       statementCounter = 0; 
       controllerUtil.performTaskSanityCheck(taskID); 
      } 
     } 
     preparedStatement.executeBatch(); 
     return rowsAffected; 
    } 


private void setColumnsAndAddBatch(ResultSet resultSet, OraclePreparedStatement preparedStatement, int columnCount) 
      throws SQLException, MLException, ParseException { 
     for (int i = 0; i < columnCount; i++) { 
      Object object = resultSet.getObject(i + 1);//Changed this to Object object = OracleDataHandler.getData(resultSet, i + 1, columntypes[i]); 

      if (maskingLibGateway != null) { 
       String columnName = columnNames[i]; 
       if (maskFields.containsKey(columnName) == true) { // never true for my examples, so the method never gets called 
        object = maskObject(object, columnName); 
       } 
      } 
      preparedStatement.setObject(i + 1, object); 
     } 
     preparedStatement.addBatch(); 
    } 

使用這個類來決定getXXX方法

公共類OracleDataHandler {

public static Object getData(ResultSet resultSet, int columnIndex, int columnType) throws SQLException { 

    switch (columnType) { 
    case Types.NUMERIC: 
    case Types.DECIMAL: 
     return resultSet.getBigDecimal(columnIndex); 
    case Types.CHAR: 
    case Types.VARCHAR: 
    case Types.LONGNVARCHAR: 
     return resultSet.getString(columnIndex); 
    case Types.INTEGER: 
     return resultSet.getInt(columnIndex); 
    case Types.DATE: 
     return resultSet.getDate(columnIndex); 
    case Types.TIMESTAMP: 
     return resultSet.getTimestamp(columnIndex); 
    case Types.TIME: 
     return resultSet.getTime(columnIndex); 
    case Types.BIGINT: 
     return resultSet.getLong(columnIndex); 
    case Types.DOUBLE: 
    case Types.FLOAT: 
     return resultSet.getDouble(columnIndex); 
    case Types.SMALLINT: 
     return resultSet.getShort(columnIndex); 
    case Types.TINYINT: 
     return resultSet.getByte(columnIndex); 
    case Types.BINARY: 
    case Types.VARBINARY: 
     return resultSet.getBytes(columnIndex); 
    case Types.CLOB: 
     return resultSet.getClob(columnIndex); 
    case Types.ARRAY: 
     return resultSet.getArray(columnIndex); 
    case Types.BLOB: 
     return resultSet.getBlob(columnIndex); 
    case Types.REAL: 
     return resultSet.getFloat(columnIndex); 
    case Types.BIT: 
    case Types.BOOLEAN: 
     return resultSet.getBoolean(columnIndex); 
    case Types.REF: 
     return resultSet.getRef(columnIndex); 
    case Types.DATALINK: 
     return resultSet.getURL(columnIndex); 
    case Types.LONGVARBINARY: 
     return resultSet.getBinaryStream(columnIndex); 
    default: 
     return resultSet.getObject(columnIndex); 
    } 

} 

}

+1

'insertScript'來自哪裏,它是否做任何明確(或實際上是隱式的)轉換?跳出的表中唯一的區別在於第二個表中的時間戳;他們是否存在於所有慢速的?另外,您是否考慮過使用數據泵通過數據庫鏈接進行傳輸,如果您希望通過API來控制數據庫鏈接? – 2012-03-03 22:49:51

+0

嗯,我建立插入腳本動態。我所做的只是「插入目標表(cols)的值(?)」。我不做轉換。而且我從時間戳以外的那張慢速表中選擇了20列,而且它還是很慢。 我們將使它在不同的數據庫平臺之間複製數據。如果定義,我們也會進行數據屏蔽。這就是爲什麼我們要使用java,並將其複製到分佈式。 – Bren 2012-03-03 23:25:06

+0

你在慢桌上有任何索引或約束嗎? – 2012-03-04 00:50:39

回答

0

我解決了這個問題;

我所做的是改變setObject方法,以適當的JDBC的setXXX方法type.Just我使用get方法

雖然每路數據,我不太明白究竟是問題。爲什麼只有一些表格而不是其他的。它不能是時間戳數據類型,因爲我選擇了除時間戳以外的列。

如果任何人都可以告訴我究竟是什麼問題,我會真正appricate。我認爲這是因爲目標數據庫正在做一些數據類型的轉換(哪一個呢?)或者我在java端更大的內存數據並通過網絡發送(使用setXXX方法可能會減小每個記錄的大小?)它花費了太多時間?這些只是假設。 (不太可能是愚蠢的。)

無論如何,我很高興我解決了這個問題。感謝所有回覆的人。

+0

我還不知道,我現在有同樣的問題,事實是,這還沒有解決。我正面臨一個市長sh * t在這裏,想象一下,有10個簡單字段沒有限制的表格,我使用一個薄連接到數據庫,自動提交關閉,準備好聲明,設置參數,增加批處理和地獄,51秒運行時間爲25,000個數據...在數據庫方面,我們沒有看到瓶頸,那裏沒有問題。另外,我注意到一個巨大的轉換量,或者在「真正的交易」之前將跟蹤級別設置爲FINEST for ojdbc。 – newhouse 2012-04-24 12:37:46

+0

「oracle.jdbc.driver.DBConversion javaCharsToCHARBytes」數千次。另外,如果我使用jar的_g調試版本,它會使運行時間減半,但如果我不追蹤FINEST,它會使SLOWER運行。我現在只是憤怒。 – newhouse 2012-04-24 12:41:44

+0

@ n3whous3:數據庫服務器或機器運行Java可能是一個緩慢的?通過數據庫鏈接複製數據需要多少時間? 或者也許還有其他的代碼會導致瓶頸,你試過使用Jvisuamvm或類似的東西來檢查CPU花費在哪裏? – Bren 2012-04-30 07:18:31