【问题标题】:Oracle Check Huge Insert ProgressOracle 检查巨大的插入进度
【发布时间】:2021-12-13 14:50:23
【问题描述】:

是否有可能监控巨大插入的进度? 假设我们有一个代码:

MERGE INTO huge_table ht
USING (
    SELECT column1, column2
    FROM another_huge_table 
) aht ON (ht.column1 = aht.column1)
WHEN NOT MATCHED THEN
INSERT (column1, column2)
VALUES (aht.column1, aht.column2);
COMMIT;

表可以包含数百万行,插入过程可能会持续数小时。是否有可能在 Oracle 中监控 DML 进度?

【问题讨论】:

  • 你可以试试v$session_longops。但它不会为您提供您正在寻找的确切内容
  • 我很确定答案是否定的。只要MERGE 正在运行,我们就不知道它考虑了多少行或完成/全部的比例是多少。如果我们想要这个,我们必须使用一个 PL/SQL 过程,在循环中插入行并报告进度或简单地每 n 行提交一次,这样我们就可以使用另一个会话并在那里计算写入的行数。跨度>
  • 如果您不介意性能受到影响,您可以将合并分块并循环通过 PL/SQL 合并块。然后在循环的底部记录有关进度的消息。但总的来说,让它运行可能会更好。
  • 您可以使用dbms_sql_monitor.report_sql_monitor来监控长时间运行的查询,它显示了SQL计划树中每个算子目前处理的行数和相应的资源使用情况。它还将帮助您调整您的陈述。如前所述,您还可以通过并行化提高性能,但我更喜欢使用dbms_parallel_execute 而不是自定义代码,它允许使用可自定义的块进行纯并行处理。

标签: oracle insert progress monitor dml


【解决方案1】:

简短回答:没有数据库引擎可以开箱即用地做到这一点。您需要了解,在开始执行之前,Oracle 不知道将合并多少行。如 cmets 中所述,一种选择是将查询拆分为 PLSQL 中的块,并使用日志记录方法来监视提交的行与所花费的时间。但这比运行普通的合并语句要慢得多,我能想到为什么值得这样做。

不过,这种方法可以以某种方式帮助您。我假设您的合并语句不是为了达到目的而并行运行的。 Oracle 提供v$session_longops 字典视图来监控长时间操作,定义为可能需要超过 6 秒的操作。但是,对于操作,您必须了解执行计划中的每个步骤。

让我们做一个 PoC

表格

SQL> create table test.huge_table ( c1 number, c2 varchar2(40) , c3 varchar2(40) ) ;

Table created.

SQL> create table another_huge_table ( c1 number, c2 varchar2(40) , c3 varchar2(40) ) ;

Table created.

让我们在目标表中插入 5M 行,在源表中插入 10m,这样我们就可以发出 merge 来插入 5M 行。

SQL> declare
  2  begin
  3  for i in 1 .. 5000000
  4  loop
  5     insert into test.huge_table
  6     values
  7     ( i ,
  8      lpad(dbms_random.string('A',1),round(dbms_random.value(20,30)),dbms_random.string('A',1)),
  9      lpad(dbms_random.string('A',1),round(dbms_random.value(20,30)),dbms_random.string('A',1))
 10     );
 11  end loop;
 12  commit;
 13* end;
SQL> /

PL/SQL procedure successfully completed.

SQL> exec dbms_stats.gather_table_stats ( ownname => 'TEST' , tabname => 'HUGE_TABLE' , block_sample => true ) ;

PL/SQL procedure successfully completed.

SQL> declare
  2  begin
  3  for i in 1 .. 10000000
  4  loop
  5     insert into test.another_huge_table
  6     values
  7     ( i ,
  8      lpad(dbms_random.string('A',1),round(dbms_random.value(20,30)),dbms_random.string('A',1)),
  9      lpad(dbms_random.string('A',1),round(dbms_random.value(20,30)),dbms_random.string('A',1))
 10     );
 11     if i = 5000000
 12     then
 13             commit ;
 14     end if;
 15  end loop;
 16  commit;
 17* end;
SQL> /

PL/SQL procedure successfully completed.

