Quali sono i miglioramenti prestazionali di Guid sequenziale rispetto a Guid standard?

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

  •  05-07-2019
  •  | 
  •  

Domanda

Qualcuno ha mai misurato le prestazioni di Guid sequenziale rispetto a Guid standard quando utilizzato come chiavi primarie all'interno di un database?

È stato utile?

Soluzione

GUID vs GUID equivalente


Un modello tipico consiste nell'utilizzare Guid come PK per le tabelle, ma, come indicato in altre discussioni (vedere Vantaggi e svantaggi delle chiavi di database GUID / UUID ) ci sono alcuni problemi di prestazioni.
Questa è una tipica sequenza Guid

f3818d69-2552-40b7-a403-01a6db4552f7
    7ce31615-FAFB-42c4-b317-40d21a6a3c60
    94732fc7-768e-4cf2-9107-f0953f6795a5
    
I problemi di questo tipo di dati sono: & Lt;
    -

  • Ampia distribuzione di valori
  • Quasi a caso
  • L'utilizzo dell'indice è molto, molto, molto negativo
  • Molta foglia in movimento
  • Quasi ogni PK deve essere almeno su un indice non cluster
  • Il problema si verifica sia su Oracle che su SQL Server


Una possibile soluzione sta utilizzando la Guida sequenziale, che viene generata come segue:

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


Come generarli dal codice 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;
}


Vantaggi

  • Migliore utilizzo dell'indice
  • Consenti l'utilizzo di chiavi raggruppate (essere verificato in scenari NLB)
  • Meno utilizzo del disco
  • 20-25% di aumento delle prestazioni a costo minimo


Misurazione della vita reale: Scenario:

  • Guid memorizzato come UniqueIdentifier tipi su SQL Server
  • Guid memorizzato come CHAR (36) su Oracle
  • Lotto di operazioni di inserimento, in batch insieme in un'unica transazione
  • Da 1 a 100s di inserti a seconda sul tavolo
  • Alcune tabelle > 10 milioni di righe


Test di laboratorio & # 8211; SQL Server

Test VS2008, 10 utenti simultanei, nessun tempo di riflessione, processo di benchmark con 600 inserti in batch per la tabella delle foglie
Guida standard
Avg. Durata del processo: 10,5 sec
Avg. Richiesta per il secondo: 54.6
Avg. Resp. Ora: 0,26

Guida sequenziale
Avg. Durata del processo: 4.6 sec
Avg. Richiesta per il secondo: 87.1
Avg. Resp. Ora: 0.12
Risultati su Oracle (scusate, diverso strumento utilizzato per il test) 1.327.613 inserto su una tabella con un Guid PK

Guida standard , 0,02 sec. tempo trascorso per ciascun inserto, 2.861 sec. del tempo della CPU, totale di 31.049 sec. trascorso

Guida sequenziale , 0,00 sec. tempo trascorso per ogni inserto, 1.142 sec. di tempo CPU, totale di 3.667 sec. trascorso

Il tempo di attesa della lettura sequenziale del file DB è passato da 6,4 milioni di eventi di attesa per 62,415 secondi a 1,2 milioni di eventi di attesa per < forte> 11,063 secondi.
È importante vedere che tutti i guid sequenziali possono essere indovinati, quindi non è una buona idea usarli se la sicurezza è un problema, sempre usando guid standard.
Per farla breve ... se usi Guid come PK usi guid sequenziali ogni volta che non passano avanti e indietro da un'interfaccia utente, accelereranno il funzionamento e non costano nulla da implementare.

Altri suggerimenti

Potrei mancare qualcosa qui (sentitevi liberi di correggermi se lo sono), ma posso vedere pochissimi benefici nell'uso di GUID / UUID sequenziali per le chiavi primarie.

Il punto dell'uso di GUID o UUID su numeri interi con incremento automatico è:

  • Possono essere creati ovunque senza contattare il database
  • Sono identificatori del tutto univoci all'interno dell'applicazione (e nel caso degli UUID, universalmente univoci)
  • Dato un identificatore, non c'è modo di indovinare il successivo o precedente (o anche qualsiasi altri identificatori validi) al di fuori del bruto forzando un enorme spazio di chiavi.

Sfortunatamente, usando il tuo suggerimento, perdi tutte quelle cose.

Quindi sì. Hai migliorato i GUID. Ma nel processo, hai buttato via quasi tutti i motivi per usarli in primo luogo.

Se veramente desideri migliorare le prestazioni, utilizza una chiave primaria intera con incremento automatico standard. Ciò offre tutti i vantaggi che hai descritto (e altro), pur essendo migliore di un "guid sequenziale" in quasi tutti i modi.

Molto probabilmente questo verrà trascinato all'oblio poiché non risponde specificamente alla tua domanda (che è apparentemente accuratamente realizzata in modo da poter rispondere da solo immediatamente), ma penso che sia un punto molto più importante da sollevare.

