Pregunta

¿Alguien ha medido alguna vez el rendimiento de la Guía secuencial frente a la Guía estándar cuando se usa como Claves primarias dentro de una base de datos?

¿Fue útil?

Solución

GUID vs.Secuencial GUID



Un patrón típico es usar Guid como PK para tablas, pero, como se mencionó en otras discusiones (vea Ventajas y desventajas de las claves de base de datos GUID / UUID ) Hay algunos problemas de rendimiento.

Esta es una secuencia típica de Guid

f3818d69-2552-40b7-a403-01a6db4552f7
    7ce31615-fafb-42c4-b317-40d21a6a3c60
    94732fc7-768e-4cf2-9107-f0953f6795a5
    

Los problemas de este tipo de datos son: & Lt;
    -

  • Distribuciones amplias de valores
  • Casi al azar
  • El uso del índice es muy, muy, muy malo
  • Muchas hojas moviéndose
  • Casi todas las PK deben ser al menos en un índice no agrupado
  • El problema ocurre tanto en Oracle como en Servidor SQL



Una posible solución es utilizar Guid secuencial, que se generan de la siguiente manera:

    cc6466f7-1066-11dd-acb6-005056c00008
    cc6466f8-1066-11dd-acb6-005056c00008
    cc6466f9-1066-11dd-acb6-005056c00008


Cómo generarlos desde el código C #:

[DllImport("rpcrt4.dll", SetLastError = true)]
static extern int UuidCreateSequential(out Guid guid);

public static Guid SequentialGuid()
{
    const int RPC_S_OK = 0;
    Guid g;
    if (UuidCreateSequential(out g) != RPC_S_OK)
        return Guid.NewGuid();
    else
        return g;
}


Beneficios

  • Mejor uso del índice
  • Permitir el uso de claves agrupadas (para ser verificado en escenarios NLB)
  • Menos uso de disco
  • 20-25% de aumento de rendimiento en un costo mínimo



Medición de la vida real: Escenario:

  • Guid almacenado como UniqueIdentifier tipos en SQL Server
  • Guid almacenado como CHAR (36) en Oracle
  • Lote de operaciones de inserción, por lotes juntos en una sola transacción
  • De 1 a 100s de insertos dependiendo en la mesa
  • Algunas tablas > 10 millones de filas



Prueba de laboratorio & # 8211; Servidor SQL
Prueba VS2008, 10 usuarios simultáneos, sin tiempo de reflexión, proceso de referencia con 600 inserciones en lote para la tabla de hojas
Guía estándar
Prom. Duración del proceso: 10.5 segundos
Prom. Solicitud de segundo: 54.6
Prom. Resp. Hora: 0.26

Guía secuencial
Prom. Duración del proceso: 4.6 segundos
Prom. Solicitud de segundo: 87.1
Prom. Resp. Hora: 0.12

Resultados en Oracle (lo siento, se utilizó una herramienta diferente para la prueba) 1.327.613 insertar en una tabla con un PK Guid

Guía estándar , 0.02 seg. tiempo transcurrido para cada inserción, 2.861 seg. del tiempo de CPU, total de 31.049 seg. transcurrido

Guía secuencial , 0.00 seg. tiempo transcurrido para cada inserción, 1.142 seg. de tiempo de CPU, total de 3.667 seg. transcurrido

El tiempo de espera de lectura secuencial del archivo DB pasó de 6.4 millones de eventos de espera durante 62.415 segundos a 1.2 millones de eventos de espera para < strong> 11.063 segundos.

Es importante ver que se puede adivinar todo el guid secuencial, por lo que no es una buena idea usarlos si la seguridad es una preocupación, aún usando guid estándar.
Para abreviar ... si usa Guid como PK use guid secuencial cada vez que no se pasan de una interfaz de usuario a otra, acelerarán la operación y no costará nada implementarla.

Otros consejos

Puede que me falte algo aquí (siéntase libre de corregirme si lo estoy), pero puedo ver muy pocos beneficios al usar GUID / UUID secuenciales para las claves principales.

