Na czym polega poprawa wydajności sekwencyjnego identyfikatora Guid w stosunku do standardowego identyfikatora Guid?

Czy ktoś kiedykolwiek mierzył wydajność sekwencyjnego Guid w porównaniu ze standardowym Guid, gdy jest używany jako klucz podstawowy wewnątrz bazy danych?

Author: Mitch Wheat, 2008-10-04

9 answers

GUID vs. Sequential GUID



Typowy wzorzec to użycie Guid jako PK dla tabel, ale, jak wspomniano w innych dyskusjach (zobacz zalety i wady kluczy bazy danych GUID / UUID ) są pewne problemy z wydajnością.

Jest to typowa Sekwencja Guid

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


Tego typu problemy dane są: -

  • szerokie rozkłady wartości
  • prawie przypadkowe
  • użycie indeksu jest bardzo, bardzo, bardzo złe
  • a lot of leaf moving
  • prawie każdy PK musi być przynajmniej na indeksie non clustered
  • Problem występuje zarówno na Oracle jak i SQL Server



Możliwe rozwiązanie to użycie sekwencyjnego Guid, które są generowane w następujący sposób:

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


Jak wygenerować je z kodu 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;
}


Korzyści

  • lepsze wykorzystanie indeksu
  • Zezwalaj na używanie kluczy klastrowych (aby sprawdzone w skryptach NLB)
  • mniejsze zużycie dysku
  • 20-25% wzrostu wydajności przy minimalny koszt



pomiar rzeczywisty: Scenariusz:

  • Guid przechowywany jako UniqueIdentifier typy NA SQL Server
  • Oracle nie jest w stanie odczytać znacznika.]}
  • wiele operacji wstawiania, partiami razem w jednej transakcji
  • od 1 do 100s wkładek w zależności na stole
  • niektóre tabele > 10 milionów wierszy



Test laboratoryjny-SQL Server

test VS2008, 10 jednoczesnych użytkowników, brak czasu na myślenie, proces porównawczy z 600 wkładkami w partii do tabeli liści
Standard Guid
Avg. Proces czas trwania: 10.5 sek
Avg. Prośba o drugą: 54.6
Avg. Resp. Czas: 0.26

Sequential Guid
Avg. Czas trwania procesu: 4.6 sek
Avg. Prośba o drugą: 87.1
Avg. Resp. Czas: 0.12

wyniki na Oracle (sorry, inne narzędzie używane do testów) 1.327.613 Wstaw na stół z GUID PK

Standardowy Guid, 0.02 sek. upłynął czas dla każdej wkładki, 2.861 sek. czasu procesora, łącznie 31.049 sek. upłynął

sekwencyjny Guid, 0.00 sek. upłynął czas dla każdej wkładki, 1.142 sek. czasu procesora, łącznie 3.667 sek. upłynął

plik DB odczyt sekwencyjny czas oczekiwania minął od 6.4 miliony czekają na wydarzenia 62.415 sekundy do 1.2 milion zdarzeń oczekujących na 11.063 sekund.

Ważne jest, aby wszystkie sekwencyjne guid mogły być domyślam się, więc nie jest dobrym pomysłem, aby ich używać, jeśli Bezpieczeństwo jest problemem, nadal używając standardowego guid.
Żeby było krótko... jeśli używasz Guid jako PK, użyj sekwencyjnego guid za każdym razem, gdy nie są przekazywane z interfejsu użytkownika do tyłu i do przodu, przyspieszą działanie i nie kosztują niczego.

 97
Author: massimogentilini,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:46:52

Może mi tu czegoś brakuje (nie krępuj się mnie poprawić, jeśli tak), ale widzę bardzo małe korzyści w używaniu sekwencyjnych GUID/uuid dla kluczy podstawowych.

Punkt użycia GUID lub UUID nad autoincrementującymi liczbami całkowitymi wynosi:

  • mogą być tworzone w dowolnym miejscu Bez kontaktu z bazą danych
  • Są to identyfikatory, które są całkowicie unikalne w Twojej aplikacji (a w przypadku uuid-uniwersalnie unikalne)
  • podano jeden identyfikator, nie ma sposobu, aby odgadnąć następny lub Poprzedni (lub nawet jakiekolwiek Inne ważne identyfikatory) poza brute-wymuszając ogromny keyspace.

Niestety, używając Twojej sugestii, tracisz wszystkie te rzeczy.

Więc tak. Sprawiłeś, że GUIDs był lepszy. Ale w tym procesie odrzuciłeś prawie wszystkie powody, aby z nich korzystać.

Jeśli naprawdę chcesz poprawić wydajność, użyj standardowego autoincrementing integer klucz główny. Zapewnia to wszystkie opisane korzyści (i więcej), a jednocześnie jest lepszy niż "sekwencyjny guid" pod niemal każdym względem.

To najprawdopodobniej zostanie pomniejszone w zapomnienie, ponieważ nie odpowiada konkretnie na twoje pytanie (które jest najwyraźniej starannie wykonane, abyś mógł odpowiedzieć sobie natychmiast), ale uważam, że jest to o wiele ważniejsza kwestia do poruszenia.

 46
Author: Dan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-04 15:51:27

Jak już powiedział massimogentilini, wydajność można poprawić przy użyciu UuidCreateSequential (podczas generowania GUID w kodzie). Ale fakt wydaje się brakować: serwer SQL (przynajmniej Microsoft SQL 2005 / 2008) używa tej samej funkcjonalności, ale: porównanie / kolejność GUID różni się w. NET i na serwerze SQL, co nadal powodowałoby więcej IO, ponieważ GUID nie będą uporządkowane poprawnie. Aby wygenerować poprawnie zarządzane GUID dla sql server (ordering), musisz wykonaj następujące czynności (zobacz porównanie Szczegóły):

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

