¿Cuáles son las mejores prácticas para usar un GUID como clave principal, específicamente con respecto al rendimiento?

336

Tengo una aplicación que usa GUID como clave principal en casi todas las tablas y he leído que hay problemas de rendimiento al usar GUID como clave primaria. Honestamente, no he visto ningún problema, pero estoy a punto de comenzar una nueva aplicación y todavía quiero usar los GUID como las Claves primarias, pero estaba pensando en usar una Clave primaria compuesta (El GUID y quizás otro campo .)

Estoy usando un GUID porque son agradables y fáciles de administrar cuando tienes diferentes entornos como bases de datos de "producción", "prueba" y "dev", y también para la migración de datos entre bases de datos.

Usaré Entity Framework 4.3 y quiero asignar el Guid en el código de la aplicación, antes de insertarlo en la base de datos. (es decir, no quiero dejar que SQL genere el Guid).

¿Cuál es la mejor práctica para crear Claves primarias basadas en GUID, para evitar los supuestos golpes de rendimiento asociados con este enfoque?

VAAA
fuente
20
El problema no se supone. Si su PK está agrupado, casi todas las inserciones tienen el potencial de causar una división de página. En las versiones modernas de SQL Server, esto se "solucionó" con NEWSEQUENTIALID (), pero pierde el beneficio de poder calcularlo de antemano. Le recomiendo que lea sobre GUID en otros lugares, ya que esta es una pregunta demasiado amplia y probablemente solicitará una batalla religiosa que continuará durante horas ...
Aaron Bertrand
44
También agregaría que el servidor de palabras es ambiguo porque quiero asignar el Guid en el lado del servidor (no quiero permitir que SQL cree el GUID) .
Erik Philips
Esta pregunta tiene similitudes con este "sql-server-guid-sort-Algoritmo-por qué" stackoverflow.com/questions/7810602/…
Clinton Ward

Respuestas:

495

Los GUID pueden parecer una opción natural para su clave principal, y si realmente debe hacerlo, probablemente podría argumentar que la usará para la CLAVE PRIMARIA de la tabla. Lo que recomiendo encarecidamente que no haga es usar la columna GUID como clave de agrupación , que SQL Server hace de manera predeterminada, a menos que se lo indique específicamente.

Realmente necesitas mantener dos problemas separados:

  1. la clave primaria es una construcción lógica, una de las claves candidatas que identifica de manera única y confiable cada fila de su tabla. Esto puede ser cualquier cosa, realmente, un INT, un GUID, una cadena, elija lo que tenga más sentido para su escenario.

  2. la clave de agrupamiento (la columna o columnas que definen el "índice agrupado" en la tabla) - esto es algo relacionado con el almacenamiento físico , y aquí, un tipo de datos pequeño, estable y en constante aumento es su mejor elección, INTo BIGINTcomo su Opción por defecto.

Por defecto, la clave principal en una tabla de SQL Server también se usa como clave de agrupación, ¡pero eso no tiene por qué ser así! Personalmente, he visto ganancias masivas de rendimiento al dividir la Clave primaria / agrupada basada en GUID anterior en dos claves separadas: la clave primaria (lógica) en el GUID y la clave de agrupamiento (orden) en una INT IDENTITY(1,1)columna separada .

Como Kimberly Tripp , la reina de la indexación, y otros han declarado muchas veces, GUIDya que la clave de agrupamiento no es óptima, ya que debido a su aleatoriedad, conducirá a una fragmentación masiva de páginas e índices y a un mal rendimiento en general.

Sí, lo sé, hay newsequentialid()en SQL Server 2005 y versiones posteriores, pero incluso eso no es secuencial y completamente secuencial y, por lo tanto, también sufre los mismos problemas que el GUID, solo que un poco menos prominente.

Luego, hay otro problema a considerar: la clave de agrupación en una tabla se agregará a todas y cada una de las entradas de todos y cada uno de los índices no agrupados en su tabla, por lo que realmente desea asegurarse de que sea lo más pequeña posible. Por lo general, una INTcon más de 2 mil millones de filas debería ser suficiente para la gran mayoría de las tablas, y en comparación con una GUIDclave de agrupación, puede ahorrarse cientos de megabytes de almacenamiento en el disco y en la memoria del servidor.

