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?
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: <
-
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
Mesure réelle: Scénario:
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.
.
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:
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.
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);
}
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.
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.
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?
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 .
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() );
}
}
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.