Usar un GUID como clave principal


32

Generalmente uso ID de incremento automático como claves principales en bases de datos. Estoy tratando de aprender los beneficios del uso de GUID. He leído este artículo: https://betterexplained.com/articles/the-quick-guide-to-guids/

Me doy cuenta de que estos GUID se utilizan para identificar objetos a nivel de aplicación. ¿Se almacenan también como la clave principal a nivel de la base de datos? Por ejemplo, digamos que tuve la siguiente clase:

public class Person
{
public GUID ID;
public string Name;
..

//Person Methods follow
}

Digamos que quería crear una nueva persona en la memoria y luego insertar la Persona en una base de datos. ¿Puedo hacer esto?

Person p1 = new Person();
p1.ID=GUID.NewGUID();
PersonRepository.Insert(p1);

Digamos que tenía una base de datos que contenía millones y millones de filas con un GUID como clave principal. ¿Será esto siempre único? ¿Incluso estoy entendiendo los GUID correctamente?

Leí este artículo anteriormente: http://enterprisecraftsmanship.com/2014/11/15/cqs-with-database-generated-ids/ . Me confunde un poco, ya que parece recomendar un medio feliz entre los GUID y los enteros como claves principales.

Editar 06/11/18

He llegado a creer que las guías son más adecuadas que las ins para mis requisitos. Estoy usando CQRS más en estos días y los GUID encajan mejor.

Me doy cuenta de que algunos desarrolladores modelan los GUID como cadenas en el modelo de dominio, por ejemplo, aquí: https://github.com/dotnet-architecture/eShopOnContainers/blob/dev/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/ Buyer.cs : en este caso: IdentityGuid es un GUID modelado como una cadena. ¿Hay alguna razón para hacer esto que no sea lo que se establece aquí: utilizar un objeto de valor personalizado o un Guid como identificador de entidad en un sistema distribuido? . ¿Es "normal" modelar el GUID como una cadena o debería modelarlo como un GUID en el modelo y la base de datos?



77
No se garantiza que sea único, aunque es poco probable que alguna vez veas una colisión. stackoverflow.com/questions/1155008/how-unique-is-uuid/…
icirellik

2
ver también: colisiones UUID
mosquito

2
Consulte también dba.stackexchange.com/questions/54690/… , así como muchas otras preguntas: este tema se ha preguntado, respondido y discutido a menudo.
Greenstone Walker

1
El sistema con el que estoy trabajando en este momento usa UUID. Una buena propiedad es que una ID identifica de forma exclusiva un registro, en oposición a una ID secuencial que identifica un registro en esa tabla.
Justin

Respuestas:


41

Los GUID son, por definición, "identificadores únicos globales". Hay un concepto similar pero ligeramente diferente en Java llamado UUID "IDentifiers universalmente únicos". Los nombres son intercambiables para todo uso práctico.

Los GUID son fundamentales para la forma en que Microsoft imaginó el agrupamiento de bases de datos para trabajar, y si necesita incorporar datos de fuentes a veces conectadas, realmente ayudan a prevenir colisiones de datos.

Algunos hechos pro-GUID:

  • Los GUID evitan colisiones clave
  • Los GUID ayudan a fusionar datos entre redes, máquinas, etc.
  • SQL Server tiene soporte para GUID semi-secuenciales para ayudar a minimizar la fragmentación del índice ( ref , algunas advertencias)

Alguna fealdad con GUID

  • Son grandes, 16 bytes cada uno
  • Están fuera de servicio, por lo que no puede ordenar el ID y esperar obtener el orden de inserción como puede hacerlo con los identificadores de incremento automático
  • Son más engorrosos para trabajar, particularmente en pequeños conjuntos de datos (como tablas de búsqueda)
  • La nueva implementación de GUID es más robusta en SQL Server que en la biblioteca C # (puede tener GUID secuenciales desde SQL Server, en C # es aleatorio)

Los GUID harán que sus índices sean más grandes, por lo que el costo de espacio en disco de indexar una columna será mayor. Los GUID aleatorios fragmentarán sus índices.

Si sabe que no va a sincronizar datos de diferentes redes, los GUID pueden llevar más sobrecarga de lo que valen.

Si necesita ingerir datos de clientes a veces conectados, pueden ser mucho más robustos para evitar colisiones clave que confiar en establecer rangos de secuencia para esos clientes.