Cálculo rápido: usando INTvs. GUIDcomo clave principal y de agrupamiento:

  • Tabla base con 1'000'000 filas (3.8 MB vs. 15.26 MB)
  • 6 índices no agrupados (22.89 MB frente a 91.55 MB)

TOTAL: 25 MB frente a 106 MB , ¡y eso es solo en una sola mesa!

Algo más para pensar: cosas excelentes de Kimberly Tripp: léelo, léelo de nuevo, ¡digerirlo! Es el evangelio de indexación de SQL Server, de verdad.

PD: por supuesto, si se trata de unos pocos cientos o miles de filas, la mayoría de estos argumentos realmente no tendrán un gran impacto en usted. Sin embargo: si entras en las decenas o cientos de miles de filas, o comienzas a contar en millones, entonces esos puntos se vuelven cruciales y muy importantes de entender.

Actualización: si desea tener su PKGUIDcolumna como su clave principal (pero no su clave de agrupación) y otra columna MYINT( INT IDENTITY) como su clave de agrupación, use esto:

CREATE TABLE dbo.MyTable
(PKGUID UNIQUEIDENTIFIER NOT NULL,
 MyINT INT IDENTITY(1,1) NOT NULL,
 .... add more columns as needed ...... )

ALTER TABLE dbo.MyTable
ADD CONSTRAINT PK_MyTable
PRIMARY KEY NONCLUSTERED (PKGUID)

CREATE UNIQUE CLUSTERED INDEX CIX_MyTable ON dbo.MyTable(MyINT)

Básicamente: solo tiene que decir explícitamente la PRIMARY KEYrestricción que es NONCLUSTERED(de lo contrario, se crea como su índice agrupado, de forma predeterminada), y luego crea un segundo índice que se define comoCLUSTERED

Esto funcionará, y es una opción válida si tiene un sistema existente que necesita ser "rediseñado" para su rendimiento. Para un nuevo sistema, si comienzas desde cero y no estás en un escenario de replicación, siempre elegiría ID INT IDENTITY(1,1)como mi clave principal agrupada, ¡mucho más eficiente que cualquier otra cosa!

marc_s
fuente
2
Esta es una gran respuesta, una cosa que mencionaría es que poder generar la clave antes de insertar es con frecuencia útil. El uso de "newsequentialid ()" puede ayudar con la agrupación, pero eso requiere un viaje de ida y vuelta adicional a SQL. Por lo tanto, otro beneficio del enfoque de "clave sustituta" es que puede generar nuevos identificadores, del lado del cliente, con menos problemas de fragmentación del índice.
Andrew Theken
2
La forma en que leo esto es que teniendo una columna de identificador único no agrupado y la columna de identidad int, ¿los FK también deberían ser un identificador único? Si lo hace, ¿cuándo usaría la columna de identidad directamente o no?
pinkfloydx33
2
Pequeña pregunta, ¿debería usarse el GUID ahora en uniones o la identificación int? Mi instinto me dice que se debe usar el GUID, pero no veo un problema técnico al usar la identificación int ...
Nicolas Belley
3
@marc_s pero en un escenario de replicación, si la columna int es identidad, ¿no deberíamos usar el GUID ya que la columna int puede repetirse en todos los dispositivos?
Nicolas Belley
66
@Kipei: los problemas principales son si SI tiene un valor tan natural, entonces sí, puede usarlo como clave principal. PERO : valores como, DATETIMEpor ejemplo, NO son útiles para una clave de agrupación, ya que solo tienen una precisión de 3,33 ms y, por lo tanto, pueden existir duplicados. Por lo tanto, en tal caso, * todavía necesita un INT IDENTITYlugar, por lo tanto, normalmente lo uso de manera predeterminada, ya que desde mis más de 20 años de experiencia, una clave natural realmente utilizable casi nunca existe ...
marc_s
51