Lub ten link lub ten link.

 18
Author: Bernhard Kircher,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-08 03:10:48

Jeśli potrzebujesz , aby użyć sekwencyjnych identyfikatorów GUID, SQL Server 2005 może wygenerować je dla ciebie za pomocą funkcji NEWSEQUENTIALID().

Jednak ponieważ podstawowym zastosowaniem GUID jest generowanie kluczy (lub kluczy alternatywnych), których nie można odgadnąć (na przykład, aby uniknąć przekazywania odgadniętych kluczy Na get), nie widzę ich zastosowania, ponieważ są tak łatwe do odgadnięcia.

From MSDN :

Ważne:
Jeśli chodzi o prywatność, nie używaj ta funkcja. Informatyka można odgadnąć wartość następnie wygenerowany GUID, a zatem, dostęp do danych powiązanych z tym identyfikatorem GUID.

 5
Author: Sklivvz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-04 15:59:44

Zobacz ten artykuł: ( http://www.shirmanov.com/2010/05/generating-newsequentialid-compatible.html )

Mimo że MSSql używa tej samej funkcji do generowania NewSequencialIds (Uuidcreatesequential (out Guid guid) ), MSSQL odwraca wzorce 3. i 4. bajtów, co nie daje takiego samego wyniku, jaki uzyskasz podczas używania tej funkcji w kodzie. Shirmanov pokazuje, jak uzyskać dokładnie te same wyniki, które MSSQL stworzy.

 3
Author: Bryon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-21 09:50:21

Check out COMBs by Jimmy Nilsson: Typ GUID, w którym liczba bitów została zastąpiona wartością podobną do znacznika czasu. Oznacza to, że COMBs można porządkować, a gdy jest używany jako klucz podstawowy, powoduje mniej podziałów strony indeksu podczas wstawiania nowych wartości.

Czy Można używać uniqueidentifier (GUID) jako klucza podstawowego?

 3
Author: Mitch Wheat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-02 03:23:18

I messured difference between Guid( clustered and non clustered), Sequential Guid and int (Identity/autoincrement) using Entity Framework. Sekwencyjny Guid był zaskakująco szybki w porównaniu do Int z tożsamością. Wyniki i Kod sekwencyjnego Guid tutaj .

 2
Author: Alex Siepman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-01 20:42:52

Nie widzę potrzeby unikalnych kluczy, aby można było zgadywać, czy nie, przekazywanie ich z interfejsu WWW lub w innej części wydaje się złą praktyką samą w sobie i nie widzę, jeśli masz obawy dotyczące bezpieczeństwa, jak używanie guid może poprawić rzeczy(jeśli to jest sprawa, użyj prawdziwego generatora liczb losowych przy użyciu odpowiednich funkcji kryptograficznych frameworka).
Pozostałe elementy są objęte moim podejściem, sekwencyjny guid może być generowany z kodu bez potrzeby dostępu do DB (również jeśli tylko dla Windows) i jest unikalny w czasie i przestrzeni.
I tak, pytanie postawiono z zamiarem odpowiedzi na to, aby dać ludziom, którzy wybrali GUID dla ich PK sposób na poprawę wykorzystania bazy danych (w moim przypadku pozwolił klientom utrzymać znacznie większe obciążenie bez konieczności zmiany serwerów).

Wydaje się, że kwestie bezpieczeństwa są dużo, w tym przypadku nie używaj sekwencyjnego Guid lub, jeszcze lepiej, Użyj standardowego Guid dla PK, które są przekazywane z powrotem i do przodu z interfejsu użytkownika i sekwencyjnego guid dla Wszystko inne. Jak zawsze nie ma absolutnej prawdy, edytowałem również główną odpowiedź, aby to odzwierciedlić.

 1
Author: massimogentilini,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-04 19:00:41

OK, w końcu dotarłem do tego punktu w projektowaniu i produkcji.

Generuję COMB_GUID, gdzie górne 32 bity są oparte na bitach od 33 do 1 czasu Uniksa w milisekundach. Tak więc, co 2 milisekundy są 93 bity losowości, a przewrócenie na górnych bitach następuje co 106 lat. Rzeczywista fizyczna reprezentacja COMB_GUID (lub UUID typu 4) jest zakodowaną wersją Base64 128 bitów, która jest łańcuchem znaków 22.

Podczas wstawiania w postgres stosunek prędkości między całkowicie losowym UUID i grzebieniem _GUID jest korzystny dla COMB_GUID. COMB_GUID jest 2x szybszy na moim sprzęcie przez wiele testów, na milion testów rekordowych. Rekordy zawierają id( 22 znaki), pole string (110 znaków), podwójną precyzję i INT.

W ElasticSearch nie ma zauważalnej różnicy między tymi dwoma indeksami. Nadal będę używać COMB_GUIDS w przypadku, gdy zawartość trafi do indeksów BTREE w dowolnym miejscu w łańcuchu jako zawartość jest powiązana z czasem lub może być wstępnie sortowana w polu id, aby była powiązana z czasem i częściowo Sekwencyjna, przyspieszy to.

Całkiem interesujące. Kod Java do utworzenia COMB_GUID znajduje się poniżej.
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() );
    }

}

 1
Author: Dennis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-26 21:54:29