Atualizações de índice exclusivas e contadores de modificação de linha de estatísticas

14

Dada a tabela a seguir, índice clusterizado exclusivo e estatísticas:

CREATE TABLE dbo.Banana
(
    pk integer NOT NULL, 
    c1 char(1) NOT NULL, 
    c2 char(1) NOT NULL
);

CREATE UNIQUE CLUSTERED INDEX pk ON dbo.Banana (pk);

CREATE STATISTICS c1 ON dbo.Banana (c1);
CREATE STATISTICS c2 ON dbo.Banana (c2);

INSERT dbo.Banana 
    (pk, c1, c2) 
VALUES 
    (1, 'A', 'W'), 
    (2, 'B', 'X'), 
    (3, 'C', 'Y'), 
    (4, 'D', 'Z');

-- Populate statistics
UPDATE STATISTICS dbo.Banana;

Dados de exemplo

Os contadores de modificação de linha de estatísticas obviamente mostram zero antes de qualquer atualização:

-- Show statistics modification counters
SELECT
    stats_name = S.[name], 
    DDSP.stats_id,
    DDSP.[rows],
    DDSP.modification_counter
FROM sys.stats AS S
CROSS APPLY sys.dm_db_stats_properties(S.object_id, S.stats_id) AS DDSP
WHERE
    S.[object_id] = OBJECT_ID(N'dbo.Banana', N'U');

Contadores de modificação zero

Incrementando cada pkvalor da coluna em um para cada linha:

-- Increment pk in every row
UPDATE dbo.Banana 
SET pk += 1;

Usa o plano de execução:

Plano de execução Split Collapse Collapse

Produz os seguintes contadores de modificação de estatísticas:

Contadores de modificação pós-atualização

Questões

  1. O que os operadores de divisão, classificação e recolhimento fazem?
  2. Por que as pkestatísticas mostram 2 modificações, mas c1e c2mostrar 5?
Paul White 9
fonte

Respostas:

15

O SQL Server sempre usa a combinação de operadores Dividir, Classificar e Recolher ao manter um índice exclusivo como parte de uma atualização que afeta (ou pode afetar) mais de uma linha.

Trabalhando com o exemplo da pergunta, podemos escrever a atualização como uma atualização de linha única separada para cada uma das quatro linhas presentes:

-- Per row updates
UPDATE dbo.Banana SET pk = 2 WHERE pk = 1;
UPDATE dbo.Banana SET pk = 3 WHERE pk = 2;
UPDATE dbo.Banana SET pk = 4 WHERE pk = 3;
UPDATE dbo.Banana SET pk = 5 WHERE pk = 4;

O problema é que a primeira instrução falhará, pois muda pkde 1 para 2, e já existe uma linha em que pk= 2. O mecanismo de armazenamento do SQL Server exige que índices exclusivos permaneçam exclusivos em todas as etapas do processamento, mesmo em uma única instrução . Esse é o problema resolvido por Dividir, Classificar e Recolher.

Dividido Dividido

A primeira etapa é dividir cada instrução de atualização em uma exclusão seguida por uma inserção:

DELETE dbo.Banana WHERE pk = 1;
INSERT dbo.Banana (pk, c1, c2) VALUES (2, 'A', 'W');

DELETE dbo.Banana WHERE pk = 2;
INSERT dbo.Banana (pk, c1, c2) VALUES (3, 'B', 'X');

DELETE dbo.Banana WHERE pk = 3;
INSERT dbo.Banana (pk, c1, c2) VALUES (4, 'C', 'Y');

DELETE dbo.Banana WHERE pk = 4;
INSERT dbo.Banana (pk, c1, c2) VALUES (5, 'D', 'Z');

O operador Split adiciona uma coluna de código de ação ao fluxo (aqui chamado Act1007):

Dividir propriedades

O código de ação é 1 para uma atualização, 3 para uma exclusão e 4 para uma inserção.

Ordenar Ordenar

As instruções de divisão acima ainda produziriam uma violação de chave exclusiva transitória falsa; portanto, a próxima etapa é classificar as instruções pelas chaves do índice exclusivo que está sendo atualizado ( pknesse caso) e depois pelo código de ação. Neste exemplo, isso significa simplesmente que as exclusões (3) na mesma chave são ordenadas antes das inserções (4). A ordem resultante é:

-- Sort (pk, action)
DELETE dbo.Banana WHERE pk = 1;
DELETE dbo.Banana WHERE pk = 2;
INSERT dbo.Banana (pk, c1, c2) VALUES (2, 'A', 'W');
DELETE dbo.Banana WHERE pk = 3;
INSERT dbo.Banana (pk, c1, c2) VALUES (3, 'B', 'X');
DELETE dbo.Banana WHERE pk = 4;
INSERT dbo.Banana (pk, c1, c2) VALUES (4, 'C', 'Y');
INSERT dbo.Banana (pk, c1, c2) VALUES (5, 'D', 'Z');

Classificar propriedades

Colapso Colapso

O estágio anterior é suficiente para garantir a prevenção de falsas violações de exclusividade em todos os casos. Como uma otimização, o Collapse combina exclusões adjacentes e insere o mesmo valor-chave em uma atualização:

