Perché le sequenze di Denali dovrebbero funzionare meglio delle colonne di identità?


36

Nella sua risposta a Quale è meglio: colonne di identità o valori ID univoci generati? mrdenny dice:

Quando uscirà SQL Denali, supporterà sequenze che saranno più efficienti dell'identità, ma non puoi creare qualcosa di più efficiente da solo.

Non sono così sicuro. Conoscendo le sequenze di Oracle , devo creare un trigger per l'inserimento, incapsulare ciascun inserto in una chiamata di una procedura memorizzata o pregare di non dimenticare di utilizzare correttamente la sequenza quando eseguo un inserimento ad hoc.

Dubito che i vantaggi delle sequenze siano così evidenti.


2
So che non risponde alla tua domanda, ma a parte qualsiasi differenza di prestazioni, le sequenze hanno altri vantaggi. Ad esempio una sequenza non ti impedisce di aggiornare la colonna di destinazione, il che è una limitazione molto scomoda di IDENTITÀ.
nvogel,

Risposte:


37

Risponderò anche qui. Ha a che fare con gli interni di come IDENTITYe SEQUENCElavorare.

Con IDENTITY, SQL Server pre-memorizza nella cache i valori in modo che siano prontamente disponibili. Vedi la risposta di Martin Smith per i dettagli. Man mano che vengono utilizzati i valori, un processo in background genera più valori. Come puoi immaginare, questo pool può esaurirsi abbastanza rapidamente, lasciando l'applicazione in balia del processo in background che sta generando i valori.

Con SEQUENCE, SQL Server consente di definire la dimensione della cache. Sebbene SQL Server non mantenga effettivamente i valori nella cache, mantiene solo il valore corrente e il valore finale superiore, ciò ridurrà notevolmente la quantità di I / O necessaria per creare valori.

Non impostare la cache su un valore troppo alto, in quanto ciò ridurrà il numero di numeri che è possibile utilizzare: se SQL Server dovesse andare in crash, tutti i valori specificati nell'intervallo di cache corrente che non sono stati utilizzati andrebbero persi.

Per quanto riguarda l'inserimento di righe, basta specificare un valore predefinito per la colonna, in questo modo:

DEFAULT (NEXT VALUE FOR Audit.EventCounter),

21

Da quando l'articolo Itzik Ben Gan è stato scritto, la dimensione della cache hardcoded di 10 per IDENTITYsembra essere stata modificata. Dai commenti su questo elemento di connessione

La dimensione della pre-allocazione si basa sulla dimensione del tipo di dati della colonna su cui è definita la proprietà dell'identità. Per una colonna di numeri interi di SQL Server, il server pre-alloca le identità in intervalli di 1000 valori. Per il tipo di dati bigint il server pre-alloca in intervalli di 10000 valori.

Il libro delle query T-SQL contiene la seguente tabella ma sottolinea che questi valori non sono documentati o garantiti invariati.

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

L'articolo qui verifica varie dimensioni della cache di sequenza e inserisce le dimensioni del batch e fornisce i seguenti risultati.

inserisci qui la descrizione dell'immagine

Che sembra mostrare che per inserti di grandi dimensioni IDENTITYesegue SEQUENCE. Tuttavia, non verifica la dimensione della cache 1.000 e anche questi risultati sono solo un test. Osservando in particolare la dimensione della cache 1.000 con vari lotti di inserti ho ottenuto i seguenti risultati (provando ogni dimensione del lotto 50 volte e aggregando i risultati come sotto, tutti i tempi 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 |
+------------+-----------+-----------+-----------+-----------+-----------+-----------+

Per lotti di dimensioni maggiori, la IDENTITYversione sembra generalmente più veloce .

Il libro di query TSQL spiega anche perché IDENTITYpuò avere un vantaggio in termini di prestazioni rispetto alla sequenza.

È IDENTITYuna tabella specifica e SEQUENCEnon lo è. Se il disastro dovesse colpire l'inserimento intermedio prima che il buffer del log fosse scaricato, non importa se l'identità recuperata è precedente poiché anche il processo di ripristino annullerà l'inserimento, quindi SQL Server non forzerà lo svuotamento del buffer log su ogni identità scrittura su disco relativa alla cache. Tuttavia, per Sequence questo viene applicato in quanto il valore potrebbe essere utilizzato per qualsiasi scopo, anche al di fuori del database. Quindi nell'esempio sopra con un milione di inserimenti e una dimensione della cache di 1.000 si tratta di ulteriori mille flush di log.

Script da riprodurre

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;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.