He estado usando GUID como PK desde 2005. En este mundo de bases de datos distribuidas, es absolutamente la mejor manera de combinar datos distribuidos. Puede disparar y olvidar tablas de fusión sin la preocupación de que las entradas coincidan en tablas unidas. Las combinaciones de GUID se pueden copiar sin ninguna preocupación.

Esta es mi configuración para usar GUID:

  1. PK = GUID. Los GUID se indexan de forma similar a las cadenas, por lo que las tablas de filas altas (más de 50 millones de registros) pueden necesitar particiones de tabla u otras técnicas de rendimiento. SQL Server se está volviendo extremadamente eficiente, por lo que los problemas de rendimiento son cada vez menos aplicables.

  2. PK Guid es un índice NO agrupado. Nunca agrupe el índice de un GUID a menos que sea NewSequentialID. Pero incluso entonces, un reinicio del servidor provocará interrupciones importantes en el pedido.

  3. Agregue ClusterID Int a cada tabla. Este es su índice agrupado ... que ordena su mesa.

  4. Unirme a ClusterIDs (int) es más eficiente, pero trabajo con 20-30 millones de tablas de registros, por lo que unirme a GUIDs no afecta visiblemente el rendimiento. Si desea un rendimiento máximo, use el concepto ClusterID como clave principal y únase a ClusterID.

Aquí está mi tabla de correo electrónico ...

CREATE TABLE [Core].[Email] (
    [EmailID]      UNIQUEIDENTIFIER CONSTRAINT [DF_Email_EmailID] DEFAULT (newsequentialid()) NOT NULL,        
    [EmailAddress] NVARCHAR (50)    CONSTRAINT [DF_Email_EmailAddress] DEFAULT ('') NOT NULL,        
    [CreatedDate]  DATETIME         CONSTRAINT [DF_Email_CreatedDate] DEFAULT (getutcdate()) NOT NULL,      
    [ClusterID] INT NOT NULL IDENTITY,
    CONSTRAINT [PK_Email] PRIMARY KEY NonCLUSTERED ([EmailID] ASC)
);
GO

CREATE UNIQUE CLUSTERED INDEX [IX_Email_ClusterID] ON [Core].[Email] ([ClusterID])
GO

CREATE UNIQUE NONCLUSTERED INDEX [IX_Email_EmailAddress] ON [Core].[Email] ([EmailAddress] Asc)
Robert J. Good
fuente
¿Podría explicar la restricción PK_Email? ¿Por qué tiene ... NonClustered (EmailID ASC) en lugar de ... Nonclustered (ClusterID ASC)?
Phil
2
Usted apuesta. Dos cosas principales suceden con los índices: 1. Agrupado en ClusterID: ordena su tabla en el disco (0% de fragmentación). 2. No agrupado en EmailID: indexa el campo EmailID para acelerar las búsquedas de ID de GUID. Una búsqueda de campo GUID se comporta como string-ish, por lo que una búsqueda de EmailID sería lenta sin el índice.
Robert J. Good
@ RobertJ.Bueno He visto este método discutido antes, es decir, agregar una clave int sustituta para agrupar. Pero no puedo encontrar ningún lugar que muestre la ganancia de rendimiento al tener un índice agrupado de clave sustituta sobre el uso de un montón. ¿Tiene algún enlace a datos de referencia?
Dale K
1
Hola @DaleBurrell, el índice agrupado es para evitar la fragmentación de la tabla. La ganancia de rendimiento ocurre a medida que la tabla crece naturalmente en orden en el disco, con baja fragmentación.
Robert J. Good
@ RobertJ.Good ¿Es una aplicación web? ¿Qué estás usando en urls / hrefs? guid o int?
dariol
10

Actualmente estoy desarrollando una aplicación web con EF Core y aquí está el patrón que uso:

