【问题标题】:Oracle direct-load INSERTs through JDBC?Oracle 通过 JDBC 直接加载 INSERT?
【发布时间】:2011-07-08 08:02:46
【问题描述】:

是否可以通过 JDBC 在 Oracle 中直接加载 INSERT?

我目前使用批处理的预处理语句(通过 Spring JDBC),有什么方法可以绕过 NOLOGGING 表上的重做日志?

这适用于 Oracle 11g。

【问题讨论】:

    标签: oracle jdbc oracle11g


    【解决方案1】:

    直接路径插入只能在insert into x as select * from y 场景中进行。这可以使用jdbc来完成,没问题。这不能通过插入和值来完成。当数据库处于强制记录模式时,这也无法完成。大多数情况下,当备用数据库处于连接状态时,主数据库将处于强制记录模式。

    正如 Gary Myers 所提到的,从 11gR2 开始就有 APPEND_VALUES 提示。与“旧”附加提示一样,它只能用于批量插入。

    我希望这会有所帮助, 罗纳德。

    【讨论】:

    • 啊,我没有意识到这不适用于单个插入。
    • 另外不要忘记插入是按块写入的......它适用于 BULK 插入。
    【解决方案2】:

    在 11gR2 中引入了 APPEND_VALUES 提示,用于使用 INSERT...VALUES 进行直接路径插入。

    没有可用的 11gR2 实例来测试它是否适用于 JDBC batch inserts。不过值得一试。

    【讨论】:

    • 谢谢,这很有用;不幸的是我在 11.1
    • 您是否能够使用 APPEND_VALUES 提示触发直接路径?
    【解决方案3】:

    我能够将 APPEND_VALUES 提示与 Oracle 12c 和 JDBC 批处理一起使用。我通过 Oracle Enterprise manager 验证了直接路径插入,其中解释计划显示 Load As Select

    编辑:我不再参与该项目,但我尝试提出更多细节: 代码类似于:

        prepareTableForLargeInsert("TABLE_X")
        preparedStatement = conn.prepareStatement("INSERT /*+ APPEND_VALUES */ INTO TABLE_X(A, B) VALUES(?,?)");
        while(thereIsStuffToInsert()) {
          for (ThingToWrite entity : getBatch()) {
             int i = 1;
             preparedStatement.setLong(i++, entity.getA());
             preparedStatement.setString(i++, entity.getB());
             ...
           }
           preparedStatement.executeBatch();
           preparedStatement.clearParameters();
        }
      repairTableAfterLargeInsert("TABLE_X")
    
    

    需要验证是否真的使用了直接路径(表被锁定/在同一个 tx 中的常规插入失败/实际执行计划显示 Load As Select)

    方法 prepareTableForLargeInsertrepairTableAfterLargeInsert 正在调用存储过程。它们可能会有所帮助:

     PROCEDURE sp_before_large_insert (in_table_name   IN       VARCHAR2) AS
       BEGIN
    
              -- force parallel processing
              EXECUTE IMMEDIATE 'ALTER SESSION FORCE PARALLEL DML';
              EXECUTE IMMEDIATE 'ALTER SESSION FORCE PARALLEL QUERY';
              EXECUTE IMMEDIATE 'ALTER SESSION FORCE PARALLEL DDL';
    
              -- set table to NOLOGGING
              EXECUTE IMMEDIATE 'ALTER TABLE ' || in_table_name || ' NOLOGGING';
    
              -- disable all FK constraints referencing the table. all but those used for Partition by reference
              FOR cur IN (SELECT a.owner, a.constraint_name, a.table_name
                                      FROM all_cons_columns a
                                      JOIN all_constraints c ON a.owner = c.owner
                                                            AND a.constraint_name = c.constraint_name
                                      JOIN all_constraints c_pk ON c.r_owner = c_pk.owner
                                                               AND C.R_CONSTRAINT_NAME = C_PK.CONSTRAINT_NAME
                                     LEFT JOIN user_part_tables pt on pt.ref_ptn_constraint_name = c.constraint_name
                                     WHERE C.CONSTRAINT_TYPE = 'R'
                                     AND pt.ref_ptn_constraint_name IS NULL
                                     AND A.OWNER LIKE '%_OWNER'
                                     AND c_pk.table_name = in_table_name)
              LOOP
                  execute immediate 'ALTER TABLE "'||cur.owner||'"."'||cur.table_name||'" MODIFY CONSTRAINT "'||cur.constraint_name||'" DISABLE';
              END LOOP;
    
              -- disable FKs (but one used for Partition by reference), PK (unless referenced by enabled FK for partition reference) and UCs on table
              FOR c IN (select distinct rc.CONSTRAINT_NAME FROM user_constraints rc
                                        LEFT JOIN user_part_tables pt on pt.ref_ptn_constraint_name = rc.constraint_name
                                        LEFT JOIN user_constraints c_fk ON c_fk.R_CONSTRAINT_NAME = rc.CONSTRAINT_NAME AND c_fk.status = 'ENABLED'
                                        WHERE rc.owner like '%OWNER'
                                        AND pt.ref_ptn_constraint_name IS NULL
                                        AND c_fk.R_CONSTRAINT_NAME IS NULL
                                        AND rc.CONSTRAINT_TYPE IN ('R', 'U', 'P')
                                        AND rc.TABLE_NAME = in_table_name)
              LOOP
                    EXECUTE IMMEDIATE 'ALTER TABLE ' || in_table_name || ' DISABLE CONSTRAINT ' || c.CONSTRAINT_NAME;
              END LOOP;
    
    
              -- set unusable non-local non-unique indexes on table
              FOR c IN (select INDEX_NAME from all_indexes
                          where table_owner LIKE '%_OWNER'
                          and PARTITIONED = 'NO'
                          and UNIQUENESS = 'NONUNIQUE'
                          and STATUS = 'VALID'
                          and TABLE_NAME = in_table_name)
              LOOP
                    EXECUTE IMMEDIATE 'ALTER INDEX ' || c.index_name || ' UNUSABLE';
              END LOOP;
       END sp_before_large_insert;
    
     PROCEDURE sp_after_large_insert (in_table_name   IN       VARCHAR2) AS
       BEGIN
              -- rebuild disabled indexes on table
              FOR c IN (select INDEX_NAME from all_indexes
                                              where table_owner LIKE '%_OWNER'
                                              and STATUS = 'UNUSABLE'
                                              and TABLE_NAME = in_table_name)
              LOOP
                    EXECUTE IMMEDIATE 'ALTER INDEX ' || c.index_name || ' REBUILD PARALLEL NOLOGGING';
              END LOOP;
    
              -- enable FKs, PK and UCs on table
              FOR c IN (select CONSTRAINT_NAME, CONSTRAINT_TYPE
                       FROM user_constraints
                       WHERE owner like '%OWNER'
                       AND CONSTRAINT_TYPE IN ('R', 'U', 'P')
                       AND TABLE_NAME = in_table_name)
              LOOP
                     IF c.CONSTRAINT_TYPE = 'P' THEN
                           EXECUTE IMMEDIATE 'ALTER TABLE ' || in_table_name || ' ENABLE CONSTRAINT ' || c.CONSTRAINT_NAME || ' USING INDEX REVERSE';
                     ELSE
                           EXECUTE IMMEDIATE 'ALTER TABLE ' || in_table_name || ' ENABLE CONSTRAINT ' || c.CONSTRAINT_NAME;
                     END IF;
              END LOOP;
    
              -- enable FKs constraints on related tables
              FOR cur IN (select fk.owner, fk.constraint_name , fk.table_name
                from all_constraints fk, all_constraints pk
                 where fk.CONSTRAINT_TYPE = 'R' and
                       pk.owner LIKE '%_OWNER' and
                       fk.r_owner = pk.owner and
                       fk.R_CONSTRAINT_NAME = pk.CONSTRAINT_NAME and
                       pk.TABLE_NAME = in_table_name)
              LOOP
                 execute immediate 'ALTER TABLE "'||cur.owner||'"."'||cur.table_name||'" MODIFY CONSTRAINT "'||cur.constraint_name||'" ENABLE';
              END LOOP;
    
              -- set table to LOGGING
              EXECUTE IMMEDIATE 'ALTER TABLE ' || in_table_name || ' LOGGING';
    
              -- disable parallel processing
              EXECUTE IMMEDIATE 'ALTER SESSION DISABLE PARALLEL DML';
              EXECUTE IMMEDIATE 'ALTER SESSION DISABLE PARALLEL QUERY';
              EXECUTE IMMEDIATE 'ALTER SESSION DISABLE PARALLEL DDL';
    
              -- clean up indexes i.e. set logging and noparallel again
              FOR c IN (SELECT INDEX_NAME FROM ALL_INDEXES
                          WHERE (TRIM(DEGREE) > TO_CHAR(1) OR LOGGING = 'NO')
                          AND OWNER LIKE '%_OWNER'
                          AND TABLE_NAME = in_table_name)
              LOOP
                    EXECUTE IMMEDIATE 'ALTER INDEX ' || c.index_name || ' NOPARALLEL LOGGING';
              END LOOP;
       END sp_after_large_insert;
    

    我记得为禁用的 UC 重新创建定制索引时存在问题,例如因为丢失了它们是如何分区的信息(全局哈希分区)(使索引只是 UNUSABLE 不起作用)

    注意事项:

    • 当并行化到不同的线程时,每个线程最终都会在表锁上序列化,因此不会获得太多收益
    • 我们的表已分区。如果批量写入不同的分区,则会观察到插入速度变慢 - 好的是每批写入尽可能少的分区
    • 如果每个线程都写入自己的普通(临时?)表并最终将这些表“合并”到主表中,则可能实现显着加速 - 但从未尝试过

    【讨论】:

    • 您能详细说明一下吗?
    【解决方案4】:

    insert /*+ append */ into desttab select * from srctab 
    

    不能在 JDBC 中工作?

    【讨论】:

    • 可以,但是行的来源在数据库之外
    • @Ronnis,是的......我已经关注了这个线程。我在 11gr2 上,我可能会尝试 Gary 提到的 'append_value' 提示,但这对 Dmitri 没有帮助(除非他当然要升级)。有任何方法可以做很多事情来提高 JDBC 性能。但这不是问题所在。干杯。
    【解决方案5】:

    用途:

    INSERT /*+ APPEND_VALUES */ INTO table_name (column1, column2) values (?,?);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-03-12
      • 2017-12-08
      • 2018-06-17
      • 2016-02-09
      • 2020-07-08
      • 2010-10-22
      • 2014-11-07
      相关资源
      最近更新 更多