Quelles sont les améliorations de performance de Sequential Guid par rapport à la norme Guid?

StackOverflow https://stackoverflow.com/questions/170346

  •  05-07-2019
  •  | 
  •  

Question

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

Était-ce utile?

La solution

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 des clés de base de données GUID / UUID ) il y a quelques problèmes de performance.

Ceci est 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 les suivants: & Lt;
    -

  • Larges distributions de valeurs
  • Ceux qui sont presque aléatoires
  • L'utilisation de l'index est très, très, très mauvaise
  • Beaucoup de feuilles en mouvement
  • Presque tous les PK doivent être au moins sur un index non clusterisé
  • Un problème survient à la fois sur Oracle et SQL Server



Une solution possible consiste à utiliser le guidage séquentiel, 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 de 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érifié dans les scénarios NLB)
  • Moins d'utilisation du disque
  • 20-25% d’augmentation de la performance à un coût minimum



Mesure réelle: Scénario:

  • Guid stocké sous UniqueIdentifier types sur SQL Server
  • Guid stocké en tant que CHAR (36) sur Oracle
  • Lot d'opérations d'insertion, en lot ensemble en une seule transaction
  • De 1 à 100 insertions en fonction de sur table
  • Quelques tables > 10 millions de lignes



Test de laboratoire & # 8211; SQL Server

Test VS2008, 10 utilisateurs simultanés, heure indéterminée, processus de référence avec 600 insertions en lot pour le tableau feuille
Standard Guid
Moy. Durée du processus: 10,5 s
Moy. Demande de deuxième: 54,6 .
Moy. Resp. Temps: 0,26

Guidage séquentiel
Moy. Durée du processus: 4.6 s
Moy. Demande de second: 87.1
Moy. Resp. Temps: 0.12

Résultats sur Oracle (désolé, outil différent utilisé pour le test) 1.327.613, insertion sur une table avec un PK de guidage

Standard Guid , 0.02 sec. temps écoulé pour chaque insertion, 2.861 sec. de temps processeur, total de 31.049 sec. écoulé

Guidage séquentiel , 0.00 sec. temps écoulé pour chaque insertion, 1.142 sec. de temps processeur, total de 3,667 sec. écoulé

Le temps d'attente de lecture séquentielle dans un fichier de base de données est passé de 6,4 à des millions d'événements de 62,415 secondes à 1,2 à un million d'événements de < strong> 11,063 secondes.

Il est important de voir que tous les guides séquentiels peuvent être devinés. Ce n'est donc pas une bonne idée de les utiliser si la sécurité est une préoccupation, toujours en utilisant le guide standard.
En résumé, si vous utilisez Guid comme PK, vous utilisez des instructions séquentielles chaque fois qu’elles ne sont pas retransmises d’une interface utilisateur à une autre, elles accélèreront les opérations et ne coûteront rien à mettre en œuvre.

Autres conseils

Il se peut que quelque chose me manque 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émentés est:

  • Ils peuvent être créés n'importe où sans contacter la base de données
  • Ce sont des identifiants totalement uniques au sein de votre application (et dans le cas des UUID, universellement uniques)
  • Étant donné un identifiant, il n'y a aucun moyen de deviner le prochain ou le précédent (ou même un autre identificateur valide) en dehors d'un brute forçant un énorme espace de clés.

Malheureusement, selon votre suggestion, vous perdez toutes 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.

Si vous voulez vraiment améliorer les performances, utilisez une clé primaire standard auto-incrémentée. Cela fournit tous les avantages que vous avez décrits (et plus) tout en étant mieux qu'un "guide séquentiel" dans presque tous les sens.

Cela tombera probablement dans l'oubli, car cela ne répond pas spécifiquement à votre question (apparemment soigneusement préparée pour que vous puissiez y répondre vous-même immédiatement), mais j'estime que c'est un point beaucoup plus important à soulever.

