web-dev-qa-db-fra.com

Quelles sont les performances améliorées du Guid séquentiel par rapport au Guid standard?

Quelqu'un a-t-il déjà mesuré les performances du Guid séquentiel par rapport au Guid standard lorsqu'il est utilisé comme clés primaires dans une base de données?

64
massimogentilini

GUID vs GUID séquentiel



Un modèle typique consiste à utiliser Guid comme PK pour les tables, mais, comme indiqué dans d'autres discussions (voir Avantages et inconvénients de GUID/Clés de base de données UUID =) il y a des problèmes de performances.

Il s'agit d'une séquence Guid typique

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


Les problèmes de ce type de données sont: <
-

  • Large distribution de valeurs
  • Presque au hasard
  • L'utilisation de l'index est très, très, très mauvaise
  • Beaucoup de feuilles en mouvement
  • Presque chaque PK doit être au moins sur un index non clusterisé
  • Le problème se produit à la fois sur Oracle et SQL Server



Une solution possible consiste à utiliser le guidage séquentiel, qui est généré comme suit:

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


Comment les générer à partir du code 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;
}


Avantages

  • Meilleure utilisation de l'index
  • Autoriser l'utilisation des clés en cluster (à vérifier dans les scénarios NLB)
  • Moins d'utilisation du disque
  • Augmentation de 20 à 25% des performances à un coût minimum



Mesure réelle: Scénario:

  • Guid stocké en tant que types UniqueIdentifier sur SQL Server
  • Guid stocké en tant que CHAR (36) sur Oracle
  • Lot d'opérations d'insertion, regroupées en une seule transaction
  • De 1 à 100s d'inserts selon table
  • Certaines tables> 10 millions de lignes



Test de laboratoire - SQL Server

Test VS2008, 10 utilisateurs simultanés, pas de temps de réflexion, processus de référence avec 600 insertions en lot pour le tableau des feuilles
Guid standard
Moy. Durée du processus: 10,5 sec
Moy. Demande de seconde: 54,6
Moy. Resp. Heure: 0,26

Guid séquentiel
Moy. Durée du processus: 4,6 sec
Moy. Demande de seconde: 87,1
Moy. Resp. Heure: 0,12

Résultats sur Oracle (désolé, outil différent utilisé pour le test) 1.327.613 insérer sur une table avec un Guid PK

Guid standard, 0,02 sec. temps écoulé pour chaque insert, 2,861 sec. du temps CPU, total de 31.049 sec. écoulé

Guid séquentiel, 0,00 sec. temps écoulé pour chaque insert, 1,142 sec. du temps CPU, total de 3,667 sec. écoulé

Le temps d'attente de lecture séquentielle du fichier DB est passé de 6,4 millions d'événements d'attente pour 62,415 secondes à 1,2 millions d'événements d'attente pour 11.063 secondes.

Il est important de voir que tous les guid séquentiels peuvent être devinés, donc ce n'est pas une bonne idée de les utiliser si la sécurité est un problème, en utilisant toujours le guid standard.
.

104
massimogentilini

Il me manque peut-être quelque chose ici (n'hésitez pas à me corriger si je le suis), mais je ne vois que très peu d'avantages à utiliser des GUID/UUID séquentiels pour les clés primaires.

Le point d'utilisation des GUID ou des UUID sur des entiers à auto-incrémentation est:

  • Ils peuvent être créés n'importe où sans contacter la base de données
  • Ce sont des identifiants entièrement uniques dans votre application (et dans le cas des UUID, universellement uniques)
  • Étant donné un identifiant, il n'y a aucun moyen de deviner le suivant ou le précédent (ou même tout autres identifiants valides) en dehors du forçage brutal d'un énorme espace de clés.

Malheureusement, en utilisant votre suggestion, vous perdez toutes ces choses.

Donc oui. Vous avez amélioré les GUID. Mais dans le processus, vous avez jeté presque toutes les raisons de les utiliser en premier lieu.

Si vous vraiment souhaitez améliorer les performances, utilisez une clé primaire entière à auto-incrémentation standard. Cela offre tous les avantages que vous avez décrits (et plus) tout en étant meilleur qu'un "guide séquentiel" dans presque tous les sens.

Cela sera très probablement réduit à l'oubli car il ne répond pas spécifiquement à votre question (qui est apparemment soigneusement conçue pour que vous puissiez y répondre vous-même immédiatement), mais je pense que c'est un point beaucoup plus important à soulever.

52
Dan

Comme l'a déjà dit massimogentilini, les performances peuvent être améliorées lors de l'utilisation de UuidCreateSequential (lors de la génération des guides dans le code). Mais un fait semble manquer: le SQL Server (au moins Microsoft SQL 2005/2008) utilise la même fonctionnalité, MAIS: la comparaison/l'ordre des GUID diffère en .NET et sur le SQL Server, ce qui entraînerait encore plus d'E/S, car les guides ne seront pas commandés correctement. Afin de générer les guides correctement commandés pour le serveur SQL (commande), vous devez faire ce qui suit (voir comparaison détails):

[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);
}

