2009-03-20 11 views
20

Acabo de leer un blog post sobre la capacidad de NHibernate para crear un GUID a partir de la hora del sistema (Guid.Comb), evitando así una buena cantidad de fragmentación de la base de datos. Podría llamarlo el equivalente del lado del cliente al ID secuencial de SQL Server.GUID secuencial en Linq-to-Sql?

¿Hay alguna manera de que yo pueda usar una estrategia similar en mi proyecto Linq-to-Sql (generando el código Guid)?

+0

Probé todas estas muestras de GUI de GUI y todas generan una gran fragmentación con 10K filas. Versión de Arul: 98% de fragmentación.
NHibernate - 53% de fragmentación.
rpcrt4.dll - 98% de fragmentación
bigint - 6% fragmentation
¿Qué tan bien esperan las personas que los productos COMB funcionen? – Shaun

+0

@Shaun esto depende de qué tan rápido inserte, si solo tiene algunas inserciones por segundo no debería haber fragmentación. – Peter

Respuesta

9

peines se generan de la siguiente manera:

DECLARE @aGuid UNIQUEIDENTIFIER 

SET @aGuid = CAST(CAST(NEWID() AS BINARY(10)) + CAST(GETDATE() AS BINARY(6)) AS UNIQUEIDENTIFIER) 

Qué transcribe en C# se vería así:

public static unsafe Guid CombGuid() 
    { 
     Guid guid = Guid.NewGuid(); 
     byte[] bytes = guid.ToByteArray(); 
     long ticks = DateTime.Now.Ticks; 
     fixed(byte* pByte = bytes) 
     { 
      int* pFirst = (int *)(pByte + 10); 
      short* pNext = (short*)(pByte + 14); 
      *pFirst = (int)(ticks & 0xFFFFFF00); 
      *pNext = (short)ticks; 
     } 

     return new Guid(bytes); 
    } 
+0

Esto también sería muy útil para mí, pero ¿tendría que compilar todo mi programa con/inseguro o podría ponerlo en su propia Biblioteca de clases y compilar solo que con la bandera insegura? –

+0

@Paladin: puede hacerlo sin recurrir a un código inseguro. Eche un vistazo a la clase BitConvert –

+1

Consulte mi respuesta a continuación para obtener una versión segura de la generación de Guid.Comb. – Doug

3

Bueno, podría generar el Guid a mano. Sin embargo, una de las ventajas de un Guid es que no es fácil de adivinar - es decir, dado el historial 0000-...-0005, suele haber mucho sentido (de un atacante) la comprobación de registro 0000-....-0004 etc.

También - re fragmentación? Siempre que tenga un índice no agrupado en estos datos, no estoy seguro de que esto sea un problema. Normalmente no colocaría un índice agrupado en un Guid, por lo que la tabla será un montón (a menos que tenga un índice agrupado por separado, como un IDENTITY int). En ese caso, agregará al final e insertará el nuevo Guid en el índice no agrupado. Sin dolor real

(editar) Un problema de utilizar el tiempo directamente es que introduce mucho más riesgo de colisión; tendría que preocuparse por la creación de Guid de ciclo cerrado (es decir, evitando la repetición al crear unos pocos en secuencia), lo que significa sincronización, etc., y se vuelve aún más problemático si varias máquinas trabajan intensivamente en paralelo; es probable que obtenga duplicados

+0

La solución que sugerí como la respuesta correcta combina un Guid arbitrario con una parte generada en el tiempo, lo que elimina el riesgo de duplicados. Aunque no sé si resuelve los problemas de fragmentación ... – JacobE

51

C# (seguro) código (Elogios del Guid peine Generador NHibernate)

