大对象类型的奇怪行为

时间:2010-04-15 18:54:54

标签: sql performance oracle plsql object-type

我认识到,当实例变大时,在Oracle对象类型上调用方法需要更长的时间。

下面的代码只是将行添加到存储在对象类型中的集合中,并在循环中调用 dummy - 过程。

当集合中有更多行时,调用会花费更长时间。当我删除对dummy的调用时,性能好多了(该集合仍包含相同数量的记录):

Calling dummy:               Not calling dummy:
11                           0
81                           0
158                          0

重现的代码:

Create Type t_tab Is Table Of VARCHAR2(10000);

Create Type test_type As Object(
  tab t_tab,
  Member Procedure dummy
);

Create Type Body test_type As
  Member Procedure dummy As Begin
    Null;  --# Do nothing
  End dummy;
End;


Declare
  v_test_type  test_type := New test_type( New t_tab() );

  Procedure run_test As
    start_time  NUMBER := dbms_utility.get_time;
  Begin
    For i In 1 .. 200 Loop
      v_test_Type.tab.Extend;
      v_test_Type.tab(v_test_Type.tab.Last) := Lpad(' ', 10000);
      v_test_Type.dummy();  --# Removed this line in second test
    End Loop;
    dbms_output.put_line( dbms_utility.get_time - start_time );
  End run_test;

Begin
  run_test;
  run_test;
  run_test;
End;

我尝试了 10g 11g 任何人都可以解释/重现这种行为吗?

2 个答案:

答案 0 :(得分:2)

我可以在我的11.1.0.7数据库上重现该行为。我不确定我有解释,但我确实有一个理论。

如果将Extend调用移到循环外部,只需向集合中添加200个元素,性能下降就会消失(见下文)。这让我相信它不仅仅是调用对象方法的行为 - 这似乎与集合的低效扩展有1次200次而不是200次元素的一次互动。

SQL> ed
Wrote file afiedt.buf

  1  Declare
  2    v_test_type  test_type := New test_type( New t_tab() );
  3    Procedure run_test As
  4      start_time  NUMBER := dbms_utility.get_time;
  5    Begin
  6      v_test_Type.tab.Extend(200);
  7      For i In 1 .. 200 Loop
  8        v_test_Type.tab(v_test_Type.tab.Last) := Lpad(' ', 10000);
  9        v_test_Type.dummy();  --# Removed this line in second test
 10      End Loop;
 11      dbms_output.put_line( dbms_utility.get_time - start_time );
 12    End run_test;
 13  Begin
 14    run_test;
 15    run_test;
 16    run_test;
 17* End;
SQL> /
11
9
10

PL/SQL procedure successfully completed.

在这里推测,但是如果对过程的调用可能会修改集合,那么编译器可能会对扩展集合的扩展集合进行一些优化,而这些集合不能(或不会)生成。

作为对该推测的快速测试,我创建了一个成员函数而不是成员过程,并在循环中调用了该函数。由于函数不会修改对象状态,因此它们不会排除我推测的那种优化。果然,如果我使用成员函数创建对象类型,性能下降就会消失

SQL> ed
Wrote file afiedt.buf

  1  Create or replace Type test_type As Object(
  2    tab t_tab,
  3    Member Procedure dummy,
  4    Member Function dummy2 return number
  5* );
SQL> /

Type created.

SQL> ed
Wrote file afiedt.buf

  1  Create or replace Type Body test_type As
  2    Member Procedure dummy As Begin
  3      Null;  --# Do nothing
  4    End dummy;
  5    Member Function dummy2
  6      return number
  7    Is
  8    Begin
  9      Return 1;
 10    End dummy2;
 11* End;
 12  /

Type body created.

SQL> ed
Wrote file afiedt.buf

  1  Declare
  2    v_test_type  test_type := New test_type( New t_tab() );
  3    Procedure run_test As
  4      start_time  NUMBER := dbms_utility.get_time;
  5      l_num       NUMBER;
  6    Begin
  7      For i In 1 .. 200 Loop
  8        v_test_Type.tab.Extend;
  9        v_test_Type.tab(v_test_Type.tab.Last) := Lpad(' ', 10000);
 10        l_num := v_test_Type.dummy2();  --# Removed this line in second test
 11      End Loop;
 12      dbms_output.put_line( dbms_utility.get_time - start_time );
 13    End run_test;
 14  Begin
 15    run_test;
 16    run_test;
 17    run_test;
 18* End;
 19  /
11
9
9

PL/SQL procedure successfully completed.

最后,我认为有问题的语句是Extend,但是如果循环中没有任何东西能够修改对象,那么优化器足够聪明,能够避免惩罚。

答案 1 :(得分:1)

我自己发现,问题在Using SELF IN OUT NOCOPY with Member Procedures中描述:

  

在成员程序中,如果未声明SELF,则其参数模式默认为IN OUT

因此,对于每个过程调用,我的整个对象被复制了两次,随着大小的增加,这需要更长时间。


解决方案是使用SELF IN OUT NOCOPY test_type作为我的过程声明的第一个参数:

Create Type test_type As Object(
  tab t_tab,
  Member Procedure dummy(SELF IN OUT NOCOPY test_type)
);

仍然在没有参数的情况下调用

v_test_type.dummy();

表现恢复正常:

0
0
0