合并多个 Oracle 触发器。任何性能影响?

Posted

技术标签:

【中文标题】合并多个 Oracle 触发器。任何性能影响?【英文标题】:Consolidate several Oracle triggers. Any performance impact? 【发布时间】:2012-01-24 15:22:12 【问题描述】:

我们的大多数表都有一个BEFORE INSERT OR UPDATE 触发器,用于设置 ID 的BEFORE INSERT 并设置创建/修改用户/日期BEFORE INSERT OR UPDATE

有几个表带有额外的触发器,它们与前一个分开,以使它们的意图更加清晰。此外,这些额外的触发器可以独立于前一个触发器被禁用,而前一个触发器永远不应该被禁用。

在大多数情况下,附加触发器也会触发BEFORE INSERT OR UPDATE,并且顺序并不重要,因为它们涵盖了不同的列/用例。所以一般来说,它们可以组合成单个触发器。

有没有关于在桌子上调用1n 触发器的速度的研究?或者这与单行插入/更新几乎无关?换句话说,是否只有 1 个全局 SQL -> PL/SQL 上下文切换,或者每个触发器会有 1 个上下文切换。

【问题讨论】:

我怀疑最好的答案是让您测试这两个选项,看看其中一个是否始终更快。 @JeffreyKemp:是的,但在我进行这些测试之前,也许有人已经对这些东西进行了基准测试...... @JeffreyKemp:请参阅我的基准测试答案。 Oracle 11g 好像没有区别 【参考方案1】:

我知道没有触发器和单个触发器之间的重做生成存在显着差异,但不是关于 1 和 n 触发器之间的差异。但是,我无法想象没有命中,因为在 SQL 和 PL/SQL 引擎之间会有更多的上下文切换。

【讨论】:

嗯,本质上,我想知道是否真的有多个上下文切换,或者只有一个。原则上,可以应用一些优化以便只切换一次上下文 我现在进行了基准测试,发现插入具有 1 个或 2 个触发器的表之间没有区别。详情见我的回答【参考方案2】:

我现在已经对这种情况进行了基准测试,我得出的结论是,当添加 1 个触发器时,很可能由于 PL/SQL 上下文切换而导致显着的性能损失。在我的基准测试中,损失是 8 倍。但是,添加第二个“兼容”触发器不再产生任何重大影响。 “兼容”是指两个触发器始终以任意顺序触发同一事件。

所以我的结论是,所有触发器很可能只有 1 个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 个触发器,执行 10 个操作,另一个表有 10 个触发器,执行相同的 10 个操作。 2 与 1 触发器的性能差异可能太小而无法超越噪音。 @JeffreyKemp:这将是一个很好的测试。然而,就我而言,我只是想知道 2 个触发器的执行时间是否是 1 个触发器的 2 倍,但似乎并非如此......【参考方案3】:

我建议您调查在触发器中使用序列的影响 - 在创建序列时使用缓存值作为种子序列 (CACHE 指定数据库预分配多少序列值并将其保存在内存中以便更快地访问)。我经历过的因素远大于与序列相关的 8 个因素。在任何情况下,为了比较触发器在上下文切换方面的影响,应该在测试中取消使用序列,或者在评估结果时考虑使用序列。

【讨论】:

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

以上是关于合并多个 Oracle 触发器。任何性能影响?的主要内容,如果未能解决你的问题,请参考以下文章

可以为当前会话禁用 Oracle 触发器吗?

ORACLE触发器

确定表是不是受任何触发器影响

Oracle:搜索所有存储的过程/触发器/其他数据库代码?

防抖和节流方法实现

Oracle中触发器有几种,用法与SQL Server一样吗?谢谢