嘿,我真的需要帮助,我很沮丧。
我正在研究这个项目,即将数据从一个数据库迁移到另一个数据库,这两个都是Oracle。我正在做的是从源数据库中获取表并在目标数据库中创建表。然后从源表中获取数据以批量插入到目标数据库中。
提供更多细节;
我通过从源表获取16个rowid范围并将数据复制的选择查询分发到8个服务器,每个2个并行执行此操作。 (实际上在测试服务器上我分配到4台机器,每台4台)。然后我在每个线程中选择要插入表格的数据。
让主模块在一台服务器上工作,该服务器在目标上创建表并决定rowid范围,以将任务发送到在其他机器上工作的从模块。但对于某些表来说,它确实很慢。我不知道为什么。现在我有两张桌子,一张桌子很快就完成了。而另一个则非常缓慢。从相同的db和相同的表空间和数据文件,到另一个db,相同的表空间和相同的数据文件。
我可以快速复制13936.4375 MB的大小,107.910.833行,并在5分钟内结束。这是表格的ddl
-- 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
);
花费太多时间完成的那个是11519 MB大小,大约3700万行,并且需要超过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读取似乎不是问题所在。
所以我认为db需要太多时间来执行批量插入。我认为当列数增加时它会变慢,我只从缓慢复制的表中选择了20列,它仍然很慢并挂在executeBatch上。然后我认为这是因为我没有对列类型使用正确的getXXX()方法,并且我将代码从getObject适当地更改为任何get方法是必要的。但它仍然太慢了。
然后我认为db需要太多时间来分配新空间。所以我创建了一个初始范围为15gb的表,以确保它在执行批处理时不会花时间分配空间。然后它再次起作用。
每当表被复制得太慢时,我看到服务器上运行从模块的CPU活动(我在下面发布的实际数据副本的代码)非常低。对于快速,大量CPU使用率的那些。
我尝试了不同的批量大小和获取大小,但没有提供太多帮助。
所以有人能告诉我这里我做错了什么吗?我用一个花哨的etl工具复制同一个表,它可以在大约10分钟内快速完成工作。
显然是关于某些特定类型的表格。但我在这里找不到什么问题。我得到了最新的jdbc驱动程序(ojdbc6),以确保它不是问题,但它仍然是相同的。
我从源db
获取查询结果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);
}
}
}
答案 0 :(得分:1)
我解决了这个问题;
我所做的就是将setObject方法更改为适合每种数据类型的JDBC setXXX方法。就像我使用get方法一样
虽然,我不太明白究竟是什么问题。为什么只有一些表而不是其他表。它不能是时间戳数据类型,因为我选择了除时间戳之外的列。
如果有人能告诉我究竟是什么问题我真的会批评它。我假设这是因为目标数据库正在对某些数据类型进行一些转换(虽然是哪一种?)或者我在java端的内存中使数据更大并通过网络发送(使用setXXX方法可能会减小每条记录的大小?)这花了太多时间?这些只是假设。 (很可能是愚蠢的。)
无论如何,我很高兴我解决了这个问题。感谢所有回复的人。