Em que momento um banco de dados atualiza seus índices em uma transação?

11

Estou tentando entender a sequência de eventos em inserções onde estão envolvidos um índice e uma transação.

Por exemplo, a documentação do Oracle afirma:

Se você criar [ou possuir] um ou mais índices antes de carregar os dados, o banco de dados deverá atualizar todos os índices à medida que cada linha é inserida.

Mas o que acontece se eu criar uma transação, inserir cinco linhas e depois confirmar? Os índices são atualizados para cada inserção ou apenas no ponto de confirmação?

A lógica me diz que eles só seriam atualizados no ponto de confirmação, porque um índice atualizado não poderia ser útil até que esses registros fossem confirmados. Mas isso é verdade?

Em caso afirmativo, quando tenho 1m de linhas para inserir, para obter o melhor desempenho, devo fazer um commit grande de todas as linhas e não 10 transações de 100k registros? É claro que percebo que isso arrisca uma maior reversão se a linha 999.999 falhar.

Desculpas se minha terminologia está um pouco fora. Eu não sou um DBA por profissão. Não estou muito interessado em um banco de dados específico, como bancos de dados em geral, embora Oracle e Postgres sejam o que eu mais uso. Eu pesquisei sobre esse tópico, mas não consigo encontrar uma resposta definitiva.

Mark Ireland
fonte

Respostas:

8

Eu trabalho com SQL Server e Oracle. Provavelmente existem algumas exceções, mas para essas plataformas a resposta geral é que dados e índices serão atualizados ao mesmo tempo.

Eu acho que seria útil fazer uma distinção entre quando os índices são atualizados para a sessão que possui a transação e para outras sessões. Por padrão, outras sessões não verão os índices atualizados até que a transação seja confirmada. No entanto, a sessão que possui a transação verá imediatamente os índices atualizados.

Para uma maneira de pensar sobre isso, considere em uma mesa com uma chave primária. No SQL Server e Oracle, isso é implementado como um índice. Na maioria das vezes, queremos que ocorra imediatamente um erro se INSERTfor feito, o que violaria a chave primária. Para que isso aconteça, o índice deve ser atualizado ao mesmo tempo que os dados. Observe que outras plataformas, como o Postgres, permitem restrições adiadas que são verificadas apenas quando a transação é confirmada.

Aqui está uma demonstração rápida do Oracle, mostrando um caso comum:

CREATE TABLE X_TABLE (PK INT NULL, PRIMARY KEY (PK));

INSERT INTO X_TABLE VALUES (1);
INSERT INTO X_TABLE VALUES (1); -- no commit

A segunda INSERTinstrução gera um erro:

Erro SQL: ORA-00001: restrição exclusiva (XXXXXX.SYS_C00384850) violada

00001. 00000 - "restrição exclusiva (% s.% S) violada"

* Causa: Uma instrução UPDATE ou INSERT tentou inserir uma chave duplicada. Para Oracle confiável configurado no modo MAC do DBMS, você poderá ver esta mensagem se existir uma entrada duplicada em um nível diferente.

* Ação: remova a restrição exclusiva ou não insira a chave.

Se você preferir ver uma ação de atualização de índice abaixo, é uma demonstração simples no SQL Server. Primeiro, crie uma tabela de duas colunas com um milhão de linhas e um índice não clusterizado na VALcoluna:

DROP TABLE IF EXISTS X_TABLE_IX;

CREATE TABLE X_TABLE_IX (
ID INT NOT NULL,
VAL VARCHAR(10) NOT NULL
PRIMARY KEY (ID)
);

CREATE INDEX X_INDEX ON X_TABLE_IX (VAL);

-- insert one million rows with N from 1 to 1000000
INSERT INTO X_TABLE_IX
SELECT N, N FROM dbo.Getnums(1000000);

A consulta a seguir pode usar o índice não clusterizado porque o índice é um índice de cobertura para essa consulta. Ele contém todos os dados necessários para executá-lo. Como esperado, nenhum retorno é retornado.

SELECT *
FROM X_TABLE_IX
WHERE VAL = 'A';

consulta 1

Agora vamos iniciar uma transação e atualizar VALpara quase todas as linhas da tabela:

BEGIN TRANSACTION

UPDATE X_TABLE_IX
SET VAL = 'A'
WHERE ID <> 1;

Aqui está parte do plano de consulta para isso:

consulta 2

Circular em vermelho é a atualização para o índice não clusterizado. Circulada em azul está a atualização do índice clusterizado, que é essencialmente os dados da tabela. Mesmo que a transação não tenha sido confirmada, vemos que os dados e o índice são atualizados em parte da execução da consulta. Observe que você nem sempre verá isso em um plano, dependendo do tamanho dos dados envolvidos, além de outros fatores.

Com a transação ainda não confirmada, vamos revisitar a SELECTconsulta acima.

SELECT *
FROM X_TABLE_IX
WHERE VAL = 'A';

insira a descrição da imagem aqui

O otimizador de consulta ainda pode usar o índice e, desta vez, estima que 999999 linhas serão retornadas. A execução da consulta retorna o resultado esperado.

Essa foi uma demonstração simples, mas espero que tenha esclarecido um pouco as coisas.

Como um aparte, estou ciente de alguns casos em que se poderia argumentar que um índice não é atualizado imediatamente. Isso é feito por motivos de desempenho e o usuário final não deve poder ver dados inconsistentes. Por exemplo, algumas vezes as exclusões não serão totalmente aplicadas a um índice no SQL Server. Um processo em segundo plano é executado e, eventualmente, limpa os dados. Você pode ler sobre registros fantasmas, se estiver curioso.

Joe Obbish
fonte
Essa é uma super resposta - e também responde outra coisa que eu queria saber: se uma violação de chave primária (ou similar) ocorreria no Insert ou no Commit. Obrigado por uma resposta tão completa.
Mark Ireland
A pergunta relacionada (sobre quando ocorrerá uma violação de restrição) está relacionada ao uso ou não de transações adiadas. O SQL Server, por exemplo, não implementou transação adiada; portanto, todas as violações ocorrem no final das instruções. Outros DBMS possuem (Postgres, por exemplo, embora não para todos os tipos de restrições); portanto, quando uma restrição é adiada, a violação será verificada na fase de confirmação da transação.
precisa saber é o seguinte
A Oracle também suporta restrições diferidos
bobc
1

Minha experiência é que 1.000.000 de inserção de linha realmente exigem mais recursos e levam mais tempo para serem concluídas do que se você usasse inserções em lote. Isso pode ser implementado, como exemplo, em 100 inserções de 10.000 linhas.

Isso reduz a sobrecarga dos lotes que estão sendo inseridos e, se um lote falhar, é uma reversão menor.

De qualquer forma, para o SQL Server, existe um utilitário bcp ou o comando BULK INSERT que pode ser usado para fazer inserções em lote.

E, é claro, você também pode implementar seu próprio código para lidar com essa abordagem.

RLF
fonte
11
Em geral, se você precisar inserir um grande número de linhas em uma tabela que precise de um índice, provavelmente será mais rápido descartar o índice, carregar os dados e depois reconstruí-lo. O Oracle também suporta uma opção de carregamento em massa de caminho direto, usando a dica / * + APPEND * /.
bobc