Warum sollen Denali-Sequenzen eine bessere Leistung erzielen als Identitätsspalten?

36

In seiner Antwort auf Welche ist besser: Identitätsspalten oder generierte eindeutige ID-Werte? Mrdenny sagt:

Wenn SQL Denali herauskommt, werden Sequenzen unterstützt, die effizienter sind als Identität, aber Sie können selbst nichts effizienteres erstellen.

Ich bin mir nicht sicher. Wenn ich die Sequenzen von Oracle kenne , muss ich entweder einen Auslöser für die Einfügung erstellen, jede Einfügung in einen Aufruf einer gespeicherten Prozedur einkapseln oder beten, dass ich nicht vergesse, die Sequenz richtig zu verwenden, wenn ich eine Ad-hoc-Einfügung vornehme.

Ich bezweifle, dass die Vorteile von Sequenzen so offensichtlich sind.

bernd_k
quelle
2
Ich weiß, dass es Ihre Frage nicht beantwortet, aber abgesehen von Leistungsunterschieden haben Sequenzen andere Vorteile. Beispielsweise hält eine Sequenz Sie nicht davon ab, die Zielspalte zu aktualisieren, was eine sehr unbequeme Einschränkung von IDENTITY darstellt.
nvogel

Antworten:

37

Ich werde auch hier antworten. Es hat mit den Interna von wie IDENTITYund SEQUENCEArbeit zu tun .

Mit IDENTITYSQL Server werden Werte vorab im Arbeitsspeicher zwischengespeichert, damit sie sofort verfügbar sind. Siehe Martin Smith Antwort für die Details. Wenn Werte verwendet werden, generiert ein Hintergrundprozess mehr Werte. Wie Sie sich vorstellen können, kann dieser Pool ziemlich schnell ausgehen und die Anwendung dem Hintergrundprozess aussetzen, der die Werte generiert.

Mit SEQUENCESQL Server können Sie festlegen, wie groß der Cache sein soll. SQL Server behält zwar nicht die Werte im Cache bei, behält jedoch nur den aktuellen Wert und den oberen Endwert bei. Dadurch wird die zum Erstellen von Werten erforderliche Anzahl von E / A-Vorgängen erheblich reduziert.

Stellen Sie den Cache nicht zu hoch ein, da dies die Anzahl der verwendbaren Zahlen verringert: Wenn SQL Server abstürzt, gehen alle im aktuellen Cache-Bereich angegebenen Werte verloren, die nicht verwendet wurden.

Geben Sie für das Einfügen von Zeilen einfach einen Standardwert für die Spalte an, wie folgt:

DEFAULT (NEXT VALUE FOR Audit.EventCounter),
mrdenny
quelle
21

Seit der Veröffentlichung des Artikels von Itzik Ben GanIDENTITY scheint sich die fest codierte Cachegröße von 10 für geändert zu haben. Aus den Kommentaren zu diesem Verbindungselement

Die Größe der Vorbelegung basiert auf der Größe des Datentyps der Spalte, für die die Identitätseigenschaft definiert ist. Für eine SQL Server-Ganzzahlspalte weist der Server Identitäten in Bereichen von 1000 Werten vorab zu. Für den bigint-Datentyp weist der Server Bereiche von 10000 Werten vorab zu.

Das T-SQL-Abfragebuch enthält die folgende Tabelle, weist jedoch darauf hin, dass diese Werte nicht dokumentiert sind oder garantiert nicht geändert werden.

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

In diesem Artikel werden verschiedene Sequence-Cache-Größen und Insert-Batch-Größen getestet und die folgenden Ergebnisse erzielt.

Bildbeschreibung hier eingeben

Was zu zeigen scheint, dass bei großen Inserts IDENTITYOut funktioniert SEQUENCE. Die Cache-Größe 1.000 wird jedoch nicht getestet, und auch diese Ergebnisse sind nur ein Test. Bei genauerer Betrachtung der Cache-Größe 1.000 mit verschiedenen Stapelgrößen von Inserts ergaben sich die folgenden Ergebnisse (50-maliger Versuch jeder Stapelgröße und Aggregation der Ergebnisse wie folgt - alle Male in μ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 |
+------------+-----------+-----------+-----------+-----------+-----------+-----------+

Bei größeren Losgrößen erscheint die IDENTITYVersion in der Regel schneller .

Das Buch TSQL-Abfragen erläutert auch, warum IDENTITYdie Leistung gegenüber der Sequenz von Vorteil sein kann.

Das IDENTITYist tabellenspezifisch und SEQUENCEnicht. Wenn die Katastrophe eine mittlere Einfügemarke war, bevor der Protokollpuffer geleert wurde, spielt es keine Rolle, ob die wiederhergestellte Identität eine frühere ist, da der Wiederherstellungsprozess auch die Einfügung rückgängig macht, sodass SQL Server das Leeren des Protokollpuffers nicht für jede Identität erzwingt Cache-bezogene Disc schreiben. Für Sequence wird dies jedoch erzwungen, da der Wert für jeden Zweck verwendet werden kann - auch außerhalb der Datenbank. Im obigen Beispiel mit einer Million Einfügungen und einer Cache-Größe von 1.000 sind dies zusätzliche tausend Protokolllöschungen.

Zu reproduzierendes Skript

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
quelle