PostgreSQL 規(guī)則 vs 觸發(fā)器

2021-09-03 16:42 更新

許多觸發(fā)器可以干的事情同樣也可以用PostgreSQL規(guī)則系統(tǒng)來(lái)實(shí)現(xiàn)。目前不能用規(guī)則來(lái)實(shí)現(xiàn)的東西之一是某些約束,特別是外鍵。 可以放置一個(gè)合格的規(guī)則在一列上,這個(gè)規(guī)則在列的值沒(méi)有出現(xiàn)在另一個(gè)表中時(shí)把命令重寫(xiě)成NOTHING。但是這樣做數(shù)據(jù)就會(huì)被不聲不響地丟棄,因此也不是一個(gè)好主意。如果要求檢查值的有效性,并且在出現(xiàn)無(wú)效值的情況下應(yīng)該生成一個(gè)錯(cuò)誤消息,這種需求就必須要用觸發(fā)器來(lái)完成。

在本章中,我們關(guān)注于使用規(guī)則來(lái)更新視圖。本章中所有的更新規(guī)則的例子都可以使用視圖上的INSTEAD OF觸發(fā)器來(lái)實(shí)現(xiàn)。編寫(xiě)這類觸發(fā)器通常比編寫(xiě)規(guī)則要容易,特別是在要求使用復(fù)雜邏輯來(lái)執(zhí)行更新的情況下。

對(duì)于兩者都可實(shí)現(xiàn)的情況,哪個(gè)更好取決于對(duì)數(shù)據(jù)庫(kù)的使用。觸發(fā)器為每一個(gè)受影響的行都執(zhí)行一次。規(guī)則修改查詢樹(shù)或生成一個(gè)額外的查詢。所以如果在一個(gè)語(yǔ)句中影響到很多行, 一個(gè)發(fā)出額外查詢的規(guī)則通常可能會(huì)比一個(gè)觸發(fā)器快,因?yàn)橛|發(fā)器對(duì)每一個(gè)行都要被調(diào)用,并且每次被調(diào)用時(shí)都需要重新判斷要做什么樣的操作。不過(guò),觸發(fā)器方法從概念上要遠(yuǎn)比規(guī)則方法簡(jiǎn)單,并且很容易讓新人上手。

下面我們展示一個(gè)例子,該例子說(shuō)明了在同種情況下兩種選擇的比較。這里有兩個(gè)表:

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

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

兩個(gè)表都有數(shù)千行,并且在hostname上的索引是唯一的。規(guī)則或觸發(fā)器應(yīng)該實(shí)現(xiàn)一個(gè)約束,該約束從software中刪除引用已刪除計(jì)算機(jī)的行。 觸發(fā)器可以用下面這條命令:

DELETE FROM software WHERE hostname = $1;

因?yàn)橛|發(fā)器會(huì)為每一個(gè)從computer中刪除的獨(dú)立行調(diào)用一次, 那么它可以準(zhǔn)備并且保存這個(gè)命令的規(guī)劃,把hostname作為參數(shù)傳入。規(guī)則應(yīng)該被寫(xiě)為:

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

現(xiàn)在看看不同類型的刪除。在這種情況:

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

computer被使用索引(快速)掃描,并且由觸發(fā)器發(fā)出的命令也將使用一個(gè)索引掃描(同樣快速)。來(lái)自規(guī)則的額外查詢應(yīng)該是:

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

由于已經(jīng)建立了合適的索引,規(guī)劃器將創(chuàng)建一個(gè)規(guī)劃

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

所以在觸發(fā)器和規(guī)則的實(shí)現(xiàn)之間沒(méi)有太多的速度差別。

在接下來(lái)的刪除中,我們想要去掉所有 2000 個(gè)hostnameold開(kāi)頭的計(jì)算機(jī)。有兩個(gè)命令可以來(lái)做這件事。一個(gè)是:

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

被規(guī)則增加的命令將是:

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

計(jì)劃是:

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

另一個(gè)可能的命令是:

DELETE FROM computer WHERE hostname ~ '^old';

它會(huì)為規(guī)劃增加的命令產(chǎn)生下面的執(zhí)行計(jì)劃:

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

這表明,當(dāng)有多個(gè)條件表達(dá)式被使用AND組合在一起時(shí),規(guī)劃器不能認(rèn)識(shí)到表computerhostname上的條件也可以被用于一個(gè)software上的索引掃描, 而在該命令的正則表達(dá)式版本中正是這樣做的。觸發(fā)器將為要被刪除的 2000 個(gè)舊計(jì)算機(jī)中的每一個(gè)調(diào)用,并且會(huì)導(dǎo)致在 computer上的一次索引掃描和software上的 2000 次索引掃描。采用規(guī)則的實(shí)現(xiàn)將會(huì)使用兩個(gè)使用索引的命令來(lái)完成。 并且在順序掃描情況下規(guī)則是否仍將更快是取決于software表的總體大小的。即使所有的索引塊都將很快地進(jìn)入高速緩存,通過(guò) SPI 管理器執(zhí)行來(lái)自觸發(fā)器的 2000 個(gè)命令也要花不少時(shí)間。

我們要看的最后一個(gè)命令是:

DELETE FROM computer WHERE manufacturer = 'bim';

同樣,這也會(huì)導(dǎo)致很多行被從computer中刪除。所以觸發(fā)器同樣會(huì)通過(guò)執(zhí)行器運(yùn)行很多命令。規(guī)則生成的命令將會(huì)是:

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

這個(gè)命令的計(jì)劃又將是在兩個(gè)索引掃描上的嵌套循環(huán),只不過(guò)使用了computer上的另一個(gè)索引:

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

在任何這些情況之一,來(lái)自規(guī)則系統(tǒng)的額外命令都或多或少與命令中影響的行數(shù)無(wú)關(guān)。

概括來(lái)說(shuō),規(guī)則只有在其動(dòng)作導(dǎo)致了大而且糟糕的條件連接時(shí)才會(huì)明顯地慢于觸發(fā)器,這種情況下規(guī)劃器將沒(méi)有什么辦法。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)