Todas mis clases (tablas) y un int PK y FK. Tengo una columna adicional con el tipo Guid (generado por el constructor de c #) con un índice no agrupado.

Todas las uniones de la tabla dentro de EF se administran a través de las teclas int, mientras que todo el acceso desde el exterior (controladores) se realiza con las Guías.

Esta solución permite no mostrar las claves int en las URL pero mantiene el modelo ordenado y rápido.

EricImhauser
fuente
¿Hay algo que deba hacer para configurar el entero pK como agrupado, como las anotaciones de datos, o simplemente se configura automáticamente?
Allen Wang
¿Cuál es el nombre de la propiedad que usa para Guid one?
Trong Phan
3

Si usa GUID como clave principal y crea un índice agrupado, le sugiero que use el valor predeterminado NEWSEQUENTIALID ()

AnandPhadke
fuente
¿Por qué harías eso?
genuinefafa
3

Este enlace lo dice mejor de lo que podría y me ayudó en la toma de decisiones. Por lo general, opto por un int como clave principal, a menos que tenga una necesidad específica de no hacerlo y también dejo que el servidor SQL genere / mantenga automáticamente este campo a menos que tenga alguna razón específica para no hacerlo. En realidad, las preocupaciones de rendimiento deben determinarse en función de su aplicación específica. Aquí hay muchos factores en juego, que incluyen, entre otros, el tamaño de base de datos esperado, la indexación adecuada, la consulta eficiente y más. Aunque las personas pueden estar en desacuerdo, creo que en muchos escenarios no notará una diferencia con ninguna de las opciones y debe elegir qué es más apropiado para su aplicación y qué le permite desarrollarse más fácil, más rápido y de manera más efectiva (si nunca completa la aplicación) ¿Qué diferencia hace el resto :).

https://web.archive.org/web/20120812080710/http://databases.aspfaq.com/database/what-should-i-choose-for-my-primary-key.html

PD: No estoy seguro de por qué usarías un PK compuesto o qué beneficio crees que te daría.

Mate
fuente
¡¡Totalmente de acuerdo!! Pero eso significa que si tengo un GUID como PK o un PK compuesto con GUID y otro campo será el mismo ¿verdad?
VAAA
1
El PK (índice) estaría compuesto por las dos columnas, pero a menos que tenga algún motivo comercial específico para hacerlo, parece innecesario.
Matt
1
Por cierto, esta pregunta es una de las preguntas más polarizadas y debatidas y, por lo tanto, es extremadamente difícil obtener una respuesta para la que se sienta 100% cómodo. Cualquiera de los métodos viene con compensaciones, así que buena suerte :)
Matt
0

Tener identificaciones secuenciales hace que MUCHO sea más fácil para un pirata informático o minero de datos comprometer su sitio y sus datos. Tenga eso en cuenta al elegir un PK para un sitio web.

DaBlue
fuente
¿Puede proporcionar alguna lógica o evidencia para respaldar esta afirmación? Me cuesta ver cómo una identificación secuencial puede comprometer la seguridad.
jonaglon
Claro, si sabe que los números de identificación son enteros, puede adivinar registros secuenciales en una base de datos. Entonces, si consulta un solo elemento, puede decir que el siguiente elemento es pk + 1. Si tiene GUID aleatorios, no seguirá un patrón. Sería casi imposible consultar otros registros que no haya consultado anteriormente (y conocer el PK).
DaBlue
1
Si un pirata informático puede consultar su base de datos, usted ya está comprometido, no veo cómo los identificadores secuenciales empeoran la situación.
jonaglon
1
Si un usuario puede cambiar 1012 por otro número y ver datos que no deberían ver, entonces hay un problema de seguridad muy serio, ese problema no es causado por la elección de la clave principal, sino que se ve exacerbado por él. Acepto su punto, gracias por explicarlo.
jonaglon
2
Puede usar un GUID para ubicar un registro en la página web, que no es el PK de la tabla. El uso del parámetro de consulta en un sitio web no debería definir cómo estructurar su esquema de base de datos. El PK no tiene nada que ver con la entrada y los parámetros en la interfaz de usuario o el sistema de fondo.
Panos Roditakis