使用 50 个并行 SQLPLUS 会话和批量收集 LIMIT 和 FORALL 使用 500+ 百万条记录更新 Table

UPDATE Table with 500+ Millions of Records using 50 Parallel SQLPLUS Sessions with Bulk Collect LIMIT and FORALL

我是 Oracle 的新手,我试图提高更新巨大 table(500+ 百万条记录)的性能 - 无论我尝试多少,它都会降低性能。

我正在尝试使用 50 个并行 SQLPLUS 会话以及批量收集 LIMIT 和 FORALL 来更新具有 5 亿条记录的 Table - 这会花费大量时间(已用:04:32:30 .29).

  CURSOR NIT_CURSOR IS
  SELECT SA_ROWID, NEW_TRX_1, NEW_TRX_2
  FROM NIT_SA
  WHERE MOD(DBMS_ROWID.rowid_block_number(SA_ROWID),&2) = &1;
Note: &2 is 50 where as &1 is number of instance invoked from shell between (0-49), 
SA_ROWID is acutal rowid from SA Table

现在UPDATE语句如下:

  C_COMMIT         CONSTANT PLS_INTEGER := 10000;
  FETCH NIT_CURSOR BULK COLLECT INTO BLK_NIT_SA LIMIT C_COMMIT;     

  FORALL indx IN 1 .. BLK_NIT_SA.COUNT
  UPDATE SA
  SET INS_TRX_ID = BLK_NIT_SA(indx).NEW_TRX_1,
      UPD_TRX_ID = BLK_NIT_SA(indx).NEW_TRX_2
  WHERE ROWID = BLK_NIT_SA(indx).SA_ROWID;

KSH 在 50 个并行 SQLPLUS 会话中调用此脚本:

typeset -A sqlFile
sqlFile[2.3.sql]=50
for counter_i in "${!sqlFile[@]}"
do
  for counter_j in {0..$((${sqlFile[$counter_i]}-1))}
  do
     sqlplus -s ${DATABASE_CONNECT} @${SQLFILE} "${SPOOLFILE}" ${sqlFile[$counter_i]} $counter_j &
  done
done

据我了解,由于我直接使用 table SA 的 rowid,这将是访问行的最快方式,而 50 SQL Session 实际上应该处理数据很多更快,根据我的观察,很快我将 SQL 会话的并行进程数从 50 增加到 100,每个进程的更新时间增加了 2 小时,即从 4.5 小时增加到 7.5 小时。

我打算在 1.5 到 2 小时内完成。不确定它是否现实。

有人可以帮我解决以上问题吗?

将 PL/SQL 转换为单个 SQL 语句并使用语句并行性应该会显着提高性能:

alter session enable parallel dml;

merge /*+ parallel(50) */ into sa using
(
    select sa_rowid, new_trx_1, new_trx_2
    from nit_sa
) nit_sa
on (sa.rowid = nit_sa.sa_rowid)
when matched then update set
    sa.ins_trx_id = nit_sa.new_trx_1,
    sa.upd_trx_id = nit_sa.new_trx_2;

以上语句只会从table中读取一次。 Oracle 会自动将 table 划分为颗粒,无需手动对 table 进行分区。

并行在理论上很容易 - 只需添加一个提示,运行 时间就可以提高一个数量级。但在实践中做到正确可能很棘手。并行性需要企业版、足够的资源、合理的配置等。并且 运行ning 并行 DML 将锁定整个 table 直到语句完成 - 没有其他 DML 可以 运行 table同时。 (虽然其他进程仍然可以从 table 中读取。)

500+ 万条记录不是一个好主意,你可以告诉 table 的结构,以及需要更新哪些列。我们可以考虑以下方案:

  1. 如果 table 上需要的新值可以动态计算,那么我们可以在 table 之上创建一个视图,这样无论何时读取视图,我们都可以得到需要的值。

  2. 创建新的 table 并根据需要插入现有 table 中的所有数据(这些列具有新值)并删除旧的 table,重命名新的