Comme le dit déjà 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 / la commande des guids diffèrent en .NET et sur le serveur SQL, ce qui causerait encore plus d'E / S, parce que les guids ne seront pas commandés correctement. Pour générer les guids correctement commandés pour le serveur SQL (ordre), procédez comme 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 .

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

Cependant , l'utilisation de base de GUIds étant généralement de générer des clés (ou des clés alternatives) qui ne peuvent pas être devinées (par exemple, pour éviter que des personnes ne transmettent des clés devinées sur des GET), je ne vois pas en quoi elles s'appliquent ils sont parce qu'ils sont si facilement devinés.

De MSDN :

  

Important:
  Si la confidentialité est une préoccupation, n'utilisez pas cette fonction. Il   est possible de deviner la valeur de la   prochain généré GUID et, par conséquent,   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 troisième et quatrième octets, ce qui ne vous donne pas le même résultat que si vous utilisiez cette fonction dans votre code. Shirmanov montre comment obtenir exactement les mêmes résultats que ceux créés par MSSQL.

Découvrez les COMB par Jimmy Nilsson: un type de GUID où un certain nombre de bits ont été remplacés par une valeur semblable à un horodatage. Cela signifie que les fichiers COMB peuvent être commandés et, lorsqu'ils sont utilisés en tant que clé primaire, ils entraînent moins de fractionnements de pages d'index lors de l'insertion de nouvelles valeurs.

Est puis-je utiliser un identifiant unique (GUID) comme clé primaire?

OK, je suis enfin arrivé à ce stade de la conception et de la production.

Je génère un COMB_GUID dans lequel les 32 bits supérieurs sont basés sur les bits 33 à 1 du temps Unix en millisecondes. Donc, il y a 93 bits de hasard toutes les 2 millisecondes et le basculement sur les bits supérieurs a lieu tous les 106 ans. La représentation physique réelle de COMB_GUID (ou de type 4 UUID) est une version codée en base64 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 totalement aléatoire et un COMB _GUID est avantageux pour le COMB_GUID. Le COMB_GUID est 2X plus rapide sur mon matériel après plusieurs tests, pour un test d'un million d'enregistrements. Les enregistrements contiennent l’id (22 caractères), un champ de chaîne (110 caractères), une double précision et un INT.

Dans ElasticSearch, il n'y a PAS de différence perceptible entre les deux pour l'indexation. Je vais toujours utiliser COMB_GUIDS dans le cas où le contenu passe aux index BTREE n'importe où dans la chaîne, car le contenu est alimenté en temps, ou peut être trié sur le champ id pour qu'il soit IS en temps et partiellement. séquentiel, il va accélérer.

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

}

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

Je ne vois pas le besoin de clés uniques pour être devinables ou non, les passer d'une interface utilisateur Web ou d'une autre partie semble une mauvaise pratique en soi et je ne vois pas, si vous avez des problèmes de sécurité, comment utiliser un guid peut améliorer les choses (si c'est le cas, utilisez un véritable générateur de nombres aléatoires utilisant les fonctions de chiffrement appropriées du framework).
Mon approche couvre les autres éléments. Un GUID séquentiel peut être généré à partir de code sans avoir besoin d’un accès à la base de données (même s’il ne s’agit que de Windows). Il est unique dans le temps et dans l’espace. Et oui, la question a été posée avec l'intention de répondre, de donner aux personnes qui ont choisi Guids pour leur PC un moyen d'améliorer l'utilisation de la base de données (dans mon cas, les clients ont pu supporter une charge de travail beaucoup plus importante sans avoir à changer de serveur).

Il semble que les problèmes de sécurité soient nombreux. Dans ce cas, n'utilisez pas le guidage séquentiel ou, mieux encore, le guidage standard pour les PK, transmis de votre interface utilisateur à un contrôle séquentiel, pour tout le reste. Comme toujours, il n’ya pas de vérité absolue, j’ai également modifié la réponse principale pour refléter cela.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top