嘿,我真的需要幫助,而且我很沮喪。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);
}
}
}
'insertScript'來自哪裏,它是否做任何明確(或實際上是隱式的)轉換?跳出的表中唯一的區別在於第二個表中的時間戳;他們是否存在於所有慢速的?另外,您是否考慮過使用數據泵通過數據庫鏈接進行傳輸,如果您希望通過API來控制數據庫鏈接? – 2012-03-03 22:49:51
嗯,我建立插入腳本動態。我所做的只是「插入目標表(cols)的值(?)」。我不做轉換。而且我從時間戳以外的那張慢速表中選擇了20列,而且它還是很慢。 我們將使它在不同的數據庫平臺之間複製數據。如果定義,我們也會進行數據屏蔽。這就是爲什麼我們要使用java,並將其複製到分佈式。 – Bren 2012-03-03 23:25:06
你在慢桌上有任何索引或約束嗎? – 2012-03-04 00:50:39