Por que as seqüências Denali devem ter um desempenho melhor que as colunas de identidade?

36

Em sua resposta para o que é melhor: colunas de identidade ou valores de ID exclusivos gerados? mrdenny diz:

Quando o SQL Denali for lançado, ele oferecerá suporte a seqüências que serão mais eficientes que a identidade, mas você não poderá criar algo mais eficiente.

Eu não tenho tanta certeza. Conhecendo as seqüências do Oracle , eu tenho que criar um gatilho para inserção, encapsular cada inserção em uma chamada de um procedimento armazenado ou orar para que eu não esqueça de usar corretamente a sequência quando fizer uma inserção ad-hoc.

Duvido que as vantagens das sequências sejam tão óbvias.

bernd_k
fonte
2
Sei que não responde à sua pergunta, mas, além de qualquer diferença de desempenho, as seqüências têm outras vantagens. Por exemplo, uma sequência não impede a atualização da coluna de destino, que é uma limitação muito inconveniente de IDENTITY.
Nvogel

Respostas:

37

Eu vou responder aqui também. Tem a ver com os aspectos internos de como IDENTITYe SEQUENCEtrabalhar.

Com IDENTITY, o SQL Server pré-armazena em cache os valores na memória para que estejam prontamente disponíveis. Veja a resposta de Martin Smith para obter detalhes. Conforme os valores são usados, um processo em segundo plano gera mais valores. Como você pode imaginar, esse pool pode acabar rapidamente, deixando o aplicativo à mercê do processo em segundo plano que está gerando os valores.

Com SEQUENCE, o SQL Server permite definir o tamanho do cache. Embora o SQL Server não mantenha os valores no cache, ele mantém apenas o valor atual e o valor final superior, mas isso reduzirá bastante a quantidade de E / S necessária para criar valores.

Não defina o cache muito alto, pois isso reduzirá o número de números que podem ser usados: se o SQL Server travar, quaisquer valores especificados no intervalo de cache atual que não foram usados ​​serão perdidos.

Quanto à inserção de linha, basta especificar um valor padrão para a coluna, assim:

DEFAULT (NEXT VALUE FOR Audit.EventCounter),
Mrdenny
fonte
21

Desde que o artigo de Itzik Ben Gan foi escrito, o tamanho do cache codificado de 10 para IDENTITYparece ter sido alterado. Dos comentários neste item de conexão

O tamanho da pré-alocação é baseado no tamanho do tipo de dados da coluna na qual a propriedade de identidade está definida. Para uma coluna inteira do SQL Server, o servidor pré-aloca identidades em intervalos de 1000 valores. Para o tipo de dados bigint, o servidor pré-aloca em intervalos de 10000 valores.

O livro de consulta T-SQL contém a tabela a seguir, mas enfatiza que esses valores não são documentados ou não são alterados.

+-----------------+-----------+
|    DataType     | CacheSize |
+-----------------+-----------+
| TinyInt         | 10        |
| SmallInt        | 100       |
| Int             | 1,000     |
| BigInt, Numeric | 10,000    |
+-----------------+-----------+

O artigo aqui testa vários tamanhos de cache de sequência e insere tamanhos de lote e apresenta os seguintes resultados.

insira a descrição da imagem aqui

O que parece mostrar que as inserções grandes IDENTITYsão executadas SEQUENCE. Porém, ele não testa o tamanho do cache 1.000 e esses resultados são apenas um teste. Analisando especificamente o tamanho do cache 1.000 com vários tamanhos de lote de inserções, obtive os seguintes resultados (tentando cada tamanho de lote 50 vezes e agregando os resultados como abaixo - sempre em µs).

