¿Por qué se supone que las secuencias de Denali funcionan mejor que las columnas de identidad?


36

En su respuesta a ¿Cuál es mejor: columnas de identidad o valores de identificación únicos generados? mrdenny dice:

Cuando salga SQL Denali, admitirá secuencias que serán más eficientes que la identidad, pero usted no puede crear algo más eficiente.

No estoy muy seguro. Conociendo las secuencias de Oracle , tengo que crear un activador para la inserción, encapsular cada inserción en una llamada de un procedimiento almacenado, o rezar para que no olvide usar la secuencia correctamente cuando hago una inserción ad-hoc.

Dudo que las ventajas de las secuencias sean tan obvias.


2
Sé que no responde a su pregunta, pero aparte de cualquier diferencia de rendimiento, las secuencias tienen otras ventajas. Por ejemplo, una secuencia no te impide actualizar la columna de destino, lo cual es una limitación muy inconveniente de IDENTITY.
nvogel

Respuestas:


37

Contestaré aquí también. Tiene que ver con lo interno de cómo IDENTITYy cómo SEQUENCEfunciona.

Con IDENTITY, SQL Server almacena en caché los valores en la memoria para que estén fácilmente disponibles. Vea la respuesta de Martin Smith para los detalles. A medida que se utilizan los valores, un proceso en segundo plano genera más valores. Como puede imaginar, este grupo puede agotarse bastante rápido, dejando la aplicación a merced del proceso en segundo plano que está generando los valores.

Con SEQUENCE, SQL Server le permite definir qué tan grande debe ser el caché. Si bien SQL Server en realidad no mantiene los valores en la memoria caché, solo mantiene el valor actual y el valor final superior, esto reducirá en gran medida la cantidad de IO que se necesita para crear valores.

No configure el caché demasiado alto, ya que esto reducirá la cantidad de números que se pueden usar: si SQL Server se bloqueara, se perderían los valores especificados en el rango de caché actual que no se usaron.

En cuanto a la inserción de filas, solo especifique un valor predeterminado para la columna, de esta manera:

DEFAULT (NEXT VALUE FOR Audit.EventCounter),

21

Desde que se escribió el artículo de Itzik Ben Gan, el tamaño de caché codificado de 10 IDENTITYparece haber cambiado. De los comentarios sobre este elemento de conexión

El tamaño de la preasignación se basa en el tamaño del tipo de datos de la columna en la que se define la propiedad de identidad. Para una columna de enteros de SQL Server, el servidor asigna previamente identidades en rangos de 1000 valores. Para el tipo de datos bigint, el servidor asigna previamente en rangos de 10000 valores.

los libro de consultas de T-SQL contiene la siguiente tabla, pero enfatiza que estos valores no están documentados ni se garantiza que no se modifiquen.

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

El artículo aquí prueba varios tamaños de caché de secuencia e inserta tamaños de lote y ofrece los siguientes resultados.

ingrese la descripción de la imagen aquí

Lo que parece mostrar que para inserciones grandes IDENTITY realiza SEQUENCE. Sin embargo, no prueba el tamaño de caché 1,000 y también esos resultados son solo una prueba. Mirando específicamente el tamaño de caché 1,000 con varios tamaños de lote de insertos, obtuve los siguientes resultados (probando cada tamaño de lote 50 veces y agregando los resultados de la siguiente manera, todo el tiempo 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 |
+------------+-----------+-----------+-----------+-----------+-----------+-----------+

Para lotes más grandes, el IDENTITY versión parece generalmente más rápida .

El libro de consulta de TSQL también explica por qué IDENTITYpuede tener una ventaja de rendimiento sobre la secuencia.

La IDENTITYtabla es específica y SEQUENCEno lo es. Si el desastre llegara a la mitad de la inserción antes de que se vacíe el búfer de registro, no importa si la identidad recuperada es anterior, ya que el proceso de recuperación también deshacerá la inserción, por lo que SQL Server no fuerza el vaciado del búfer de registro en cada identidad escritura en disco relacionada con caché. Sin embargo, para Secuencia esto se aplica, ya que el valor puede usarse para cualquier propósito, incluso fuera de la base de datos. Entonces, en el ejemplo anterior con un millón de inserciones y un tamaño de caché de 1,000, esto es un adicional de mil descargas de registros.

Script para reproducir

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;
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.