有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java Oracle到Postgres的数据传输

几年前,我编写了一个小实用程序,用于将数据从Oracle数据库移动到Postgres数据库。我使用Java和JDBC来实现这一点,因为我希望Java能够处理准备好的语句中使用的数据的数据格式,以进行插入。该实用工具的原始版本假定两个数据库中的表名和列名相同。后来的版本接受了一个映射文件来处理名称差异。这个实用程序在我的组织中大受欢迎,但不幸的是,它没有扩展。它的最大速度是每小时移动一百万行。我们现在有3000多万行的表,没有人愿意等待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");
    }
}

我不确定是否可以重构此方法以充分缩短传输时间。我认为这是一种逐栏的方法 根本无法扩展

有人能提出更好的方法吗?语言不是问题,我可以用任何能处理问题的东西来解决 工作理想情况下,我希望看到传输速率至少为每小时1000万条记录


共 (4) 个答案

  1. # 1 楼答案

    您检索的信息在该表的整个处理过程中都是不变的

    为了每个表只执行一次,您可以创建一个数据持有者,其中包含列名、列索引、列类型和hasColumn标志的成员。传递此数据对象的一个实例,而不是columnName,第一次对其进行初始化,并将该数据用于表的其余部分。像这样兑现数据将节省2*num_rows*num_cols检索元数据的调用

  2. # 2 楼答案

    这一行可能有问题:

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

    而是使用:

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

    在Oracle中,getObject(String)是O(n)——至少在10g上是这样。该方法看起来像是为结果集中的每一行的每一列调用的。您不应该每次调用都获取元数据。将与元数据相关的所有调用在每个查询中移动一次,并在移动结果集时将它们传递给此方法

  3. # 3 楼答案

    您可以尝试创建某种类型的类(可能是某种类型的数组)来保存有关结果集列和类型的信息,这些列和类型在处理给定的结果集时是不变的。然后从数组中提取值,而不是在每次需要时调用findColumn和getColumnType。这将大大减少对findColumn和getColumnType的调用,并有助于改进运行时

    祝你好运

  4. # 4 楼答案

    我建议使用DB提供的导出/导入工具。Oracle和PostgreSQL同时支持XML和CSV格式

    如果您想坚持使用JDBC,那么将列按与PreparedStatementINSERT查询中的值相同的顺序放置在SELECTSELECT查询中,只需执行以下操作,而不是整个if/switch块:

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

    但是,我并不期望这会大大提高性能。DB提供的导出/导入工具可以比Java中的导出/导入工具效率更高