Oracle 检查巨大的插入进度
Oracle Check Huge Insert Progress
是否有可能监控大量插入的进度?
假设我们有一个代码:
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 进度?
简短回答:没有数据库引擎可以开箱即用。您需要了解,在开始执行之前,Oracle 并不知道将合并多少行。如评论中所述,一个选项是在 PLSQL 中将查询拆分为多个块,并使用日志记录方法来监控提交的行与花费的时间。但这比 运行 一个普通的合并语句要慢得多,我能想到为什么这样做是值得的。
不过,这种方法可以以某种方式帮助您。为此,我假设您的合并语句不是 运行ning 并行。 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.
让我们在目标 table 中插入 5M 行,在源 table 中插入 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 或任何其他语言中包含合并,包括用于收集开始时间、结束时间和合并行的日志记录方法。随着时间的推移和数十次执行,您可以应用统计数据并获得平均 and/or 标准偏差。我正在使用类似的方法来生成这种类型的信息,我可以用它来进行有根据的猜测,仅此而已。
希望这对你有所帮助。
是否有可能监控大量插入的进度? 假设我们有一个代码:
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 进度?
简短回答:没有数据库引擎可以开箱即用。您需要了解,在开始执行之前,Oracle 并不知道将合并多少行。如评论中所述,一个选项是在 PLSQL 中将查询拆分为多个块,并使用日志记录方法来监控提交的行与花费的时间。但这比 运行 一个普通的合并语句要慢得多,我能想到为什么这样做是值得的。
不过,这种方法可以以某种方式帮助您。为此,我假设您的合并语句不是 运行ning 并行。 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.
让我们在目标 table 中插入 5M 行,在源 table 中插入 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 或任何其他语言中包含合并,包括用于收集开始时间、结束时间和合并行的日志记录方法。随着时间的推移和数十次执行,您可以应用统计数据并获得平均 and/or 标准偏差。我正在使用类似的方法来生成这种类型的信息,我可以用它来进行有根据的猜测,仅此而已。
希望这对你有所帮助。