Pergunta

Tem alguém desempenho já medido de Sequential Guid vs. Padrão Guid quando usado como chaves primárias dentro de um banco de dados?

Foi útil?

Solução

GUID vs.Sequential GUID


Um padrão típico é usar Guid como PK para tabelas, mas, tal como referido em outras discussões (ver Vantagens e desvantagens de chaves de banco de dados GUID / UUID ) existem alguns problemas de desempenho.
Este é um típico Guid seqüência

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

Problemas desse tipo de dados são: << br> -

  • distribuições largas de valores
  • Quase aleatoriamente queridos
  • a utilização do índice é muito, muito, muito ruim
  • Um monte de folhas em movimento
  • Quase todas as necessidades PK para ser pelo menos em um índice não agrupado
  • O problema acontece tanto no Oracle e SQL Server


Uma possível solução é usar Sequential Guid, que são gerados da seguinte forma:

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

Como gerar-los de código 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;
}


Benefícios

  • Melhor uso de índice
  • Permitir uso de chaves de cluster (para ser verificada em cenários de NLB)
  • uso do disco Menos
  • 20-25% de aumento de desempenho em um custo mínimo


medição vida real: Cenário:

  • Guid armazenado como UniqueIdentifier tipos em SQL Server
  • Guid armazenado como CHAR (36) no Oracle
  • Lote de operações de inserção, em lote juntos em uma única transação
  • De 1 a 100s de inserções, dependendo na tabela
  • Algumas tabelas> 10 milhões de linhas


Teste de laboratório - Servidor
SQL teste
VS2008, 10 usuários simultâneos, não acha que o tempo, o processo de benchmark com 600 inserções em lote para a tabela folha
Padrão Guid
médio. duração do processo: 10,5 sec
médio. Pedido de segundo: 54,6
médio. Resp. Tempo: 0,26
Sequential Guid
médio. duração do processo: 4,6 sec
médio. Pedido de segundo: 87,1
médio. Resp. Tempo: 0,12
Resultados no Oracle (desculpe, ferramenta diferente utilizada para o teste) 1.327.613 inserção em uma tabela com um Guid PK

Padrão Guid , 0,02 sec. tempo decorrido para cada inserção, 2,861 sec. de tempo de CPU, total de 31,049 sec. decorrido
Sequential Guid , 0.00 sec. tempo decorrido para cada inserção, 1.142 sec. de tempo de CPU, total de 3.667 sec. decorrido
O seqüencial arquivo DB ler tempo de espera passou de 6,4 milhões esperar eventos para 62,415 segundos para 1,2 milhão de eventos de espera para < strong> 11.063 segundos.
É importante ver que todo o guid sequencial pode ser adivinhada, por isso não é uma boa idéia para usá-los se a segurança é uma preocupação, ainda usando guid padrão.
Para ser breve ... se você usar Guid como o uso PK sequencial guid cada vez que eles não são passados ??para trás e para a frente a partir de uma interface do usuário, que irá acelerar a operação e não custam nada para implementar.

Outras dicas

Eu pode estar faltando alguma coisa aqui (sinta-se livre para me corrigir se eu sou), mas eu posso ver muito pouco benefício em usar sequenciais GUID / UUID para chaves primárias.

O ponto de usar GUIDs ou UUIDs sobre inteiros autoincrementável é:

  • Eles podem ser criados em qualquer lugar sem entrar em contato com o banco de dados
  • Eles são identificadores que são inteiramente original dentro do seu aplicativo (e no caso de UUIDs, universalmente exclusivo)
  • Dado um identificador, não há nenhuma maneira de adivinhar o exterior seguinte ou anterior (ou mesmo qualquer outros identificadores válidos) de-força bruta de um enorme keyspace.

Infelizmente, usando a sua sugestão, você perde todas essas coisas.

Então, sim. Você fez GUIDs melhor. Mas, no processo, você jogado fora quase todas as razões para usá-los em primeiro lugar.

Se você realmente quiser melhorar o desempenho, use uma autoincrementável padrão inteiro de chave primária. Que oferece todos os benefícios que você descreveu (e mais) enquanto está a ser melhor do que um 'seqüencial guid' em quase todos os sentidos.

Este provavelmente irá se downmodded no esquecimento, pois não especificamente responder a sua pergunta (que aparentemente é cuidadosamente trabalhada para que você pudesse responder it yourself imediatamente), mas eu sinto que é um ponto muito mais importante aumento.