18
Entiendo que los GUID son sinónimos de UUID. UUID es el nombre estándar. GUID es lo que Microsoft los acuñó antes de RFC 4122 .
JimmyJames

13
"Están fuera de servicio, por lo que no puede ordenar la identificación y esperar obtener el orden de inserción como puede hacerlo con los identificadores de incremento automático" Francamente, tampoco me siento cómodo confiando en eso con los identificadores regulares. Si bien en un caso extremo extremo es posible que una identificación inferior se confirme en el disco más tarde, prefiero confiar en datos de clasificación útiles, como una marca de tiempo de inserción. Los ID deben tratarse como direcciones de memoria: todo tiene uno, pero el valor en sí no tiene sentido. Úselos como desempate como máximo. Especialmente porque si tiene una carga masiva, no se garantiza el orden de inserción.
Clockwork-Muse

8
@CortAmmon Según Wikipedia y RFC 4122 , son sinónimos. P. Leach de Microsoft fue uno de los creadores de la RFC. Creo que desde que se creó el RFC, los dos son iguales. Del RFC: "UUID (ID único de identificación universal), también conocido como GUID (ID único de identificación global)". Creo que también es útil tener en cuenta que los GUID no fueron creados por MS. Acaban de crear un nuevo nombre para una tecnología adoptada de otros lugares.
JimmyJames

66
"SQL Server tiene optimizaciones para tratar con GUID, por lo que no debería afectar mucho el rendimiento de las consultas". -1 No está lo suficientemente optimizado. Estoy trabajando con una base de datos donde todas las PK son guías, y es una de las principales causas del bajo rendimiento.
Andy

77
"SQL Server tiene optimizaciones para tratar con GUID, por lo que no debería afectar mucho el rendimiento de la consulta " . No es cierto. Esa declaración supone que otros tipos de datos no están optimizados. Los servidores de bases de datos también tienen optimizaciones para tratar con valores int simples, por ejemplo. Los GUID / UUID son mucho más lentos que el uso de un valor int de 4 bytes. 16 bytes nunca serán tan rápidos como 4 bytes, especialmente en una máquina que maneja como máximo 4 u 8 bytes de forma nativa.
Andrew Henle

28

¿Será esto siempre único?

¿Siempre? no, no siempre Es una secuencia finita de bits.

Digamos que tenía una base de datos que contenía millones y millones de filas con un GUID como clave principal.

Millones y millones, probablemente estés a salvo. Un millón de millones, y la probabilidad de una colisión se vuelve significativa. Sin embargo, hay buenas noticias: ya se ha quedado sin espacio en disco para cuando eso suceda.

¿Puedo hacer esto?

Usted puede; No es una buena idea. Su modelo de dominio normalmente no debería generar números aleatorios; deberían ser entradas para su modelo.

Más allá de eso, cuando se trata de una red poco confiable, donde puede obtener mensajes duplicados, un UUID generado de manera determinista lo protegerá de tener entidades duplicadas. Pero si asigna un nuevo número aleatorio a cada uno, entonces tiene más trabajo por hacer para identificar la duplicación.

Vea la descripción del uuid basado en nombre en RFC 4122

¿Es "normal" modelar el GUID como una cadena o debería modelarlo como un GUID en el modelo y la base de datos?

No creo que importe mucho. Para la mayoría de su modelo de dominio, es un identificador ; la única consulta que le haces es si es o no lo mismo que algún otro identificador. Su modelo de dominio normalmente no verá la representación en memoria de un identificador.

Si GUID está disponible como un "tipo primitivo" en su configuración agnóstica de dominio, lo usaría; permite que el contexto de soporte elija las optimizaciones apropiadas que puedan estar disponibles.

Sin embargo, lo que debe reconocer es que la representación del identificador, tanto en la memoria como en el almacenamiento, es una decisión que está tomando en su implementación y, por lo tanto, debe tomar medidas para asegurarse de que la huella del código junto con eso la decisión es pequeña, ver Parnas 1972 .


20
+1 para "ya te has quedado sin espacio en el disco cuando eso sucede".
w0051977

2
Siento que el concepto de " UUID generado de forma determinista " es esencial (ver Data Vault 2)
alk

De hecho, ser capaz de volver a calcular un UUID / GUID basado en otros datos es una gran ayuda, especialmente para detectar duplicados. Una vez construí un sistema de procesamiento de mensajes que almacenaba los mensajes y los empujó a través de una tubería de procesamiento. Creé un hash del mensaje y lo usé como clave principal en todo el sistema. solo eso, en sí mismo, me resolvió MUCHOS problemas para identificar el mensaje cuando tuvimos que escalar.
Newtopian