ou ce lien ou ce lien .

20
Bernhard Kircher

Si vous avez besoin d'utiliser des GUI séquentiels, SQL Server 2005 peut les générer pour vous avec la fonction NEWSEQUENTIALID().

Cependant puisque l'utilisation de base des GUIds est de générer des clés (ou des clés alternatives) qui ne peuvent pas être devinées (par exemple pour éviter que les gens passent des clés devinées sur les GET) , Je ne vois pas à quel point ils sont applicables car ils sont si faciles à deviner.

De MSDN :

Important:
Si la confidentialité est une préoccupation, n'utilisez pas cette fonction. Il est possible de deviner la valeur du prochain GUID et donc d'accéder aux données associées à ce GUID.

4
Sklivvz

Voir cet article: ( http://www.shirmanov.com/2010/05/generating-newsequentialid-compatible.html )

Même si MSSql utilise cette même fonction pour générer NewSequencialIds (UuidCreateSequential (out Guid guid)), MSSQL inverse les modèles de 3e et 4e octets, ce qui ne vous donne pas le même résultat que vous obtiendriez en utilisant cette fonction dans votre code. Shirmanov montre comment obtenir exactement les mêmes résultats que MSSQL créerait.

4
Bryon

Découvrez COMBs par Jimmy Nilsson: un type de GUID où un certain nombre de bits ont été remplacés par une valeur de type horodatage. Cela signifie que les COMB peuvent être commandés et lorsqu'il est utilisé en tant que clé primaire, il en résulte moins de fractionnements de page d'index lors de l'insertion de nouvelles valeurs.

Peut-on utiliser un identifiant unique (GUID) comme clé primaire?

3
Mitch Wheat

J'ai modifié la différence entre Guid (cluster et non cluster), Guid séquentiel et int (Identité/auto-incrémentation) en utilisant Entity Framework. Le Guid séquentiel était étonnamment rapide par rapport à l'int avec l'identité. Résultats et code du Guid séquentiel ici .

2
Alex Siepman

OK, j'ai finalement atteint ce stade de la conception et de la production moi-même.

Je génère un COMB_GUID où les 32 bits supérieurs sont basés sur les bits 33 à 1 du temps Unix en millisecondes. Ainsi, il y a 93 bits d'aléatoire toutes les 2 millisecondes et le roulement sur les bits supérieurs se produit tous les 106 ans. La représentation physique réelle du COMB_GUID (ou type 4 UUID) est une version codée en base 64 des 128 bits, qui est une chaîne de 22 caractères.

Lors de l'insertion dans postgres, le rapport de vitesse entre un UUID entièrement aléatoire et un COMB _GUID est considéré comme bénéfique pour le COMB_GUID. Le COMB_GUID est 2X plus rapide sur mon matériel sur plusieurs tests, pour un test d'un million d'enregistrements. Les enregistrements contiennent l'identifiant (22 caractères), un champ de chaîne (110 caractères), une double précision et un INT.

Dans ElasticSearch, il n'y a AUCUNE différence perceptible entre les deux pour l'indexation. Je vais toujours utiliser COMB_GUIDS au cas où le contenu irait aux index BTREE n'importe où dans la chaîne car le contenu est alimenté en fonction du temps, ou peut être présorté sur le champ id pour qu'il EST lié au temps et partiellement séquentiel, il accélérera.

Assez intéressant. Le code Java pour créer un COMB_GUID est ci-dessous.

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() );
    }

}

2
Dennis

Je ne vois pas la nécessité de deviner ou non les clés uniques, les passer à partir d'une interface Web ou dans une autre partie semble une mauvaise pratique en soi et je ne vois pas, si vous avez des problèmes de sécurité, comment l'utilisation d'un guid peut s'améliorer les choses (si tel est le cas, utilisez un véritable générateur de nombres aléatoires en utilisant les fonctions de cryptographie appropriées du cadre).
.
. changer de serveur).

. Comme toujours, il n'y a pas de vérité absolue, j'ai également édité la réponse principale pour refléter cela.

1
massimogentilini