Oracle для передачи данных Postgres - PullRequest
2 голосов
/ 23 сентября 2010

Пару лет назад я написал небольшую утилиту для перемещения данных из базы данных Oracle в базу данных Postgres.Я использовал Java и JDBC для этого, потому что я хотел, чтобы Java обрабатывала форматирование данных, используемых в подготовленном операторе для вставки.Исходная версия утилиты предполагала, что имена таблиц и столбцов были одинаковыми в обеих базах данных.Более поздние версии приняли файл сопоставления для обработки различий в именах.Эта утилита имела большой успех в моей организации, но, к сожалению, она не масштабировалась.Максимальная скорость составляет около миллиона строк в час.Теперь у нас есть таблицы с 30+ миллионами строк, и никто не готов ждать 30 часов для передачи своих данных.

Метод, приведенный ниже, является сердцем утилиты и причиной ее не масштабирования.Этот метод выполняется один раз для каждого столбца данных, поэтому он вызывается (num_rows * num_cols times).С профилировщиком я вижу, что этот метод потребляет 58% времени выполнения.Одни только вызовы getObject () и findColumn () составляют 53% времени выполнения!

    public void setPlaceholderValue ( int placeHolderNum, ResultSet rs, String oracleColumnName, PreparedStatement stmt ) throws Exception {

    int columnIndex = rs.findColumn(oracleColumnName) ;
    int columnType = rs.getMetaData().getColumnType(columnIndex) ;

    try{
        if ( rs.getObject(oracleColumnName) != null ){
            switch (columnType) {
                case Types.VARCHAR: stmt.setString(placeHolderNum,  rs.getString(columnIndex)); break;
                case Types.INTEGER:   stmt.setInt(placeHolderNum, rs.getInt(columnIndex)); break ;
                case Types.DATE:       stmt.setDate(placeHolderNum, rs.getDate(columnIndex)); break;
                case Types.FLOAT:      stmt.setFloat(placeHolderNum, rs.getFloat(columnIndex)); break ;
                case Types.NUMERIC:  stmt.setBigDecimal(placeHolderNum,rs.getBigDecimal(columnIndex)); break ;
                case Types.TIMESTAMP:      stmt.setTimestamp(placeHolderNum, rs.getTimestamp(columnIndex)); break ;
                default: throw new SQLException("The result set column type " +  rs.getMetaData().getColumnType(columnIndex) + " was not recognized. see the java.sql.Types class at http://java.sun.com/j2se/1.5.0/docs/api/ ");
            }
        } else {
            stmt.setNull(placeHolderNum, columnType);
        }
    } catch (SQLException e){
        System.out.println ("SQLException: " + e.getMessage() + " for record id=" + rs.getLong("id"));
        throw new SQLException("rethrow");
    }
}

Я не уверен, что смогу реорганизовать этот метод для достаточного сокращения времени передачи.Я думаю, что подход столбец за столбцом просто не масштабируется.

Кто-нибудь может предложить лучший способ сделать это?Язык не проблема, я могу сделать это с любым, что может справиться с работой.В идеале я хотел бы видеть скорость передачи не менее 10 миллионов записей в час.

Ответы [ 4 ]

5 голосов
/ 23 сентября 2010

Я бы предложил использовать предоставляемые БД инструменты экспорта / импорта.Oracle и PostgreSQL поддерживают форматы XML и CSV.

Если вы хотите придерживаться JDBC, поместите столбцы в запросе SELECT для ResultSet в том же порядке, что и значения вINSERT запрос PreparedStatement и просто сделайте следующее вместо всего этого блока if/switch:

preparedStatement.setObject(index, resultSet.getObject(index));

Однако я не ожидаю, что это значительно улучшит производительность.Предоставленные БД средства экспорта / импорта могут сделать это намного эффективнее, чем когда-либо в Java.

1 голос
/ 23 сентября 2010

Эта строка, вероятно, будет проблематичной:

if ( rs.getObject(oracleColumnName) != null ){

Вместо этого используйте:

if ( rs.getObject(columnIndex) != null ){

В Oracle getObject (String) имеет значение O (n) - по крайней мере для 10g это было,Этот метод выглядит так, как будто он вызывается для каждого столбца для каждой строки в наборе результатов.Вы не должны получать метаданные при каждом вызове.Переместите все вызовы, связанные с метаданными, чтобы они происходили один раз для каждого запроса, и передайте их этому методу при перемещении по набору результатов.

0 голосов
/ 23 сентября 2010

Информация, которую вы получаете, является постоянной для всей обработки этой таблицы.

Чтобы выполнить это только один раз для таблицы, вы можете создать держатель данных, содержащий элементы для имени столбца, индекса столбца, типа столбца и флага hasColumn.Передайте экземпляр этого объекта данных вместо columnName, инициализируйте его в первый раз и используйте данные для остальной части таблицы.Кэширование данных, как это, сэкономит вам 2*num_rows*num_cols звонков при получении метаданных.

0 голосов
/ 23 сентября 2010

Вы можете попытаться создать некоторый класс (возможно, массив некоторого разнообразия) для хранения информации о столбцах и типах набора результатов, которые являются инвариантными при обработке данного набора результатов. Затем извлекайте значения из массива, а не вызывайте findColumn и getColumnType каждый раз, когда они вам нужны. Это должно значительно сократить вызовы findColumn и getColumnType и помочь улучшить время выполнения.

Удачи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...