Un millón millones = 2 ^ 40. Eso hace 2 ^ 79 pares de posibles colisiones. GUID tiene 2 ^ 128 bits, por lo que la probabilidad es de 1 en 2 ^ 49. Es mucho más probable que tenga un error que reutiliza el mismo GUID para dos registros, o que cree erróneamente que hay una colisión donde no la hay.
gnasher729

Estoy volviendo a mis preguntas históricas. Antes de aceptar; ¿podrías echar un vistazo a mi edición?
w0051977

11

Es muy probable que el GUID o UUID sea único debido a cómo se generan y proporcionan una forma segura de garantizar la unicidad sin tener que comunicarse con una autoridad central.

Beneficios de los GUID como clave principal:

  • Puede copiar datos entre diferentes fragmentos de un clúster y no tiene que preocuparse por las colisiones PK.
  • Le permite conocer su clave principal antes de insertar ningún registro.
  • Simplifica la lógica de transacción para insertar registros secundarios.
  • No se puede adivinar fácilmente.

En el ejemplo que proporcionó:

Person p1 = new Person();
p1.ID = GUID.NewGUID();
PersonRepository.Insert(p1);

Especificar el GUID antes del tiempo de inserción puede ahorrar un viaje de ida y vuelta a la base de datos al insertar registros secundarios sucesivos y le permite confirmarlos en la misma transacción.

Person p2 = new Person();
p2.ParentID = p1.ID
PersonRepository.Insert(p2);

En detrimento de los GUID como clave principal:

  • Son grandes 16 bytes, lo que significa que consumirán más espacio a medida que se agreguen índices y claves externas.
  • No se clasifican bien, ya que son esencialmente números aleatorios.
  • El uso del índice es muy, muy, muy malo.
  • Mucha hoja en movimiento.
  • Son difíciles de recordar.
  • Son difíciles de verbalizar.
  • Pueden hacer que las URL sean más difíciles de leer.

Si su aplicación no necesita fragmentar o agrupar, sería mejor quedarse con tipos de datos más pequeños y simples, como int o bigint.

Muchas bases de datos tienen sus propias implementaciones internas que intentan mitigar los problemas de almacenamiento causados ​​por los GUID y SQL Server, incluso tienen una función nueva secuencial para ayudar con el pedido de UUID que permite un mejor uso de los índices y generalmente tienen mejores características de rendimiento.

Además, desde la perspectiva de un probador, usuario o desarrollador que trabaje con la aplicación, usar una ID sobre un GUID mejorará significativamente la comunicación. Imagine tener que leer un GUID por teléfono.

Al final, a menos que sea un clúster a gran escala u ofuscar URLs es un requisito, es más pragmático seguir con ID de incremento automático.


1
Una cosa a tener en cuenta es que, según el tipo de UUID , contienen información que podría usarse para identificar la máquina en la que se generan. La variante aleatoria pura puede ser más propensa a colisionar sin suficiente entropía. Esto debe considerarse antes de su uso en un URI.
JimmyJames

De acuerdo, aunque uno nunca debe exponer su clave principal en una URL. Se debe utilizar un método más apropiado para garantizar que no haya fugas de datos seguras a un sistema externo
S

1
Hay un caso de uso más: bases de datos OLTP de inserción pesada en las que el bloqueo de la secuencia es un cuello de botella. Según mi amigo Oracle DBA, esto no es tan raro como parece, ni siquiera necesita gran escala o grupos para eso. • Al final, pondere los pros y los contras (y no confunda los pros / contras de los UUID con los pros / contras que no son específicos de los UUID como lo hacen algunos pósters) y mida .
mirabilos

1
Si usa newsequentialid, entonces tiene que ir a la base de datos para obtener la identificación (como con una identidad int), ¿no? ¿Cuál es el beneficio aquí?
w0051977

1
@mirabilos Para ser claros, cuando digo horrible, terminamos teniendo inserciones que demoraban minutos por fila. Comenzó bien, pero después de que hubo 10 de miles de filas, se fue muy rápido. Si no es obvio, 10 de miles de filas es una tabla muy pequeña.
JimmyJames

4

Yo diría que no, no use GUID como claves principales. En realidad estoy lidiando con una base de datos de este tipo ahora, y son una de las principales causas de problemas de rendimiento.