El punto de usar GUID o UUID sobre enteros automáticos es:

  • Se pueden crear en cualquier lugar sin contactar con la base de datos
  • Son identificadores que son completamente únicos dentro de su aplicación (y en el caso de UUID, universalmente únicos)
  • Dado un identificador, no hay forma de adivinar el siguiente o anterior (o incluso ninguno otros identificadores válidos) fuera de la fuerza bruta de un espacio de teclas enorme .

Desafortunadamente, usando su sugerencia, pierde todas esas cosas.

Entonces, sí. Has mejorado los GUID. Pero en el proceso, ha descartado casi todas las razones para usarlas en primer lugar.

Si realmente desea mejorar el rendimiento, utilice una clave primaria de entero con incremento automático estándar. Eso proporciona todos los beneficios que describió (y más) a la vez que es mejor que una 'guía secuencial' en casi todos los sentidos.

Es muy probable que esto quede en el olvido, ya que no responde específicamente a su pregunta (que aparentemente está cuidadosamente diseñada para que pueda responderla usted mismo de inmediato), pero creo que es un punto mucho más importante que plantear.

Como ya dijo massimogentilini, el rendimiento se puede mejorar cuando se usa UuidCreateSequential (al generar las guías en el código). Pero parece faltar un hecho: el SQL Server (al menos Microsoft SQL 2005/2008) usa la misma funcionalidad, PERO: la comparación / ordenación de Guías difiere en .NET y en el Servidor SQL, lo que aún causaría más IO, porque las guías no se ordenarán correctamente. Para generar las guías ordenadas correctamente para el servidor SQL (pedidos), debe hacer lo siguiente (consulte detalles de comparación ):

[System.Runtime.InteropServices.DllImport("rpcrt4.dll", SetLastError = true)]
static extern int UuidCreateSequential(byte[] buffer);

static Guid NewSequentialGuid() {

    byte[] raw = new byte[16];
    if (UuidCreateSequential(raw) != 0)
        throw new System.ComponentModel.Win32Exception(System.Runtime.InteropServices.Marshal.GetLastWin32Error());

    byte[] fix = new byte[16];

    // reverse 0..3
    fix[0x0] = raw[0x3];
    fix[0x1] = raw[0x2];
    fix[0x2] = raw[0x1];
    fix[0x3] = raw[0x0];

    // reverse 4 & 5
    fix[0x4] = raw[0x5];
    fix[0x5] = raw[0x4];

    // reverse 6 & 7
    fix[0x6] = raw[0x7];
    fix[0x7] = raw[0x6];

    // all other are unchanged
    fix[0x8] = raw[0x8];
    fix[0x9] = raw[0x9];
    fix[0xA] = raw[0xA];
    fix[0xB] = raw[0xB];
    fix[0xC] = raw[0xC];
    fix[0xD] = raw[0xD];
    fix[0xE] = raw[0xE];
    fix[0xF] = raw[0xF];

    return new Guid(fix);
}

o este enlace o este enlace .

Si necesita usar GUI secuenciales, SQL Server 2005 puede generarlos para usted con la función NEWSEQUENTIALID().

Sin embargo, dado que el uso básico de las GUI es generar claves (o claves alternativas) que no se pueden adivinar (por ejemplo, para evitar que las personas pasen las claves adivinadas en GET), no veo qué tan aplicable son porque son muy fáciles de adivinar.

De MSDN :

  

Importante:
  Si la privacidad es una preocupación, no use esta función. Eso   es posible adivinar el valor de la   siguiente GUID generado y, por lo tanto,   acceder a los datos asociados con ese GUID.