Guid GenerateComb() 
{ 
    byte[] destinationArray = Guid.NewGuid().ToByteArray(); 
    DateTime time = new DateTime(0x76c, 1, 1); 
    DateTime now = DateTime.Now; 
    TimeSpan span = new TimeSpan(now.Ticks - time.Ticks); 
    TimeSpan timeOfDay = now.TimeOfDay; 
    byte[] bytes = BitConverter.GetBytes(span.Days); 
    byte[] array = BitConverter.GetBytes((long) (timeOfDay.TotalMilliseconds/3.333333)); 
    Array.Reverse(bytes); 
    Array.Reverse(array); 
    Array.Copy(bytes, bytes.Length - 2, destinationArray, destinationArray.Length - 6, 2); 
    Array.Copy(array, array.Length - 4, destinationArray, destinationArray.Length - 4, 4); 
    return new Guid(destinationArray); 
} 

Un enlace a la fuente en github: https://github.com/nhibernate/nhibernate-core/blob/master/src/NHibernate/Id/GuidCombGenerator.cs

+1

¡Acaba de ser agregado a mi proyecto! –

+1

Me alegra oírlo. Encontré esta pequeña joya en la fuente NHibernate y simplemente tuve que compartirla :) – Doug

3

Siempre puede llamar a UuidCreateSequential; este es el "viejo" generador de guid (antes de 2000 cuando MSFT lo cambió a las guías de estilo más aleatorias a las que estamos acostumbrados hoy). Cambiaron el nombre de la antigua UuidCreate a UuidCreateSequential, y pusieron su nuevo generador de guid en una nueva implementación de UuidCreate. UuidCreateSequential es también lo que SQL Server usa en NewSequentialID(), y es tan único como las guías normales, pero con la ventaja de que son secuenciales si crea una pila de ellas seguidas en el mismo proceso.

using System; 
using System.Runtime.InteropServices; 

namespace System 
{ 
    public static class GuidEx 
    { 
     [DllImport("rpcrt4.dll", SetLastError = true)] 
     private static extern int UuidCreateSequential(out Guid guid); 
     private const int RPC_S_OK = 0; 

     /// <summary> 
     /// Generate a new sequential GUID. If UuidCreateSequential fails, it will fall back on standard random guids. 
     /// </summary> 
     /// <returns>A GUID</returns> 
     public static Guid NewSeqGuid() 
     { 
      Guid sequentialGuid; 
      int hResult = UuidCreateSequential(out sequentialGuid); 
      if (hResult == RPC_S_OK) 
      { 
       return sequentialGuid; 
      } 
      else 
      { 
       //couldn't create sequential guid, fall back on random guid 
       return Guid.NewGuid(); 
      } 
     } 
    } 
} 
+2

FYI, si estás usando Mono, entonces rpcrt4.dll no existirá, y esto no funcionará. – Doug

+0

Buen punto Doug. Uso UuidCreateSequential, pero me olvidé de este snapful. – granadaCoder

+0

¿Qué hacer si la máquina se reinicia? entonces perderás la secuencia –

2

@arul, @Doug

¿Por qué se pone la parte de tiempo al final del GUID?

Pensé que los bytes iniciales son más significativos para ordenar, y ordenar por qué la parte de tiempo se introdujo en primer lugar para evitar la fragmentación del índice.

Ok, encontré el answer, y este answer from Bernhard Kircher y el sitio Comparing GUID and uniqueidentifier Values (ADO.NET) al que hace referencia.

Los GUID generados de esta manera, por lo tanto, no funcionarían de la misma manera en otras bases de datos que MS SQL-Server, pero esto no está relacionado con LINQ-to-SQL.

Disculpe las URL deformadas, pero no tengo la reputación suficiente para publicar más enlaces.

0

Utilizamos un método similar al que Doug publicó anteriormente en el modelo de Entity Framework, por lo que debe poder usar también Linq to SQL.

Mientras se hace esto, necesita un generador GUID peine para las pruebas, y terminó la construcción de esta pequeña herramienta para generar guids peine línea

http://www.webdesigncompany.co.uk/comb-guid/

Es de esperar que le ayudará también.