Los 12 bytes adicionales se suman rápidamente; recuerde, la mayoría de las PK serán FK en otras tablas, y solo tres FK en una tabla ahora tiene 48 bytes adicionales por cada fila. Eso se suma en la tabla y en los índices. También se suma en el disco de E / S. Esos 12 bytes adicionales deben leerse y escribirse.

Y si no está usando guías secuenciales y las PK están agrupadas (que es lo que sucede de manera predeterminada), SQL de vez en cuando tendrá que mover páginas enteras de datos para exprimir más en el "lugar" correcto. Para una base de datos altamente transaccional con muchas inserciones, actualizaciones y eliminaciones, las cosas se estancan rápidamente.

Si necesita algún tipo de identificador único para la sincronización o algo así, agregue una columna guid. Simplemente no lo hagas PK.


4
Person p1 = new Person();
p1.ID=GUID.NewGUID();
PersonRepository.Insert(p1);

Esta es, con mucho, la razón más importante para usar GUID.

El hecho de que pueda crear una identificación única sin que su código conozca o se comunique con su capa de persistencia es un gran beneficio.

Puede estar seguro de que el objeto Persona que acaba de generar en su servidor, teléfono PC, computadora portátil, dispositivo fuera de línea o lo que sea único en todos sus servidores en todo el mundo, sin embargo, se distribuye.

Puede pegarlo en cualquier tipo de base de datos rdb o no-sql, archivo, enviarlo a cualquier servicio web o tirarlo inmediatamente como no sea necesario

No, nunca tendrás una colisión.

Sí, las inserciones pueden ser un poco más lentas ya que es posible que sea necesario manipular el índice.

Sí, es más grande que un int.

  • editar. tuvo que disparar antes de terminar.

Sé que muchas personas se sienten fuertemente acerca de las entradas de automóviles y este es un tema controvertido con los DBA

Pero realmente no puedo decir con la suficiente fuerza lo superiores que son las guías. Debe usar guías por defecto en cualquier aplicación.

las entradas de auto inc tienen muchos defectos

  • Utiliza un DB distribuido sin SQL. Simplemente no puede hablar con todas las demás instancias para averiguar cuál es el siguiente número.

  • Utiliza un sistema de cola de mensajes. Las cosas necesitan identificadores antes de llegar a la base de datos

  • Está creando varios elementos y editándolos antes de guardarlos. Cada uno necesita una identificación antes de llegar a la base de datos

  • Desea eliminar y volver a insertar filas. ¡Asegúrate de no contar tus ID de auto inc y agotarte!

  • No desea exponer la cantidad de pedidos que ha tomado este año a cada usuario

  • Desea mover datos anónimos de producción para probar y mantener intactas las relaciones. Pero no elimine todos los datos de prueba existentes.

  • Desea fusionar su producto de inquilino único en una base de datos multicliente, pero todos tienen un pedido 56.

  • Crea objetos que son persistentes pero efímeros. (pedidos incompletos) nuevamente, no use todas sus entradas con cosas que ya no existen.

La lista es interminable y todos son problemas reales que le pasan a las personas todo el tiempo. a diferencia de quedarse sin espacio en disco debido a cols FK ligeramente más grandes

Finalmente el problema masivo con enteros se ejecuta fuera de ellos !!! ok en teoría no, hay un montón. Pero en la práctica lo haces porque la gente no los trata como números aleatorios sin significado. hacen cosas como

  • oh, no quiero que los clientes piensen que somos nuevos. comenzar en 10,000

  • Tuve que importar una carga de datos, así que simplemente subí la semilla a 1 m para que sepamos qué se importa

  • Necesitamos categorías de datos. cada período comienza en el próximo millón para que podamos usar los primeros dígitos como un número mágico

  • Eliminé y volví a importar todos los datos con nuevos identificadores. Sí, incluso los registros de auditoría.

  • use este número, que es una clave compuesta, como la identificación de esta otra cosa


1
Esta respuesta no tiene nada de malo, pero (para evitar más votos negativos) tal vez explique la advertencia de que, aunque las aplicaciones de la vida real no encuentren colisiones, es teóricamente posible. (O tal vez más de 45 bases de datos exabytes son más frecuentes de lo que pensaba ...). Aunque creo que el lenguaje "la razón más importante" es un poco fuerte, esto es lo que considero más útil.
BurnsBA

