全部產品
Search
文件中心

PolarDB:規則vs觸發器

更新時間:Jul 06, 2024

許多觸發器可以乾的事情同樣也可以用PostgreSQL規則系統來實現。目前不能用規則來實現的東西之一是某些約束,特別是外鍵。 可以放置一個合格的規則在一列上,這個規則在列的值沒有出現在另一個表中時把命令重寫成NOTHING。但是這樣做資料就會被不聲不響地丟棄,因此也不是一個好主意。如果要求檢查值的有效性,並且在出現無效值的情況下應該產生一個錯誤訊息,這種需求就必須要用觸發器來完成。

在本章中,我們關注於使用規則來更新視圖。本章中所有的更新規則的例子都可以使用視圖上的INSTEAD OF觸發器來實現。編寫這類觸發器通常比編寫規則要容易,特別是在要求使用複雜邏輯來執行更新的情況下。

對於兩者都可實現的情況,哪個更好取決於對資料庫的使用。觸發器為每一個受影響的行都執行一次。規則修改查詢樹或產生一個額外的查詢。所以如果在一個語句中影響到很多行, 一個發出額外查詢的規則通常可能會比一個觸發器快,因為觸發器對每一個行都要被調用,並且每次被調用時都需要重新判斷要做什麼樣的操作。不過,觸發器方法從概念上要遠比規則方法簡單,並且很容易讓新人上手。

下面我們展示一個例子,該例子說明了在同種情況下兩種選擇的比較。這裡有兩個表:

    CREATE TABLE computer (
        hostname        text,    -- 被索引
        manufacturer    text     -- 被索引
    );

    CREATE TABLE software (
        software        text,    -- 被索引
        hostname        text     -- 被索引
    );

兩個表都有數千行,並且在hostname上的索引是唯一的。規則或觸發器應該實現一個約束,該約束從software中刪除引用已刪除電腦的行。 觸發器可以用下面這條命令:

    DELETE FROM software WHERE hostname = $1;

因為觸發器會為每一個從computer中刪除的獨立行調用一次, 那麼它可以準備並且儲存這個命令的規劃,把hostname作為參數傳入。規則應該被寫為:

    CREATE RULE computer_del AS ON DELETE TO computer
        DO DELETE FROM software WHERE hostname = OLD.hostname;

現在看看不同類型的刪除。在這種情況:

    DELETE FROM computer WHERE hostname = 'mypc.local.net';

computer被使用索引(快速)掃描,並且由觸發器發出的命令也將使用一個索引掃描(同樣快速)。來自規則的額外查詢應該是:

    DELETE FROM software WHERE computer.hostname = 'mypc.local.net'
                           AND software.hostname = computer.hostname;

由於已經建立了合適的索引,規劃器將建立一個規劃

Nestloop
  ->  Index Scan using comp_hostidx on computer
  ->  Index Scan using soft_hostidx on software
所以在觸發器和規則的實現之間沒有太多的速度差別。

在接下來的刪除中,我們想要去掉所有 2000 個`hostname`以`old`開頭的電腦。有兩個命令可以來做這件事。一個是:

```sql
    DELETE FROM computer WHERE hostname >= 'old'
                           AND hostname <  'ole'

被規則增加的命令將是:

    DELETE FROM software WHERE computer.hostname >= 'old' AND computer.hostname < 'ole'
                           AND software.hostname = computer.hostname;

計劃是:

    Hash Join
      ->  Seq Scan on software
      ->  Hash
        ->  Index Scan using comp_hostidx on computer

另一個可能的命令是:

    DELETE FROM computer WHERE hostname ~ '^old';

它會為規劃增加的命令產生下面的執行計畫:

    Nestloop
      ->  Index Scan using comp_hostidx on computer
      ->  Index Scan using soft_hostidx on software

這表明,當有多個條件運算式被使用AND組合在一起時,規劃器不能認識到表computerhostname上的條件也可以被用於一個software上的索引掃描, 而在該命令的Regex版本中正是這樣做的。觸發器將為要被刪除的 2000 箇舊電腦中的每一個調用,並且會導致在computer上的一次索引掃描和software上的 2000 次索引掃描。採用規則的實現將會使用兩個使用索引的命令來完成。 並且在順序掃描情況下規則是否仍將更快是取決於software表的總體大小的。即使所有的索引塊都將很快地進入快取,通過 SPI 管理器執行來自觸發器的 2000 個命令也要花不少時間。

我們要看的最後一個命令是:

    DELETE FROM computer WHERE manufacturer = 'bim';

同樣,這也會導致很多行被從computer中刪除。所以觸發器同樣會通過執行器運行很多命令。規則產生的命令將會是:

    DELETE FROM software WHERE computer.manufacturer = 'bim'
                           AND software.hostname = computer.hostname;

這個命令的計劃又將是在兩個索引掃描上的嵌套迴圈,只不過使用了computer上的另一個索引:

    Nestloop
      ->  Index Scan using comp_manufidx on computer
      ->  Index Scan using soft_hostidx on software

在任何這些情況之一,來自規則系統的額外命令都或多或少與命令中影響的行數無關。概括來說,規則只有在其動作導致了大而且糟糕的條件串連時才會明顯地慢於觸發器,這種情況下規劃器將沒有什麼辦法。