Ver este artículo: ( http://www.shirmanov.com/2010/05/generating- newsequentialid-compatible.html )

Aunque MSSql usa esta misma función para generar NewSequencialIds (UuidCreateSequential (fuera Guid guid)), MSSQL invierte los patrones de 3er y 4to byte, lo que no le da el mismo resultado que obtendría al usar esta función en su código. Shirmanov muestra cómo obtener exactamente los mismos resultados que MSSQL crearía.

Echa un vistazo a COMBs de Jimmy Nilsson: un tipo de GUID donde varios bits han sido reemplazados por un valor similar a la marca de tiempo. Esto significa que los COMB se pueden ordenar, y cuando se usan como clave principal dan como resultado menos divisiones de página de índice al insertar nuevos valores.

es ¿Está bien usar un identificador único (GUID) como clave principal?

OK, finalmente llegué a este punto en diseño y producción.

Genero un COMB_GUID donde los 32 bits superiores se basan en los bits 33 a 1 del tiempo Unix en milisegundos. Entonces, hay 93 bits de aleatoriedad cada 2 milisegundos y el rollover en los bits superiores ocurre cada 106 años. La representación física real de COMB_GUID (o UUID de tipo 4) es una versión codificada en base64 de los 128 bits, que es una cadena de 22 caracteres.

Al insertar en postgres, la relación de velocidad entre un UUID completamente aleatorio y un COMB _GUID se considera beneficioso para el COMB_GUID. El COMB_GUID es 2X más rápido en mi hardware en múltiples pruebas, para una prueba de registro de un millón. Los registros contienen la identificación (22 caracteres), un campo de cadena (110 caracteres), una precisión doble y un INT.

En ElasticSearch, NO hay una diferencia discernible entre los dos para la indexación. Todavía voy a usar COMB_GUIDS en caso de que el contenido vaya a los índices BTREE en cualquier parte de la cadena, ya que el contenido se alimenta en función del tiempo, o se puede clasificar previamente en el campo de identificación para que ES relacionado con el tiempo y parcialmente secuencial, se acelerará.

Bastante interesante. El código Java para hacer un COMB_GUID está debajo.

import java.util.Arrays;
import java.util.UUID;
import java.util.Base64; //Only avail in Java 8+
import java.util.Date;

import java.nio.ByteBuffer; 

    private ByteBuffer babuffer = ByteBuffer.allocate( (Long.SIZE/8)*2 );
private Base64.Encoder encoder = Base64.getUrlEncoder();
public  String createId() {
    UUID uuid = java.util.UUID.randomUUID();
        return uuid2base64( uuid );
}

    public String uuid2base64(UUID uuid){ 

        Date date= new Date();
        int intFor32bits;
        synchronized(this){
        babuffer.putLong(0,uuid.getLeastSignificantBits() );
        babuffer.putLong(8,uuid.getMostSignificantBits() );

                long time=date.getTime();
        time=time >> 1; // makes it every 2 milliseconds
                intFor32bits = (int) time; // rolls over every 106 yers + 1 month from epoch
                babuffer.putInt( 0, intFor32bits);

    }
        //does this cause a memory leak?
        return encoder.encodeToString( babuffer.array() );
    }

}

Envié la diferencia entre Guid (en clúster y no en clúster), Guid secuencial e int (Identidad / incremento automático) usando Entity Framework. El Guión secuencial fue sorprendentemente rápido en comparación con el int con identidad. Resultados y código de la Guía secuencial aquí .

No veo la necesidad de claves únicas para ser adivinables o no, pasarlas desde una interfaz de usuario web o en alguna otra parte parece una mala práctica en sí misma y no veo, si tiene problemas de seguridad, cómo usar un guid puede mejorar las cosas (si este es el caso, use un generador de números aleatorios real usando las funciones de cifrado adecuadas del marco).
Los otros elementos están cubiertos por mi enfoque, se puede generar un guid secuencial a partir del código sin necesidad de acceso a la base de datos (también aunque solo sea para Windows) y es único en tiempo y espacio.
Y sí, se planteó una pregunta con la intención de responderla, para dar a las personas que han elegido Guías para su PK una forma de mejorar el uso de la base de datos (en mi caso, ha permitido a los clientes mantener una carga de trabajo mucho mayor sin tener que cambiar de servidor).

Parece que las preocupaciones de seguridad son muchas, en este caso no use Guid secuencial o, mejor aún, use Guid estándar para PK que se pasan de su interfaz de usuario hacia adelante y hacia atrás y guid secuencial para todo lo demás. Como siempre no hay una verdad absoluta, también he editado la respuesta principal para reflejar esto.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top