2
es más probable que un auto inc int choque contra un guid
Ewan

44
-1 para "Debería usar guías por defecto en cualquier aplicación". Depende ™. Y como otros han demostrado, los GUID / UUID no están garantizados para ser únicos.
Max Vernon

3
Las respuestas "depende" son inútiles, seguro que habrá algunas aplicaciones extrañas donde un int es mejor. Pero es probable que su aplicación no sea una de ellas. Los GUID son lo más exclusivo que puede obtener
Ewan

2
Creo que habrá algunas aplicaciones extrañas donde las guías son mejores. Único no es lo más importante a considerar. Sus "fallas" de ints son exageradas, y no considera ninguno de los muchos inconvenientes de las guías.
Andy

2

Me doy cuenta de que estos GUID se utilizan para identificar objetos a nivel de aplicación. ¿Se almacenan también como la clave principal a nivel de la base de datos?

Ahí es donde debes detenerte, allí mismo, y repensar.

La clave principal de su base de datos NUNCA debe tener un significado comercial. No debería tener sentido por definición.

Por lo tanto, agregue el GUID como clave empresarial y una clave primaria normal (generalmente una int larga) como clave primaria de la base de datos. Siempre puede poner un índice único en el GUID para garantizar la unicidad.

Eso es teoría de bases de datos, por supuesto, pero también es una buena práctica. He tratado con bases de datos en las que las claves principales tenían un significado comercial (un cliente había pensado ahorrar algunos recursos de la base de datos al usarlas como números de empleados, números de clientes, etc., etc.) y siempre genera problemas.


1
¿En qué se diferencia esto de consultar desde la capa de aplicación usando una clave primaria entera? En ese punto, también se está utilizando para identificar objetos en la capa de aplicación. Necesita una forma de identificar objetos en una base de datos desde la capa de aplicación.
icirellik

@icirellik la clave primaria está destinada para uso interno de la base de datos, para vincular registros primarios y secundarios y similares. NO está destinado a ser usado por la lógica de la aplicación, usted usa ID de negocios para eso, como un número o nombre de producto.
Jwenting

2

Utilice siempre las claves primarias (PK) de autogeneración generadas por la base de datos.

¿Por qué utilizar el incremento automático en lugar de GUID / UUID?

  • Los GUID (UUID) no evitan las colisiones de teclas, ya que no son únicas y no hay forma de hacerlas únicas, ya que se generan a partir de numerosas fuentes.
  • Los GUID no ayudan con la fusión, ya que aumentan en gran medida el proceso de fusión, que ya lleva mucho tiempo, con columnas PK y FK no enteras extremadamente largas que requieren mucho tiempo para procesar. Recuerde que para la mayoría de las PK, habrá al menos otra tabla con al menos 2 claves del mismo tamaño: su propia PK y un FK volverán a la primera tabla. Todos tienen que resolverse en una fusión.

¿Pero cómo manejar fragmentos, racimos, etc.?

  • Cree PK de varias columnas formadas por columnas separadas que identifiquen cada fragmento / clúster / base de datos / lo que sea que gestione sus propias claves de incremento automático. Por ejemplo...

Un PK de 3 columnas para una tabla agrupada podría ser ...

 DB | SH | KEY     |
----|----|---------|
 01 | 01 | 1234567 |

Pero que pasa...?

  • Múltiples viajes a la base de datos: la mayoría de las aplicaciones no tienen necesidad de identificar de forma exclusiva un registro que se está creando hasta que se inserta en la base de datos, ya que ese hilo / sesión / lo que sea solo funciona en uno a la vez. Si la aplicación realmente necesita esta capacidad, use una PK temporal generada por la aplicación que no se envíe a la base de datos . Deje que la base de datos coloque su propia PK de incremento automático en la fila cuando se inserte. Las inserciones usarán la PK temporal, mientras que las actualizaciones y eliminaciones usarán la PK permanente asignada por la base de datos.

  • Rendimiento: las computadoras pueden procesar enteros simples mucho más rápido que cualquier otra cosa debido al dominio mucho mayor si es posible los valores por elemento en un GUID (37) frente a un entero (10). Recuerde también que cada carácter en un GUID primero debe convertirse en un número para ser manipulado por la CPU.

Uso indebido común de las claves primarias Las PK tienen un solo propósito ... identificar de manera absolutamente única una fila en una tabla. Cualquier otra cosa es un mal uso muy común.