+------------+-----------+-----------+-----------+-----------+-----------+-----------+
|            |             Sequence              |             Identity              |
| Batch Size |    Min    |    Max    |    Avg    |    Min    |    Max    |    Avg    |
+------------+-----------+-----------+-----------+-----------+-----------+-----------+
| 10         | 2,994     | 7,004     | 4,002     | 3,001     | 7,005     | 4,022     |
| 100        | 3,997     | 5,005     | 4,218     | 4,001     | 5,010     | 4,238     |
| 1,000      | 6,001     | 19,013    | 7,221     | 5,982     | 8,006     | 6,709     |
| 10,000     | 26,999    | 33,022    | 28,645    | 24,015    | 34,022    | 26,114    |
| 100,000    | 189,126   | 293,340   | 205,968   | 165,109   | 234,156   | 173,391   |
| 1,000,000  | 2,208,952 | 2,344,689 | 2,269,297 | 2,058,377 | 2,191,465 | 2,098,552 |
+------------+-----------+-----------+-----------+-----------+-----------+-----------+

Para lotes maiores, a IDENTITYversão geralmente parece mais rápida .

O livro TSQL Querying também explica por que IDENTITYpode ter uma vantagem de desempenho sobre a sequência.

A IDENTITYtabela é específica e SEQUENCEnão é. Se o desastre ocorreu no meio da inserção antes da liberação do buffer de log, não importa se a identidade recuperada é anterior, pois o processo de recuperação também desfaz a inserção, portanto, o SQL Server não força a liberação do buffer de log em todas as identidades. gravação em disco relacionada ao cache. No entanto, para Sequência, isso é imposto, pois o valor pode ser usado para qualquer finalidade - inclusive fora do banco de dados. Portanto, no exemplo acima, com um milhão de inserções e tamanho de cache de 1.000, esse é um milhar adicional de liberações de log.

Script para reproduzir

DECLARE @Results TABLE(
  BatchCounter INT,
  NumRows      INT,
  SequenceTime BIGINT,
  IdTime       BIGINT);

DECLARE @NumRows      INT = 10,
        @BatchCounter INT;

WHILE @NumRows <= 1000000
  BEGIN
      SET @BatchCounter = 0;

      WHILE @BatchCounter <= 50
        BEGIN
            --Do inserts using Sequence
            DECLARE @SequenceTimeStart DATETIME2(7) = SYSUTCDATETIME();

            INSERT INTO dbo.t1_Seq1_cache_1000
                        (c1)
            SELECT N
            FROM   [dbo].[TallyTable] (@NumRows)
            OPTION (RECOMPILE);

            DECLARE @SequenceTimeEnd DATETIME2(7) = SYSUTCDATETIME();
            --Do inserts using IDENTITY
            DECLARE @IdTimeStart DATETIME2(7) = SYSUTCDATETIME();

            INSERT INTO dbo.t1_identity
                        (c1)
            SELECT N
            FROM   [dbo].[TallyTable] (@NumRows)
            OPTION (RECOMPILE);

            DECLARE @IdTimeEnd DATETIME2(7) = SYSUTCDATETIME();

            INSERT INTO @Results
            SELECT @BatchCounter,
                   @NumRows,
                   DATEDIFF(MICROSECOND, @SequenceTimeStart, @SequenceTimeEnd) AS SequenceTime,
                   DATEDIFF(MICROSECOND, @IdTimeStart, @IdTimeEnd)             AS IdTime;

            TRUNCATE TABLE dbo.t1_identity;

            TRUNCATE TABLE dbo.t1_Seq1_cache_1000;

            SET @BatchCounter +=1;
        END

      SET @NumRows *= 10;
  END

SELECT NumRows,
       MIN(SequenceTime) AS MinSequenceTime,
       MAX(SequenceTime) AS MaxSequenceTime,
       AVG(SequenceTime) AS AvgSequenceTime,
       MIN(IdTime)       AS MinIdentityTime,
       MAX(IdTime)       AS MaxIdentityTime,
       AVG(IdTime)       AS AvgIdentityTime
FROM   @Results
GROUP  BY NumRows;
Martin Smith
fonte