Desempenho de um procedimento Trigger vs Stored no MySQL

11

Uma publicação aqui no DBA.StackExchange ( Quais são as práticas recomendadas para os gatilhos manterem um número de revisão nos registros? ) Gerou uma pergunta interessante (pelo menos, interessante para mim) sobre o desempenho no MySQL.

O contexto é que queremos inserir um registro em uma tabela para cada linha que é atualizada. Antes da atualização da linha, queremos armazenar um valor anterior e incrementar uma das colunas (uma coluna "versão").

Se fizermos isso dentro de um gatilho, funcionará bem. Para o MySQL, os gatilhos são linha por linha , por isso seria uma solução fácil. Selecione os dados atualmente na tabela, insira-os na tabela de log e atualize a coluna "versão" nos novos dados.

No entanto, é possível mover essa lógica para um procedimento armazenado. Se você fizer isso, estará executando a inserção e incrementando a coluna "version" na tabela. A coisa toda seria definida com base.

Então, quando se trata de executar essa inserção, seria mais eficiente usar a abordagem de procedimento armazenado baseado em conjunto ou uma abordagem baseada em gatilho?

Esta pergunta é para o MySQL (já que possui gatilhos linha a linha), embora possa se aplicar a outros DBMSs de linha a linha.

Richard
fonte
11
Um aspecto a ter em mente no que diz respeito ao envio da lógica de versão para um procedimento armazenado - como você ficará empolgado quando alguém, de alguma forma, grava diretamente na tabela ignorando seu mecanismo de auditoria?
billinkc
Concordo. Mas, no outro extremo da escala, talvez você queira ignorar intencionalmente esse log em determinadas circunstâncias. Claro, essa é uma pergunta totalmente diferente . Estou realmente curioso sobre as implicações de desempenho.
Richard

Respostas:

7

Por uma questão de simplicidade, os gatilhos são o caminho a seguir para implementar qualquer tipo de rastreamento de alterações no banco de dados. No entanto, você precisa estar ciente do que acontece quando você usa gatilhos.

De acordo com a Programação de procedimentos armazenados do MySQL , a página 256, sob o cabeçalho "Trigger Overhead", diz o seguinte:

É importante lembrar que, por necessidade, os gatilhos adicionam sobrecarga à instrução DML à qual eles se aplicam. a quantidade real de sobrecarga dependerá da natureza do gatilho, mas --- como todos os gatilhos do MySQL executam FOR EACH ROW --- a sobrecarga pode acumular-se rapidamente para instruções que processam um grande número de linhas. Portanto, você deve evitar colocar quaisquer instruções SQL caras ou código de procedimento nos gatilhos.

Uma explicação expandida da sobrecarga do acionador é fornecida nas páginas 529-531. O ponto de conclusão dessa seção afirma o seguinte:

A lição aqui é a seguinte: como o código do acionador será executado uma vez para cada linha afetada por uma instrução DML, o acionador pode facilmente se tornar o fator mais significativo no desempenho do DML. O código dentro do corpo do acionador precisa ser o mais leve possível e - em particular - qualquer instrução SQL no acionador deve ser suportada por índices sempre que possível.

Não mencionado no livro é outro fator ao usar gatilhos: Quando se trata de auditoria de log, lembre-se de onde você faz o log de dados. Digo isso porque, se você optar por fazer logon em uma tabela MyISAM, cada INSERT em uma tabela MyISAM produz um bloqueio de tabela completo durante o INSERT. Isso pode se tornar um gargalo sério em um ambiente de alto tráfego e alta transação. Além disso, se o gatilho estiver em uma tabela do InnoDB e você registrar alterações no MyISAM de dentro do gatilho, isso desativará secretamente a conformidade com ACID (ou seja, reduza as transações de bloco ao comportamento de confirmação automática), que não podem ser revertidas.

Ao usar gatilhos em tabelas do InnoDB e alterações de log

  • A tabela na qual você se registra também é InnoDB
  • Você desativou a confirmação automática
  • Você configura blocos START TRANSACTION ... COMMIT / ROLLBACK completamente

Dessa maneira, os logs de auditoria podem se beneficiar do COMMIT / ROLLBACK, como as tabelas principais.