Detectar registros faltantes

  • Los registros que faltan no se pueden detectar mirando las PK. Bendice QA por al menos intentar garantizar la calidad de los datos. Sin embargo, ellos y la falta de comprensión del programador de cómo se asignan las claves en los sistemas de bases de datos modernas a menudo los lleva a la creencia errónea de que un número faltante en un PK de incremento automático significa datos faltantes. Lo hace no porque ...
  • Para el rendimiento, los sistemas de bases de datos asignan bloques de números en 'secuencias' (lotes, rangos) para minimizar los viajes a la base de datos real almacenada. El tamaño de estas secuencias de números a menudo está bajo el control del DBA, pero es posible que no se pueda ajustar por tabla.
  • La conclusión clave es que ... los números no utilizados de estas secuencias nunca se devuelven a la base de datos, por lo que siempre hay espacios en blanco en los números PK.
  • ¿Por qué habría números no utilizados que preguntas? Debido a que una variedad de acciones de mantenimiento de la base de datos puede provocar el abandono de secuencias. Estas son cosas como reinicios, recargas masivas de tablas, algunos tipos de restauración desde copias de seguridad y algunas otras operaciones.

Clasificación

  • Ordenar por PK es muy propenso a errores ya que la mayoría de las personas pensarán que enumera las filas en el orden en que fueron creadas y que corresponde a la hora del reloj. Sobre todo, pero no necesariamente.
  • Los motores de base de datos están optimizados para un rendimiento máximo y eso puede significar retrasar la inserción de los resultados de una transacción complicada de larga duración para insertar transacciones simples y cortas, "fuera de turno", por así decirlo.

¿Qué piensa sobre el esquema de la tabla, de modo que la única columna única es una clave primaria de incremento automático creada por la base de datos? ¿Particularmente para tablas que no tienen clave externa pero cuya clave principal es la clave externa para varias tablas relacionadas?
RibaldEddie

He agregado mucho más a la respuesta en ese sentido. La respuesta original era incompleta debido a la aplicación Android SE que estoy colgando. Creo que una reescritura importante de la aplicación está en desarrollo.
DocSalvager

Entonces, en su opinión, ¿estaría bien que una tabla contuviera cualquier número de filas que fueran idénticas salvo por su clave primaria de incremento automático?
RibaldEddie

@RibaldEddie: en cuanto a lo que el DB está diseñado para permitir ... absolutamente. Las eliminaciones son fáciles. Cuando ocurra su escenario, consideraría que se corrigió un error en el software y luego eliminaría cualquier fila. Sin embargo, el caso mucho más común son dos registros para la misma cosa con datos ligeramente diferentes, por lo que deben fusionarse. Si una columna está vacía en un registro y tiene un valor en el otro, la elección es obvia y puede automatizarse. A menudo, la marca de fecha y hora se puede utilizar para arbitrar una fusión automática. Algunos duplicados requieren que una persona finalice y verifique la fusión según las reglas comerciales.
DocSalvager

1

Como todo, hay ventajas y desventajas de hacer esto:

El bueno:

  1. Sus claves siempre tienen la misma longitud (las bases de datos muy grandes pueden tener claves muy grandes)

  2. La unicidad está prácticamente garantizada, incluso cuando las genera desde un sistema separado y / o no ha leído la última ID de la base de datos

El malo:

  1. Como se mencionó mucho más arriba: índices más grandes y almacén de datos.

  2. No puede ordenar por ID, tiene que ordenar por otra cosa. Más índices, probablemente menos eficientes.

  3. Son menos legibles para los humanos. Los enteros son generalmente más fáciles de analizar, recordar y escribir para las personas. El uso de GUID como ID en las cláusulas WHERE en varias tablas unidas puede hacer que su cabeza se derrita.

Como todo, úselos donde sea apropiado, no sea dogmático: en muchas situaciones, los enteros de incremento automático son mejores, ocasionalmente los GUID son geniales.


0

Sí, puede usar GUID como clave principal. El lado negativo es el tamaño y la fragmentación rápida del índice.

A menos que necesite unicidad en las bases de datos (por ejemplo, un clúster), se prefiere un entero.