-- Collapse (pk)
DELETE dbo.Banana WHERE pk = 1;
UPDATE dbo.Banana SET c1 = 'A', c2 = 'W' WHERE pk = 2;
UPDATE dbo.Banana SET c1 = 'B', c2 = 'X' WHERE pk = 3;
UPDATE dbo.Banana SET c1 = 'C', c2 = 'Y' WHERE pk = 4;
INSERT dbo.Banana (pk, c1, c2) VALUES (5, 'D', 'Z');

Os pares de exclusão / inserção para os pkvalores 2, 3 e 4 foram combinados em uma atualização, deixando uma única exclusão em pk= 1 e uma inserção em pk= 5.

O operador Recolher agrupa linhas pelas colunas principais e atualiza o código de ação para refletir o resultado do recolhimento:

Recolher propriedades

Atualização de índice em cluster Atualização de índice em cluster

Este operador é rotulado como uma atualização, mas é capaz de inserções, atualizações e exclusões. A ação que é executada pela Atualização de Índice em Cluster por linha é determinada pelo valor do código de ação nessa linha. O operador possui uma propriedade Action para refletir este modo de operação:

Propriedade de ação de Atualização de Índice em Cluster


Contadores de modificação de linha

Observe que as três atualizações acima não modificam as chaves do índice exclusivo que está sendo mantido. Com efeito, transformamos as atualizações nas colunas- chave do índice em atualizações das colunas não-chave ( c1e c2), além de uma exclusão e uma inserção. Nem uma exclusão nem uma inserção podem causar uma violação falsa de chave exclusiva.

Uma inserção ou exclusão afeta todas as colunas da linha; portanto, as estatísticas associadas a cada coluna terão seus contadores de modificação incrementados. Para atualizações, apenas as estatísticas com qualquer uma das colunas atualizadas como coluna principal têm seus contadores de modificação incrementados (mesmo que o valor seja inalterado).

Os contadores de modificação de linha de estatísticas mostram, portanto, 2 alterações em pk e 5 para c1e c2:

-- Collapse (pk)
DELETE dbo.Banana WHERE pk = 1;                         -- All columns modified
UPDATE dbo.Banana SET c1 = 'A', c2 = 'W' WHERE pk = 2;  -- c1 and c2 modified
UPDATE dbo.Banana SET c1 = 'B', c2 = 'X' WHERE pk = 3;  -- c1 and c2 modified
UPDATE dbo.Banana SET c1 = 'C', c2 = 'Y' WHERE pk = 4;  -- c1 and c2 modified
INSERT dbo.Banana (pk, c1, c2) VALUES (5, 'D', 'Z');    -- All columns modified

Nota: Somente as alterações aplicadas ao objeto base (heap ou índice clusterizado) afetam os contadores de modificação de linha de estatísticas. Índices não agrupados em cluster são estruturas secundárias, refletindo as alterações já feitas no objeto base. Eles não afetam os contadores de modificação de linha de estatísticas.

Se um objeto tiver vários índices exclusivos, uma combinação separada de Dividir, Classificar e Recolher será usada para organizar as atualizações para cada um. O SQL Server otimiza esse caso para índices não clusterizados, salvando o resultado do Split em um spool de tabela ansioso e reproduzindo o conjunto para cada índice exclusivo (que terá sua própria classificação por chaves de índice + código de ação e recolhimento).

Efeito nas atualizações de estatísticas

As atualizações automáticas de estatísticas (se ativadas) ocorrem quando o otimizador de consultas precisa de informações estatísticas e percebe que as estatísticas existentes são desatualizadas (ou inválidas devido a uma alteração no esquema). As estatísticas são consideradas desatualizadas quando o número de modificações registradas excede um limite.

O arranjo Dividir / Classificar / Recolher resulta em diferentes modificações de linha sendo gravadas do que seria esperado. Por sua vez, isso significa que uma atualização de estatísticas pode ser acionada mais cedo ou mais tarde do que seria o caso.

No exemplo acima, as modificações de linha da coluna chave aumentam em 2 (a alteração líquida) em vez de 4 (uma para cada linha da tabela afetada) ou 5 (uma para cada exclusão / atualização / inserção produzida pelo recolhimento).

Além disso, colunas não chave que não foram logicamente alteradas pela consulta original acumulam modificações de linha, que podem numerar o dobro do dobro das linhas da tabela atualizadas (uma para cada exclusão e uma para cada inserção).


O número de alterações registradas depende do grau de sobreposição entre os valores antigos e novos da coluna-chave (e, portanto, o grau em que as exclusões e exclusões separadas podem ser reduzidas). Redefinindo a tabela entre cada execução, as seguintes consultas demonstram o efeito nos contadores de modificação de linha com diferentes sobreposições:

UPDATE dbo.Banana SET pk = pk + 0; -- Full overlap

pk = pk + 0

UPDATE dbo.Banana SET pk = pk + 1;

pk = pk + 1

UPDATE dbo.Banana SET pk = pk + 2;

pk = pk + 2

UPDATE dbo.Banana SET pk = pk + 3;

pk = pk + 3

UPDATE dbo.Banana SET pk = pk + 4; -- No overlap

pk = pk + 4

Paul White 9
fonte