33.6. Regras versus gatilhos

Várias coisas que podem ser feitas utilizando gatilho também podem ser implementadas utilizando o sistema de regras do PostgreSQL. Entre o que não pode ser implementado pelas regras estão alguns tipos de restrição, especialmente as chaves estrangeiras. É possível definir uma regra qualificada para reescrever o comando como NOTHING se o valor da coluna não constar da outra tabela, mas neste caso os dados são desprezados em silêncio, e esta não é uma boa idéia. Se a verificação dos valores válidos for requerida, e for necessária uma mensagem de erro no caso de um valor inválido, deve ser definido através de um gatilho.

Por outro lado, um gatilho disparado pelo INSERT em uma visão pode fazer o mesmo que uma regra faz: colocar os dados em algum outro lugar, e suprimir a inserção na visão. Porém, para UPDATE e DELETE o gatilho não pode fazer o mesmo que a regra faz, porque não existem dados reais na relação da visão para serem varridos e, portanto, o gatilho nunca vai ser chamado. Nestes casos só a regra funciona.

Entre o que pode ser implementado por ambos, qual é melhor depende da utilização do banco de dados. O gatilho é disparado uma vez para cada linha afetada. A regra manipula o comando, ou gera um comando adicional. Portanto, se uma instrução afetar muitas linhas é provável que a regra emitindo um comando adicional seja mais rápida que o gatilho chamado para todas as linhas, ocasionando a execução de suas operações muitas vezes. Entretanto, a abordagem do gatilho é conceitualmente bem mais simples que a abordagem da regra, sendo mais fácil para os usuários inexperientes definir um gatilho corretamente.

Abaixo está mostrado um exemplo de como a escolha entre regra e gatilho se apresenta em uma situação. Existem duas tabelas:

CREATE TABLE computador (
    hospedeiro    text,    -- indexado
    fabricante    text     -- indexado
);

CREATE TABLE programa (
    programa      text,    -- indexado
    hospedeiro    text     -- indexado
);

As duas tabelas possuem milhares de linhas, e os índices para hospedeiro são únicos. A regra e o gatilho devem implementar uma restrição para excluir as linhas de programa que fazem referência a um computador excluído. O gatilho utiliza este comando:

DELETE FROM programa WHERE hospedeiro = $1;

Uma vez que o gatilho é chamado para cada linha excluída da tabela computador, pode preparar e salvar o plano para este comando e passar o valor do hospedeiro para o parâmetro. A regra é escrita como:

CREATE RULE computador_del AS ON DELETE TO computador
    DO DELETE FROM programa WHERE hospedeiro = OLD.hospedeiro;

A seguir são analisados tipos diferentes de exclusão. No caso de

DELETE FROM computador WHERE hospedeiro = 'meupc.local.net';

a tabela computador é varrida pelo índice (rápido), e o comando emitido pelo gatilho também utiliza uma varredura de índice (também rápido). O comando adicionado pela regra é:

DELETE FROM programa WHERE computador.hospedeiro = 'meupc.local.net'
                       AND programa.hospedeiro = computador.hospedeiro;

Uma vez que existem índices apropriados definidos, o planejador cria o plano

Nestloop
  ->  Index Scan using comp_hospidx on computador
  ->  Index Scan using prog_hospidx on programa

Portanto, não há muita diferença de velocidade entre a implementação do gatilho e da regra.

Na próxima exclusão serão eliminados 2000 computadores onde hospedeiro começa por old. Existem dois comandos possíveis de serem utilizados. Um é

DELETE FROM computador WHERE hospedeiro >= 'old'
                       AND   hospedeiro <  'ole'

O comando adicionado pela regra é

DELETE FROM programa WHERE computador.hospedeiro >= 'old' AND computador.hospedeiro < 'ole'
                       AND programa.hospedeiro = computador.hospedeiro;

com o plano

Hash Join
  ->  Seq Scan on programa
  ->  Hash
    ->  Index Scan using comp_hospidx on computador

O outro comando possível é

DELETE FROM computador WHERE hospedeiro ~ '^old';

que resulta no seguinte plano de execução para o comando adicionado pela regra:

Nestloop
  ->  Index Scan using comp_hospidx on computador
  ->  Index Scan using prog_hospidx on programa

Isto mostra que o planejador não percebe que a qualificação para hospedeiro em computador também pode ser utilizada para uma varredura de índice em programa quando há expressões de qualificação combinadas por AND, que é o que o planejador faz na versão do comando com expressão regular. O gatilho é chamado uma vez para cada um dos 2000 computadores antigos a serem excluídos, e isto resulta em uma varredura de índice para computador e 2000 varreduras de índice para programa. A implementação da regra faz isto com dois comandos que utilizam índices. Se a regra é mais rápida que a situação da varredura seqüencial, depende do tamanho total da tabela programa. A execução de 2000 comandos pelo gatilho através do gerenciador da SPI toma algum tempo, mesmo que todos os blocos do índice fiquem rapidamente no cache.

O último comando a ser visto é

DELETE FROM computador WHERE fabricante = 'bim';

Novamente poderia resultar em muitas linhas sendo excluídas de computador. Portanto, o gatilho novamente executa muitos comandos através do executor. O comando gerado pela regra é

DELETE FROM programa WHERE computador.fabricante = 'bim'
                       AND programa.hospedeiro = computador.hospedeiro;

O plano para este comando novamente é um laço aninhado sobre duas varreduras de índice, apenas usando um índice diferente para computador:

Nestloop
  ->  Index Scan using comp_fabridx on computador
  ->  Index Scan using prog_hospidx on programa

Em qualquer um destes casos, os comandos adicionais do sistema de regras são mais ou menos independentes do número de linhas afetadas pelo comando.

Em resumo, as regras somente são significativamente mais lentas que os gatilhos quando suas ações resultam em junções grandes e mal qualificadas, uma situação onde o planejador falha.

SourceForge.net Logo CSS válido!