Como massimogentilini já referido, o desempenho pode ser melhorado quando se usa UuidCreateSequential (ao gerar os Guids no código). Mas um fato parece estar faltando: O SQL Server (pelo menos Microsoft SQL 2005/2008) usa a mesma funcionalidade, mas: a comparação / ordenação de Guids diferem em .NET e no SQL Server, o que ainda causa mais IO, porque os guids não serão ordenados corretamente. A fim de gerar os guids ordenados corretamente para sql server (ordenação), você tem que fazer o seguinte (ver detalhes de comparação ):

[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 este link ou este link .

Se você necessidade usar GUIDs seqüenciais, SQL Server 2005 pode gerar-los para você com a função NEWSEQUENTIALID().

No entanto desde o uso básico de GUIDs é gerar chaves (ou chaves suplentes) que não podem ser adivinhadas (por exemplo, para evitar que as pessoas que passam chaves da adivinhadas na Gets), eu não vejo como aplicável eles são, porque eles são tão fáceis de adivinhar.

A partir MSDN :

Importante:
Se a privacidade é uma preocupação, não use esta função. isto é possível adivinhar o valor do seguinte gerado GUID e, por conseguinte, dados de acesso associado a esse GUID.

Veja este artigo: ( http://www.shirmanov.com/2010/05/generating- newsequentialid-compatible.html )

Mesmo que MSSql usa essa mesma função para gerar NewSequencialIds (UuidCreateSequential (fora Guid Guid)), MSSQL inverte a 3ª e 4ª padrões de bytes que não lhe dá o mesmo resultado que você deseja obter ao utilizar esta função em seu código. mostra Shirmanov como obter exatamente o mesmo resultado que MSSQL criaria.

Confira pentes por Jimmy Nilsson: um tipo de GUID onde um número de bits foram substituídos por um valor timestamp-like. Isto significa que os pentes podem ser encomendados, e quando usado como resultado de chave primária em menos divisões de página de índice ao inserir novos valores.

é OK para usar um uniqueidentifier (GUID) como uma chave primária?

OK, eu finalmente chegou a esse ponto em design e produção de mim mesmo.

I gerar um COMB_GUID onde os 32 bits superiores são baseados sobre os bits 33 a um tempo de Unix em milissegundos. Então, há 93 bits de aleatoriedade a cada 2 milissegundos e a rolagem sobre os bits superiores acontece a cada 106 anos. A representação física real da COMB_GUID (ou tipo 4 UUID) é uma versão codificada de base 64 dos 128 bits, que é uma cadeia de char 22.

Ao inserir em postgres a relação de velocidade entre um UUID totalmente aleatória e um pente _GUID tem como benéficos para o COMB_GUID. O COMB_GUID é 2X mais rápido no meu hardware através de vários testes, para um teste de um milhão de registro. Os registros contêm o id (22 caracteres), um campo string (110 caracteres), uma precisão dupla, e um INT.

Em ElasticSearch, não há diferença perceptível entre os dois para a indexação. Eu ainda vou usar COMB_GUIDS no conteúdo caso vai a índices BTREE qualquer lugar na cadeia como o conteúdo é alimentado tempo relacionado, ou pode ser pré-classificados no campo id para que ele é tempo relacionado e parcialmente seqüencial, ele irá acelerar.

interessante Pretty. O código Java para fazer um COMB_GUID está abaixo.

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

}

Eu messured diferença entre Guid (cluster e não agrupado), Sequential Guid e int (Identidade / autoincrement) usando o Entity Framework. O Sequential Guid foi surpreendentemente rápido em comparação com o int com a identidade. resultados e código do Sequential Guid aqui .

Eu não vejo a necessidade de chaves únicas para ser guessable ou não, passá-los a partir de uma interface web ou em alguma outra parte parece uma má prática por si só e não vejo, se você tem preocupações de segurança, como o uso de um guid pode melhorar as coisas (se isso é o uso importa um gerador de números aleatórios real usando as funções de criptografia apropriados do quadro).
Os outros itens são cobertos pela minha abordagem, um guid sequencial pode ser gerado a partir do código, sem necessidade de acesso DB (também se somente para Windows) e é único no tempo e no espaço.
E sim, pergunta foi feita com a intenção de respondê-la, para dar às pessoas que têm escolhidos Guids para a sua PK uma maneira de melhorar o uso de banco de dados (no meu caso permitiu que os clientes para sustentar uma carga de trabalho muito maior sem a necessidade de servidores de mudança).
Parece que as preocupações de segurança são muito, neste caso, não use Sequential Guid ou, melhor ainda, use Guid padrão para PK que são passados ??para trás e para a frente de sua interface do usuário e guid sequencial para tudo o resto. Como sempre não há verdade absoluta, Eu editei resposta também principal para refletir isso.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top