Эй, мне действительно нужна помощь, и я очень расстроен.
Я работаю над этим проектом, который посвящен переносу данных из одной базы данных в другую, как в Oracle. Что я делаю, это чтобы получить таблицу из исходной базы данных и создать таблицу в целевой базе данных. А затем извлечь данные из исходной таблицы для пакетной вставки в целевую базу данных.
Даем немного больше деталей;
Я делаю это параллельно, получая 16 диапазонов строк из исходной таблицы и распределяя запросы на выборку для копирования данных на 8 серверов, по 2 на каждый. (На самом деле на тестовых серверах я распределяю по 4 машины, по 4 на каждую). И тогда я выбираю данные для каждого потока для вставки в таблицу.
Наличие главного модуля, работающего на одном сервере, который создает целевую таблицу и определяет диапазоны строк для отправки задач подчиненным модулям, работающим на других машинах. Но для некоторых таблиц это работает очень медленно. И я не знаю почему. Теперь у меня есть две таблицы, одна делается очень быстро. И еще один резко медленный. И из той же базы данных и того же табличного пространства и файла данных, в другую базу данных, того же табличного пространства и того же файла данных.
Тот, который я могу быстро скопировать размером 13936,4375 МБ с 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
);
И тот, который занимает слишком много времени, занимает 11519 МБ, около 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% времени процессора тратится на метод executeBatch, более конкретно oracle.net.ns.Packet.receive ( ) в поддереве executeBatch. Для быстрого он все еще занимает больше всего времени, но составляет около 45%.
Я комментирую механизм записи и просто выполняю метод getObject, и он завершает загрузку таблиц за 100 секунд. Так что чтение из источника db не похоже на проблему для меня.
Так что я думал, что БД занимает слишком много времени для выполнения пакетной вставки. Я думал, что это становится медленнее, когда увеличивается число столбцов, и я выбрал только 20 столбцов из медленно копируемой таблицы, он все еще был медленным и зависал на executeBatch. Затем я подумал, что это потому, что я не использовал надлежащий метод getXXX () для типов столбцов и изменил код соответствующим образом с getObject на любой метод get, который был необходим. Но все же он был слишком медленным.
Тогда я подумал, что БД занимает слишком много времени, чтобы выделить новое пространство. Поэтому я создал таблицу с начальным экстентом 15 ГБ, чтобы быть уверенным, что он не будет тратить время на выделение пространства при выполнении пакета. Опять же, это не сработало.
И всякий раз, когда таблица копируется слишком медленно, я вижу, что загрузка ЦП на сервере, на котором я запускаю подчиненные модули (код, выполняющий фактическое копирование данных, который я разместил ниже), действительно низкая. И для тех это быстро, много использования процессора.
Я пробовал с разными размерами партий и размерами выборки, но не очень помог.
Так может кто-нибудь сказать мне, что я здесь делаю не так? Я копирую ту же таблицу с помощью необычного инструмента 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();
}
Целевое соединение - автоматическая фиксация false
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);
}
}
}