SQL> exec dbms_stats.gather_table_stats ( ownname => 'TEST' , tabname => 'ANOTHER_HUGE_TABLE' , block_sample => true ) ;

PL/SQL procedure successfully completed.

让我们验证计划

SQL> explain plan for
merge into test.huge_table t
using (
    select c1,c2,c3 from test.another_huge_table
) s
ON (t.c1 = s.c1)
when not matched then
insert (c1, c2, c3)
values ( s.c1, s.c2, s.c3 );  2    3    4    5    6    7    8    9

Explained.

SQL> select * from table(dbms_xplan.display());

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 4265949913

------------------------------------------------------------------------------------------------------
| Id  | Operation               | Name               | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | MERGE STATEMENT         |                    |    10M|  1096M|       | 67324   (1)| 00:00:06 |
|   1 |  MERGE                  | HUGE_TABLE         |       |       |       |            |          |
|   2 |   VIEW                  |                    |       |       |       |            |          |
|*  3 |    HASH JOIN RIGHT OUTER|                    |    10M|  1106M|   333M| 67324   (1)| 00:00:06 |
|   4 |     TABLE ACCESS FULL   | HUGE_TABLE         |  5000K|   276M|       |  8232   (1)| 00:00:01 |
|   5 |     TABLE ACCESS FULL   | ANOTHER_HUGE_TABLE |    10M|   553M|       | 16291   (1)| 00:00:02 |
------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("T"."C1"(+)="C1")

然后我们运行合并

SQL> merge into test.huge_table t
using (
    select c1,c2,c3 from test.another_huge_table
) s
ON (t.c1 = s.c1)
when not matched then
insert (c1, c2, c3)
values ( s.c1, s.c2, s.c3 );  2    3    4    5    6    7    8

5000000 rows merged.

Elapsed: 00:00:36.39

SQL> commit ;

Commit complete.

Elapsed: 00:00:00.01

在进程运行的同时,我可以获得有关步骤的信息

SQL> r
  1  select opname,
  2         target,
  3         round( ( sofar/totalwork ), 2 ) * 100 percentage_complete,
  4         start_time,
  5         ceil( time_remaining / 60 ) max_time_remaining_in_min,
  6         floor( elapsed_seconds / 60 ) time_spent_in_min
  7         from v$session_longops
  8*        where sofar != totalwork

OPNAME     TARGET          PERCENTAGE_COMPLETE START_TIM MAX_TIME_REMAINING_IN_MIN TIME_SPENT_IN_MIN
------------------- --------- ---------------------------------------------------
Table Scan TEST.ANOTHER_HUGE_TABLE          86 28-OCT-21                        1                 0


OPNAME     TARGET          PERCENTAGE_COMPLETE START_TIM MAX_TIME_REMAINING_IN_MIN TIME_SPENT_IN_MIN
------------------- --------- ---------------------------------------------------
Hash Join TEST.ANOTHER_HUGE_TABLE          42 28-OCT-21                        1                 0

OPNAME     TARGET          PERCENTAGE_COMPLETE START_TIM MAX_TIME_REMAINING_IN_MIN TIME_SPENT_IN_MIN
------------------- --------- ---------------------------------------------------
Hash Join TEST.ANOTHER_HUGE_TABLE          77 28-OCT-21                        1                 0

总结

如果 Oracle 认为这些步骤中的任何一个将花费超过 6 秒,它会将它们包含在 v$session_longops 中。您可以查询此 viev 以获取每一步的估计值,但这并不是整个 DML 将花费多少时间的真正度量。但这可能是一个好的开始。

另一种可能更好的方法是生成基线。您可以在 PLSQL 或任何其他语言中包含合并,包括收集开始时间、结束时间和合并行的日志记录方法。随着时间的推移和数十次执行,您可以应用统计数据并获得平均值和/或标准偏差。我正在使用类似的方法来生成这种类型的信息,我可以使用这些信息进行有根据的猜测,仅此而已。

希望这会对您有所帮助。

【讨论】:

  • 感谢罗伯托,提供了很好且内容丰富的答案
  • 不客气@AleksandrBeliavski ;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-20
相关资源
最近更新 更多