Em relação ao uso de procedimentos armazenados, você teria que chamar minuciosamente o procedimento armazenado em todos os pontos do DML na tabela que está sendo rastreada. Pode-se facilmente perder as alterações de registro em face de dezenas de milhares de linhas de código de aplicativo. Colocar esse código em um gatilho elimina a localização de todas essas instruções DML.

EMBARGO

Dependendo da complexidade do gatilho, ele ainda pode ser um gargalo. Se você deseja reduzir gargalos no log de auditoria, há algo que você pode fazer. No entanto, isso exigirá uma pequena alteração na infraestrutura.

Usando hardware comum, crie mais dois servidores de banco de dados

Isso servirá para reduzir a E / S de gravação no banco de dados principal (MD) devido ao log de auditoria. Aqui está como você pode fazer isso:

Etapa 01) Ative o log binário no banco de dados principal.

Etapa 02) Usando um servidor barato, configure o MySQL (mesma versão que o MD) com o log binário ativado. Este será o DM. Replicação de instalação do MD para o DM.

Etapa 03) Usando um segundo servidor barato, configure o MySQL (mesma versão do MD) com o log binário desativado. Configure cada tabela de auditoria para usar --replicate-do-table . Isso será AU. Replicação de instalação do DM para o AU.

Etapa 04) mysqldump as estruturas da tabela do MD e carregue-a no DM e AU.

Etapa 05) Converta todas as tabelas de auditoria no MD para usar o mecanismo de armazenamento BLACKHOLE

Etapa 06) Converta todas as tabelas no DM e AU para usar o mecanismo de armazenamento BLACKHOLE

Etapa 07) Converta todas as tabelas de auditoria na AU para usar o mecanismo de armazenamento MyISAM

Quando terminar

  • O DM replicará do MD e gravará as coisas apenas em seu log binário
  • Com o filtro --replicate-do-table em todas as tabelas de auditoria, o AU será replicado do DM

O que isso faz é armazenar informações de auditoria em um servidor de banco de dados separado e também reduzir qualquer degradação de E / S de gravação que o MD normalmente teria.

RolandoMySQLDBA
fonte
Resposta tremenda +++ 1
b_dubb 08/11/19
1

Aqui está uma abordagem para executar esta atualização em massa.

Para este exemplo

  • table_A possui o ID da PRIMARY KEY
  • Você cria uma tabela chamada table_A_Keys2Update com apenas id como PRIMARY KEY
  • Você preenche table_A_Keys2Atualize com os IDs de table_A que você sabe que devem ser atualizados

Para criar table_A_Keys2Update, faça o seguinte:

CREATE TABLE table_A_Keys2Update SELECT id FROM table_A;
ALTER TABLE table_A_Keys2Update ADD PRIMARY KEY (id);

Depois de preencher table_A_Keys2Update com os IDs cujo número de revisão precisa ser incrementado, execute o seguinte UPDATE JOIN para aumentar o número de revisão de todas as linhas cujo ID está em table_A e table_A_Keys2Update:

UPDATE table_A A INNER JOIN table_A_Keys2Update B USING (id)
SET A.revision = A.revision + 1;

Essa consulta de uma linha pode substituir um gatilho e um procedimento armazenado.

Opcionalmente, você pode colocar essa consulta em um procedimento armazenado e chamá-lo, se desejar.

RolandoMySQLDBA
fonte
É realmente a inserção que me interessa. Se você INSERIR NA auditoria SELECT <whatever> FROM <tabela_primary> WHERE <parâmetros do procedimento armazenado>, poderá fazer a inserção em massa. No gatilho, você apenas INSERIRIA VALORES DE auditoria <dados da linha atualizada> . Portanto, a inserção de linha única linha por linha seria mais rápida que a inserção em massa?
Richard
Por uma questão de simplicidade, o gatilho seria muito melhor 1) desde que a tabela primária nunca experimente inserções em massa no meio de qualquer horário de pico, 2) as informações de auditoria precisem ser lidas sob demanda a qualquer momento e 3) seu site tem pouco tráfego.
RolandoMySQLDBA