Come già detto massimogentilini, le prestazioni possono essere migliorate quando si utilizza UuidCreateSequential (quando si generano le guide nel codice). Ma un fatto sembra mancare: SQL Server (almeno Microsoft SQL 2005/2008) utilizza la stessa funzionalità, MA: il confronto / ordinamento delle guide differisce in .NET e in SQL Server, il che causerebbe ancora più IO, perché le guide non verranno ordinate correttamente. Per generare le guide ordinate correttamente per il server sql (ordinamento), è necessario effettuare le seguenti operazioni (vedere confronto dettagli):

[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 questo collegamento o questo link .

Se hai bisogno di per usare le GUI sequenziali, SQL Server 2005 può generarle per te con la funzione NEWSEQUENTIALID().

Tuttavia poiché l'uso di base delle GUI è di generare chiavi (o chiavi alternative) che non possono essere indovinate (ad esempio per evitare che le persone passino chiavi indovinate su GET), non vedo quanto applicabile sono perché sono così facilmente indovinati.

Da MSDN :

  

Importante:
  Se la privacy è un problema, non utilizzare questa funzione. esso   è possibile indovinare il valore di   GUID generato successivamente e, quindi,   accedere ai dati associati a quel GUID.

Vedi questo articolo: ( http://www.shirmanov.com/2010/05/generating- NEWSEQUENTIALID-compatible.html )

Anche se MSSql utilizza questa stessa funzione per generare NewSequencialIds (UuidCreateSequential (out Guid guid)), MSSQL inverte i pattern di 3 ° e 4 ° byte che non danno lo stesso risultato che otterresti usando questa funzione nel tuo codice. Shirmanov mostra come ottenere esattamente gli stessi risultati che MSSQL creerebbe.

Scopri COMBs di Jimmy Nilsson: un tipo di GUID dove un numero di bit è stato sostituito con un valore simile a data / ora. Ciò significa che è possibile ordinare i COMB e, se utilizzati come chiave primaria, si traducono in una minore suddivisione della pagina dell'indice quando si inseriscono nuovi valori.

Is va bene usare un identificatore univoco (GUID) come chiave primaria?

OK, finalmente sono arrivato a questo punto nel design e nella produzione da solo.

Genero un COMB_GUID in cui i 32 bit superiori si basano sui bit da 33 a 1 del tempo Unix in millisecondi. Quindi, ci sono 93 bit di casualità ogni 2 millisecondi e il rollover sui bit superiori avviene ogni 106 anni. La rappresentazione fisica effettiva di COMB_GUID (o UUID di tipo 4) è una versione codificata in base64 dei 128 bit, che è una stringa di 22 caratteri.

Quando si inserisce in postgres il rapporto di velocità tra un UUID completamente casuale e un COMB _GUID è vantaggioso per il COMB_GUID. COMB_GUID è 2X più veloce sul mio hardware su più test, per un test da un milione di record. I record contengono l'id (22 caratteri), un campo stringa (110 caratteri), una doppia precisione e un INT.

In ElasticSearch, NON c'è alcuna differenza riconoscibile tra i due per l'indicizzazione. Utilizzerò ancora COMB_GUIDS nel caso in cui il contenuto vada agli indici BTREE in qualsiasi punto della catena poiché il contenuto viene alimentato in base al tempo o può essere preordinato nel campo ID in modo che sia IS correlato al tempo e parzialmente sequenziale, accelererà.

Abbastanza interessante. Il codice Java per creare un COMB_GUID è inferiore.

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

}

Ho notato la differenza tra Guid (cluster e non cluster), Guid sequenziale e int (Identity / autoincrement) usando Entity Framework. Il Guid sequenziale è stato sorprendentemente veloce rispetto all'int con identità. Risultati e codice della Guida sequenziale qui .

Non vedo la necessità di indovinare o meno chiavi univoche, passarle da un'interfaccia utente Web o in qualche altra parte sembra una cattiva pratica da sola e non vedo, se hai problemi di sicurezza, come usare un guid può migliorare le cose (se questo è il caso, usa un vero generatore di numeri casuali usando le appropriate funzioni di crittografia del framework). Le altre voci sono coperte dal mio approccio, una guida sequenziale può essere generata dal codice senza necessità di accesso al DB (anche se solo per Windows) ed è unica nel tempo e nello spazio.
E sì, la domanda è stata posta con l'intento di rispondere, per dare alle persone che hanno scelto le Guide per il loro PK un modo per migliorare l'utilizzo del database (nel mio caso ha permesso ai clienti di sostenere un carico di lavoro molto più elevato senza dover cambiare server).
Sembra che i problemi di sicurezza siano molti, in questo caso non usare Guid Guid sequenziali o, meglio ancora, usare Guid standard per PK che vengono passati avanti e indietro dalla tua IU e guid sequenziali per tutto il resto. Come sempre non esiste una verità assoluta, ho modificato anche la risposta principale per riflettere questo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top