Пакетное обновление Oracle JDBC плохой производительности для некоторых таблиц - PullRequest
0 голосов
/ 04 марта 2012

Эй, мне действительно нужна помощь, и я очень расстроен.

Я работаю над этим проектом, который посвящен переносу данных из одной базы данных в другую, как в 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);
    }

}

}

1 Ответ

1 голос
/ 05 марта 2012

Я решил проблему;

Все, что я сделал, это изменил метод setObject на правильный метод setXXX JDBC для каждого типа данных. Просто то, как я использую методы get

Хотя я не сделалЯ не совсем понимаю, в чем именно проблема.И почему только для некоторых таблиц, а не для других.Это не может быть тип данных метки времени, так как я выбрал столбцы, отличные от меток времени.

Если кто-нибудь может сказать мне, в чем именно заключается проблема, я бы действительно оценил это.Я предполагаю, что это произошло из-за того, что целевая БД выполняла какое-то преобразование для некоторых типов данных (какой, хотя?) Или я увеличивала объем данных в памяти на стороне Java и отправляла их по сети (используя метод setXXX, возможно, уменьшая размер каждой записи?)и это занимало слишком много времени?То это всего лишь предположения.(Может быть, очень глупы.)

В любом случае, я счастлив, что решил проблему.Спасибо всем, кто ответил.

...