整合多个Oracle触发器。会有性能影响吗?

6
我们的大多数表都有一个BEFORE INSERT OR UPDATE触发器,以便在INSERT之前设置ID,并在INSERT OR UPDATE之前设置创建/修改用户/日期。
有一些表具有额外的触发器,这些触发器与先前的触发器分开,以使其意图更加清晰。此外,这些附加触发器可以独立于先前的触发器禁用,而先前的触发器永远不应该被禁用。
在大多数情况下,附加触发器也会在BEFORE INSERT OR UPDATE时触发,顺序无关紧要,因为它们涵盖了不同的列/用例。因此,它们通常可以合并为单个触发器。
是否有关于在表上调用1n触发器速度的研究?或对于单行插入/更新来说这是否非常无关紧要?换句话说,是否只有1个全局的SQL -> PL/SQL上下文切换,还是每个触发器将有1个上下文切换。

2
我怀疑最好的答案是让你测试这两个选项,看看哪一个始终更快。 - Jeffrey Kemp
@JeffreyKemp:是的,但在我进行这些测试之前,也许有人已经对这些东西进行了基准测试... - Lukas Eder
@JeffreyKemp:请查看我的答案以获取基准测试结果。在Oracle 11g中似乎没有任何区别。 - Lukas Eder
3个回答

5
我已经对这种情况进行了基准测试,并得出结论:添加一个触发器可能会导致显着的性能损失,很可能是由于 PL/SQL 上下文切换引起的。在我的基准测试中,这种损失可以达到 8 倍。然而,添加第二个“兼容”的触发器就不再有任何显著影响了。所谓“兼容”,是指两个触发器始终以相同的顺序在同一事件中触发。

因此,我得出结论:所有触发器最有可能只需要执行一次 SQL -> PL/SQL 上下文切换。


以下是基准测试过程:

创建表格

-- A typical table with primary key, creation/modification user/date, and 
-- other data columns
CREATE TABLE test(
  id number(38)    not null, -- pk
  uc varchar2(400) not null, -- creation user
  dc date          not null, -- creation date
  um varchar2(400),          -- modification user
  dm date,                   -- modification date
  data number(38)
);

... 和序列

CREATE SEQUENCE s_test;

典型的触发器设置ID、创建/修改用户和日期

CREATE OR REPLACE TRIGGER t_test BEFORE INSERT OR UPDATE
  ON test
  FOR EACH ROW
BEGIN
  IF inserting THEN
    SELECT s_test.nextval INTO :new.id FROM dual;

    :new.uc := USER;
    :new.dc := SYSDATE;
    :new.um := NULL;
    :new.dm := NULL;
  END IF;
  IF updating THEN
    :new.um := USER;
    :new.dm := SYSDATE;
    :new.uc := :old.uc;
    :new.dc := :old.dc;
  END IF;
END t_test;

插入1000、10000、100000条记录

declare
  procedure run (limit number) is
    t timestamp;
  begin
    t := systimestamp;

    insert into test (data)
    select level from dual connect by level < limit;

    dbms_output.put_line(to_char(systimestamp - t));

    rollback;
  end;
begin
  run(1000);
  run(10000);
  run(100000);
end;

结果

-- ------------------------------------
-- +000000000 00:00:00.086603000
-- +000000000 00:00:00.844333000
-- +000000000 00:00:08.429186000
-- ------------------------------------

另一个“兼容”的触发器(执行顺序无关)


(注:此处为标题,无需翻译)
CREATE OR REPLACE TRIGGER t_test_other BEFORE INSERT OR UPDATE
  ON test
  FOR EACH ROW
BEGIN
  :new.data := 42;
END t_test_other;

另一个测试脚本的结果

-- ------------------------------------
-- +000000000 00:00:00.088551000
-- +000000000 00:00:00.876028000
-- +000000000 00:00:08.731345000
-- ------------------------------------

禁用触发器

alter trigger t_test disable;
alter trigger t_test_other disable;

运行一个略微不同的测试脚本

declare
  procedure run (limit number) is
    t timestamp;
  begin
    t := systimestamp;

    insert into test (id, uc, dc, data)
    select s_test.nextval, user, sysdate, level from dual 
    connect by level < limit;

    dbms_output.put_line(to_char(systimestamp - t));

    rollback;
  end;
begin
  run(1000);
  run(10000);
  run(100000);
end;

结果

-- ------------------------------------
-- +000000000 00:00:00.012712000
-- +000000000 00:00:00.104903000
-- +000000000 00:00:01.043984000
-- ------------------------------------

1
我不确定这个测试非常可靠。我会比较两个相同的表格;一个有1个触发器执行10个动作,另一个表格有10个触发器分别执行这些相同的10个动作。2个触发器与1个触发器的性能差异可能太小以超过噪声。 - Jeffrey Kemp
@JeffreyKemp:这是一个很好的测试。然而,在我的情况下,我只是想知道是否执行2个触发器需要比执行1个触发器多花费 ~2倍的时间,但似乎并非如此... - Lukas Eder

1
我建议您调查在触发器中使用Sequence的影响-创建序列时,使用缓存值来种子化序列(CACHE指定数据库预分配和保留在内存中以加快访问速度的序列值的数量)。我已经遇到了与序列相关的远大于8的因素。无论如何,在比较触发器在上下文切换方面的影响时,应该消除测试中使用Sequence或在评估结果时考虑Sequence的使用。

有趣。我没有想到这里的问题可能是序列的问题... - Lukas Eder

1

我知道在没有触发器和单个触发器之间的重做生成方面存在显着差异,但不清楚在1个和n个触发器之间是否有差异。然而,我无法想象这里没有影响,因为SQL和PL/SQL引擎之间会有更多的上下文切换。


1
嗯,本质上,我想知道是否真的存在多个上下文切换,还是只有一个。原则上,可以应用一些优化来仅切换一次上下文。 - Lukas Eder
我现在已经进行了基准测试,并发现在插入具有1个或2个触发器的表时没有任何区别。有关详细信息,请参见我的答案。 - Lukas Eder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接