Pourquoi les séquences de Denali sont-elles supposées être plus performantes que les colonnes d'identité?


36

Dans sa réponse à Quel est le meilleur: les colonnes d’identité ou les identifiants uniques générés? mrdenny dit:

Lorsque SQL Denali sera disponible, il prendra en charge des séquences plus efficaces que l’identité, mais vous ne pouvez pas créer quelque chose de plus efficace.

Je ne suis pas si sûr. Connaissant les séquences d'Oracle , je dois créer un déclencheur d'insertion, encapsuler chaque insertion dans un appel d'une procédure stockée ou prier pour que je n'oublie pas d'utiliser correctement la séquence lorsque je fais une insertion ad hoc.

Je doute que les avantages des séquences soient si évidents.


2
Je sais que cela ne répond pas à votre question, mais en dehors de toute différence de performances, les séquences présentent d'autres avantages. Par exemple, une séquence ne vous empêche pas de mettre à jour la colonne cible, ce qui constitue une limitation très incommode de IDENTITY.
Nvogel

Réponses:


37

Je vais répondre ici aussi. Cela a à voir avec les aspects internes de comment IDENTITYet de SEQUENCEtravail.

Avec IDENTITY, SQL Server met en cache les valeurs en mémoire afin qu’elles soient facilement disponibles. Voir la réponse de Martin Smith pour les détails. Au fur et à mesure que les valeurs sont utilisées, un processus en arrière-plan génère plus de valeurs. Comme vous pouvez l'imaginer, ce pool peut s'épuiser assez rapidement, laissant l'application à la merci du processus d'arrière-plan qui génère les valeurs.

Avec SEQUENCE, SQL Server vous permet de définir la taille du cache. Bien que SQL Server ne conserve pas les valeurs dans le cache, il ne conserve que la valeur actuelle et la valeur de l'extrémité supérieure, ce qui réduira considérablement la quantité d'E / S nécessaire pour créer des valeurs.

Ne définissez pas le cache trop haut, car cela réduirait le nombre de nombres pouvant être utilisés: si SQL Server devait tomber en panne, toutes les valeurs spécifiées dans la plage de cache actuelle non utilisées seraient perdues.

En ce qui concerne l'insertion de ligne, spécifiez simplement une valeur par défaut pour la colonne, comme suit:

DEFAULT (NEXT VALUE FOR Audit.EventCounter),

21

Depuis la rédaction de l' article d'Itzik Ben Gan, la taille de la mémoire cache codée en dur de 10 IDENTITYsemble avoir été modifiée. Parmi les commentaires sur cet élément de connexion

La taille de la pré-allocation est basée sur la taille du type de données de la colonne sur laquelle la propriété d'identité est définie. Pour une colonne entière SQL Server, le serveur préalloue des identités dans des plages de 1 000 valeurs. Pour le type de données bigint, le serveur effectue une pré-affectation dans des plages de 10 000 valeurs.

Le manuel d' interrogation T-SQL contient le tableau suivant, mais souligne que ces valeurs ne sont ni documentées ni garanties d'être inchangées.

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

L'article ici teste différentes tailles de cache de séquence et de tailles de lot d'insertion et donne les résultats suivants.

entrez la description de l'image ici

Ce qui semble montrer que pour les grands inserts IDENTITYout effectue SEQUENCE. Cependant, il ne teste pas la taille de cache 1 000 et ces résultats ne sont qu'un test. En examinant spécifiquement la taille du cache 1 000 avec différentes tailles de lots d'insertions, j'ai obtenu les résultats suivants (essayer 50 fois la taille de chaque lot et agréger les résultats comme ci-dessous - tous les temps sont en μ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 |
+------------+-----------+-----------+-----------+-----------+-----------+-----------+

Pour les lots de plus grande taille, la IDENTITYversion semble généralement plus rapide .

Le manuel TSQL Querying explique également pourquoi IDENTITYun avantage en termes de performances est obtenu par rapport à la séquence.

La IDENTITYtable est spécifique et SEQUENCEne l'est pas. Si un sinistre devait frapper mi insertion avant que le tampon de journal ne soit vidé, peu importe si l'identité récupérée est antérieure, le processus de récupération annulant également l'insertion, de sorte que SQL Server ne force pas le vidage du tampon de journal pour chaque identité. cache lié disque écrire. Cependant, pour Sequence, ceci est appliqué car la valeur peut être utilisée à n'importe quelle fin, y compris en dehors de la base de données. Ainsi, dans l'exemple ci-dessus, avec un million d'insertions et une taille de cache de 1 000, il s'agit de mille vidages supplémentaires du journal.

Script à reproduire

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;
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.