Después de hacer esta pregunta comparando GUID secuenciales y no secuenciales, traté de comparar el rendimiento de INSERT en 1) una tabla con una clave primaria GUID inicializada secuencialmente newsequentialid()
y 2) una tabla con una clave primaria INT inicializada secuencialmente identity(1,1)
. Esperaría que este último sea más rápido debido al menor ancho de los enteros, y también parece más simple generar un entero secuencial que un GUID secuencial. Pero para mi sorpresa, los INSERT en la tabla con la tecla entera fueron significativamente más lentos que la tabla secuencial GUID.
Esto muestra el uso de tiempo promedio (ms) para las ejecuciones de prueba:
NEWSEQUENTIALID() 1977
IDENTITY() 2223
¿Alguien puede explicar esto?
Se utilizó el siguiente experimento:
SET NOCOUNT ON
CREATE TABLE TestGuid2 (Id UNIQUEIDENTIFIER NOT NULL DEFAULT NEWSEQUENTIALID() PRIMARY KEY,
SomeDate DATETIME, batchNumber BIGINT, FILLER CHAR(100))
CREATE TABLE TestInt (Id Int NOT NULL identity(1,1) PRIMARY KEY,
SomeDate DATETIME, batchNumber BIGINT, FILLER CHAR(100))
DECLARE @BatchCounter INT = 1
DECLARE @Numrows INT = 100000
WHILE (@BatchCounter <= 20)
BEGIN
BEGIN TRAN
DECLARE @LocalCounter INT = 0
WHILE (@LocalCounter <= @NumRows)
BEGIN
INSERT TestGuid2 (SomeDate,batchNumber) VALUES (GETDATE(),@BatchCounter)
SET @LocalCounter +=1
END
SET @LocalCounter = 0
WHILE (@LocalCounter <= @NumRows)
BEGIN
INSERT TestInt (SomeDate,batchNumber) VALUES (GETDATE(),@BatchCounter)
SET @LocalCounter +=1
END
SET @BatchCounter +=1
COMMIT
END
DBCC showcontig ('TestGuid2') WITH tableresults
DBCC showcontig ('TestInt') WITH tableresults
SELECT batchNumber,DATEDIFF(ms,MIN(SomeDate),MAX(SomeDate)) AS [NEWSEQUENTIALID()]
FROM TestGuid2
GROUP BY batchNumber
SELECT batchNumber,DATEDIFF(ms,MIN(SomeDate),MAX(SomeDate)) AS [IDENTITY()]
FROM TestInt
GROUP BY batchNumber
DROP TABLE TestGuid2
DROP TABLE TestInt
ACTUALIZACIÓN: Modificando el script para realizar las inserciones basadas en una tabla TEMP, como en los ejemplos de Phil Sandler, Mitch Wheat y Martin a continuación, también encuentro que IDENTITY es más rápido como debería ser. Pero esa no es la forma convencional de insertar filas, y todavía no entiendo por qué el experimento salió mal al principio: incluso si omito GETDATE () de mi ejemplo original, IDENTITY () sigue siendo mucho más lento. Por lo tanto, parece que la única forma de hacer que IDENTITY () supere a NEWSEQUENTIALID () es preparar las filas para insertar en una tabla temporal y realizar las muchas inserciones como inserción por lotes utilizando esta tabla temporal. En general, no creo que hayamos encontrado una explicación al fenómeno, e IDENTITY () todavía parece ser más lento para la mayoría de los usos prácticos. ¿Alguien puede explicar esto?
fuente
INT IDENTITY
IDENTITY
no requiere un bloqueo de mesa. Conceptualmente, pude ver que podría esperar que tome MAX (id) + 1, pero en realidad se almacena el siguiente valor. En realidad, debería ser más rápido que encontrar el siguiente GUID.Respuestas:
Modifiqué el código de @Phil Sandler para eliminar el efecto de llamar a GETDATE () (puede haber efectos / interrupciones de hardware involucrados ??), e hice filas de la misma longitud.
[Ha habido varios artículos desde SQL Server 2000 relacionados con problemas de temporización y temporizadores de alta resolución, por lo que quería minimizar ese efecto].
En el modelo de recuperación simple con datos y archivos de registro de tamaño similar al requerido, aquí están los tiempos (en segundos): (actualizado con nuevos resultados basados en el código exacto a continuación)
El código usado:
Después de leer la investigación de @ Martin, volví a correr con el TOP sugerido (@num) en ambos casos, es decir
y aquí están los resultados de tiempo:
¡No pude obtener el plan de ejecución real, ya que la consulta nunca regresó! Parece un error probable. (Ejecución de Microsoft SQL Server 2008 R2 (RTM) - 10.50.1600.1 (X64))
fuente
SORT
operador para los GUID?NEWSEQUENTIALID
todos modos. Hará que el índice sea más profundo, usará un 20% más de páginas de datos en el caso del OP y solo se garantizará que aumente hasta que se reinicie la máquina, por lo que tiene muchas desventajasidentity
. ¡Parece que en este caso el Plan de consulta agrega otro innecesario!En una nueva base de datos en un modelo de recuperación simple con el archivo de datos de 1 GB y el archivo de registro a 3 GB (máquina portátil, ambos archivos en la misma unidad) y el intervalo de recuperación establecido en 100 minutos (para evitar que un punto de control sesgue los resultados) veo resultados similares para usted con una sola fila
inserts
.Probé tres casos: para cada caso hice 20 lotes de inserción de 100,000 filas individualmente en las siguientes tablas. Los scripts completos se pueden encontrar en el historial de revisión de esta respuesta .
Para la tercera tabla, la prueba insertó filas con un
Id
valor incremental , pero esto se calculó incrementando el valor de una variable en un bucle.Promediar el tiempo empleado en los 20 lotes dio los siguientes resultados.
Conclusión
Por lo tanto, definitivamente parece ser una sobrecarga del
identity
proceso de creación responsable de los resultados. Para el entero incremental autocalculado, los resultados están mucho más en línea con lo que se esperaría ver al considerar solo el costo IO.Cuando pongo el código de inserción descrito anteriormente en los procedimientos almacenados y lo reviso,
sys.dm_exec_procedure_stats
se obtienen los siguientes resultadosEntonces, en esos resultados
total_worker_time
es aproximadamente un 30% más alto. Esto representaPor lo tanto, simplemente parece que el código que genera el
IDENTITY
valor es más intensivo en CPU que el que genera elNEWSEQUENTIALID()
(La diferencia entre las 2 cifras es 10231308 que promedia aproximadamente 5 µs por inserto) y que para esta definición de tabla este costo fijo de CPU era lo suficientemente alto como para superar las lecturas y escrituras lógicas adicionales incurridas debido al mayor ancho de la clave. (Nota: Itzik Ben Gan hizo pruebas similares aquí y encontró una penalización de 2 µs por inserción)Entonces, ¿por qué se
IDENTITY
necesita más CPU queUuidCreateSequential
?Creo que esto se explica en este artículo . Por cada décimo
identity
valor generado, SQL Server tiene que escribir el cambio en las tablas del sistema en el disco¿Qué pasa con los insertos MultiRow?
Cuando se insertaron las 100.000 filas en una sola declaración, descubrí que la diferencia desapareció y que tal vez sea un ligero beneficio para el
GUID
caso, pero que ni de lejos resulta tan claro. El promedio de 20 lotes en mi prueba fueLa razón por la que no tiene la penalidad aparente en el código de Phil y el primer conjunto de resultados de Mitch es porque sucedió que el código que usé para hacer la inserción de varias filas utilizada
SELECT TOP (@NumRows)
. Esto evitó que el optimizador estimara correctamente el número de filas que se insertarán.Esto parece ser beneficioso ya que hay un cierto punto de inflexión en el que agregará una operación de clasificación adicional para los (¡supuestamente secuenciales!)
GUID
S.Esta operación de clasificación no es necesaria del texto explicativo en BOL .
Por lo tanto, me pareció un error o falta de optimización que SQL Server no reconoce que la salida del escalar de cómputo ya estará ordenada previamente, como aparentemente ya lo hace para la
identity
columna. ( Editar , informé esto y el problema de clasificación innecesario ahora está solucionado en Denali )fuente
Muy simple: con GUID, es más barato generar el siguiente número en la línea que IDENTIDAD (el valor actual del GUID no tiene que almacenarse, la IDENTIDAD tiene que ser). Esto es cierto incluso para NEWSEQUENTIALGUID.
Podría hacer que la prueba sea más justa y usar un SECUENCIADOR con un CACHÉ grande, que es más barato que IDENTIDAD.
Pero como dice MR, hay algunas ventajas importantes para los GUID. De hecho, son MUCHO más escalables que las columnas IDENTITY (pero solo si NO son secuenciales).
Ver: http://blog.kejser.org/2011/10/05/boosting-insert-speed-by-generating-scalable-keys/
fuente
IDENTITY
. por lo tanto, quejas aquíEstoy fascinado por este tipo de preguntas. ¿Por qué tuviste que publicarlo un viernes por la noche? :)
Creo que incluso si su prueba SOLO está destinada a medir el rendimiento de INSERT, usted (puede) haber introducido una serie de factores que podrían ser engañosos (bucle, una transacción de larga duración, etc.)
No estoy completamente convencido de que mi versión pruebe algo, pero la identidad funciona mejor que los GUID (3.2 segundos frente a 6.8 segundos en una PC doméstica):
fuente
Ejecuté su script de muestra varias veces haciendo algunos ajustes al recuento y tamaño del lote (y muchas gracias por proporcionarlo).
Primero diré que solo está midiendo una vez el aspecto del rendimiento de las teclas: la
INSERT
velocidad. Entonces, a menos que esté específicamente preocupado solo por obtener datos en las tablas lo más rápido posible, hay mucho más para este animal.Mis hallazgos fueron en general similares a los suyos. Sin embargo, mencionaría que la variación en la
INSERT
velocidad entreGUID
yIDENTITY
(int) es ligeramente mayor conGUID
que conIDENTITY
- tal vez +/- 10% entre carreras. Los lotes que usaronIDENTITY
variaron menos del 2 - 3% cada vez.También para tener en cuenta, mi caja de prueba es claramente menos potente que la suya, por lo que tuve que usar recuentos de filas más pequeños.
fuente
Voy a referirme a otra conv en stackoverflow para este mismo tema: https://stackoverflow.com/questions/170346/what-are-the-performance-improvement-of-sequential-guid-over-standard-guid
Una cosa que sí sé es que tener GUID secuenciales es que el uso del índice es mejor debido al muy poco movimiento de las hojas y, por lo tanto, reduce la búsqueda de HD. Creo que debido a esto, las inserciones también serían más rápidas, ya que no tiene que distribuir las claves en una gran cantidad de páginas.
Mi experiencia personal es que cuando implementa una gran base de datos de alto tráfico, es mejor usar GUID, porque la hace mucho más escalable para la integración con otros sistemas. Eso va para la replicación, específicamente, y los límites int / bigint ... no es que te quedes sin bigints, pero eventualmente lo harás, y volverás en ciclo.
fuente