Los generadores de GUID pueden producir el mismo GUID más de una vez, ahí radica una falla. Si lo harán o no depende de su granularidad, principalmente del intervalo entre los tics del reloj. Por ejemplo, un generador basado en reloj solo puede marcar cada 100 ms, lo que lleva a que se soliciten 2 GUID dentro de esos 100 ms en esa máquina. Hay formas de evitar eso, principalmente, pero muchos generadores GUID funcionan completamente fuera de la dirección IP y / o la dirección MAC y una marca de tiempo.
Jwenting

0

Aquí está mi opinión sobre este tema: la solución es un punto medio entre los valores GUID e int, tomando lo mejor de ambos.

La clase genera un valor de Id pseudoaleatorio (pero que aumenta con el tiempo), que es similar a un GUID de peine .

La ventaja clave es que permite que se generen valores de Id en el cliente, en lugar de usar valores de incremento automático generados en el servidor (que requiere un viaje de ida y vuelta) con un riesgo casi nulo de valores duplicados.

Los valores generados solo usan 8 bytes en lugar de 16 para un GUID, y no dependen de un orden de clasificación de base de datos específico (por ejemplo, Servidor SQL para GUID ). Los valores podrían expandirse para usar todo el rango largo sin signo, pero esto causaría problemas con cualquier base de datos u otro repositorio de datos que solo tenga tipos enteros con signo.

public static class LongIdGenerator
{
    // set the start date to an appropriate value for your implementation 
    // DO NOT change this once any application that uses this functionality is live, otherwise existing Id values will lose their implied date
    private static readonly DateTime PeriodStartDate = new DateTime(2017, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    private static readonly DateTime PeriodEndDate = PeriodStartDate.AddYears(100);
    private static readonly long PeriodStartTicks = PeriodStartDate.Ticks;
    private static readonly long PeriodEndTicks = PeriodEndDate.Ticks;
    private static readonly long TotalPeriodTicks = PeriodEndTicks - PeriodStartTicks;

    // ensures that generated Ids are always positve
    private const long SEQUENCE_PART_PERMUTATIONS = 0x7FFFFFFFFFFF; 

    private static readonly Random Random = new Random();

    private static readonly object Lock = new object();
    private static long _lastSequencePart;

    public static long GetNewId()
    {
        var sequencePart = GetSequenceValueForDateTime(DateTime.UtcNow);

        // extra check, just in case we manage to call GetNewId() twice before enough ticks have passed to increment the sequence 
        lock (Lock)
        {
            if (sequencePart <= _lastSequencePart)
                sequencePart = _lastSequencePart + 1;

            _lastSequencePart = sequencePart;
        }

        // shift so that the sequence part fills the most significant 6 bytes of the result value
        sequencePart = (sequencePart << 16);

        // randomize the lowest 2 bytes of the result, just in case two different client PCs call GetNewId() at exactly the same time
        var randomPart = Random.Next() & 0xFFFF;

        return sequencePart + randomPart;
    }

    // used if you want to generate an Id value for a historic time point (within the start and end dates)
    // there are no checks, compared to calls to GetNewId(), but the chances of colliding values are still almost zero
    public static long GetIdForDateTime(DateTime dt)
    {
        if (dt < PeriodStartDate || dt > PeriodStartDate)
            throw new ArgumentException($"value must be in the range {PeriodStartDate:dd MMM yyyy} - {PeriodEndDate:dd MMM yyyy}");

        var sequencePart = GetSequenceValueForDateTime(dt.ToUniversalTime());
        var randomPart = Random.Next() & 0xFFFF;
        return ( sequencePart << 16 ) + randomPart;
    }

    // Get a 6 byte sequence value from the specified date time - startDate => 0 --> endDate => 0x7FFFFFFFFFFF
    // For a 100 year time period, 1 unit of the sequence corresponds to about 0.022 ms
    private static long GetSequenceValueForDateTime(DateTime dt)
    {
        var ticksFromStart = dt.ToUniversalTime().Ticks - PeriodStartTicks;
        var proportionOfPeriod = (decimal)ticksFromStart / TotalPeriodTicks;
        var result = proportionOfPeriod * SEQUENCE_PART_PERMUTATIONS;
        return (long)result;
    }

    public static DateTime GetDateTimeForId(long value)
    {
        // strip off the random part - the two lowest bytes
        var timePart = value >> 16;
        var proportionOfTotalPeriod = (decimal) timePart / SEQUENCE_PART_PERMUTATIONS;
        var ticks = (long)(proportionOfTotalPeriod * TotalPeriodTicks);
        var result = PeriodStartDate.AddTicks(ticks);
        return result;
    }
}
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.