Quais são as melhores práticas para usar um GUID como chave primária, especificamente em relação ao desempenho?

336

Eu tenho um aplicativo que usa GUID como chave primária em quase todas as tabelas e li que há problemas sobre o desempenho ao usar GUID como chave primária. Sinceramente, não vi nenhum problema, mas estou prestes a iniciar um novo aplicativo e ainda quero usar os GUIDs como chaves primárias, mas estava pensando em usar uma chave primária composta (o GUID e talvez outro campo .)

Estou usando um GUID porque eles são agradáveis ​​e fáceis de gerenciar quando você tem ambientes diferentes, como bancos de dados de "produção", "teste" e "dev", e também para dados de migração entre bancos de dados.

Usarei o Entity Framework 4.3 e desejo atribuir o Guid no código do aplicativo, antes de inseri-lo no banco de dados. (ou seja, não quero permitir que o SQL gere o Guid).

Qual é a melhor prática para criar chaves primárias baseadas em GUID, a fim de evitar os supostos hits de desempenho associados a essa abordagem?

VAAA
fonte
20
A questão não é suposta. Se sua PK estiver em cluster, quase todas as inserções podem causar uma divisão de página. Nas versões modernas do SQL Server, isso era "corrigido" com NEWSEQUENTIALID (), mas perde o benefício de poder calculá-lo com antecedência. Eu recomendo fortemente que você leia sobre GUIDs em outros lugares como este é muito ampla uma pergunta e provavelmente irá solicitar uma batalha religiosa que vai continuar por horas ...
Aaron Bertrand
4
Eu também acrescentaria que a palavra servidor é ambígua . Quero atribuir o Guid no lado do servidor (não permita que o SQL crie o GUID) .
Erik Philips
Esta questão tem semelhanças a este "-sql-server guid-sort-algoritmo-porquê" stackoverflow.com/questions/7810602/...
Clinton Ward

Respostas:

494

Os GUIDs podem parecer uma escolha natural para sua chave primária - e se você realmente precisar, provavelmente poderá argumentar para usá-la na CHAVE PRIMÁRIA da tabela. O que eu recomendo fortemente não fazer é usar a coluna GUID como a chave de cluster , o que o SQL Server faz por padrão, a menos que você diga especificamente que não.

Você realmente precisa manter dois problemas separados:

  1. a chave primária é uma construção lógica - uma das chaves candidatas que identifica de maneira exclusiva e confiável todas as linhas da sua tabela. Isso pode ser qualquer coisa, realmente - uma INT, uma GUID, uma sequência - escolha o que faz mais sentido para o seu cenário.

  2. a chave de cluster (a coluna ou colunas que definem o "índice de cluster" na tabela) - isso é relacionado ao armazenamento físico e, aqui, um tipo de dados pequeno, estável e sempre crescente é a melhor opção - INTou BIGINTcomo opção padrão.

Por padrão, a chave primária em uma tabela do SQL Server também é usada como chave de cluster - mas isso não precisa ser assim! Eu, pessoalmente, vi ganhos de desempenho maciços ao dividir a Chave Primária / Clusterizada baseada em GUID anterior em duas chaves separadas - a chave primária (lógica) no GUID e a chave de agrupamento (ordem) em uma INT IDENTITY(1,1)coluna separada .

Como Kimberly Tripp - a rainha da indexação - e outras já declararam muitas vezes - GUIDa chave de cluster não é ideal, pois, devido à sua aleatoriedade, levará a uma fragmentação maciça de páginas e índices e a um desempenho geralmente ruim.

Sim, eu sei - existe newsequentialid()no SQL Server 2005 e acima - mas mesmo isso não é verdadeira e totalmente seqüencial e, portanto, também sofre dos mesmos problemas que os GUID- apenas um pouco menos proeminentemente.

Depois, há outra questão a considerar: a chave de cluster em uma tabela será adicionada a toda e qualquer entrada em todo e qualquer índice não em cluster da sua tabela - assim, você realmente deseja garantir que seja o menor possível. Normalmente, um INTcom mais de 2 bilhões de linhas deve ser suficiente para a grande maioria das tabelas - e comparado a uma GUIDchave de cluster, você pode economizar centenas de megabytes de armazenamento em disco e na memória do servidor.

Cálculo rápido - usando INTvs. GUIDcomo chave primária e de cluster:

  • Tabela base com 1'000'000 linhas (3,8 MB vs. 15,26 MB)
  • 6 índices não clusterizados (22,89 MB vs. 91,55 MB)

TOTAL: 25 MB vs. 106 MB - e isso é apenas uma tabela!

Um pouco mais de reflexão - coisas excelentes de Kimberly Tripp - leia, leia novamente, digera! É o evangelho de indexação do SQL Server, na verdade.

PS: é claro, se você estiver lidando com apenas algumas centenas ou milhares de linhas - a maioria desses argumentos não terá muito impacto sobre você. No entanto: se você entrar em dezenas ou centenas de milhares de linhas, ou você comece a contar em milhões - , em seguida, os pontos tornam-se muito crucial e muito importante para entender.

Atualização: se você deseja que sua PKGUIDcoluna seja sua chave primária (mas não sua chave de cluster) e outra coluna MYINT( INT IDENTITY) como sua chave de cluster - use isto:

CREATE TABLE dbo.MyTable
(PKGUID UNIQUEIDENTIFIER NOT NULL,
 MyINT INT IDENTITY(1,1) NOT NULL,
 .... add more columns as needed ...... )

ALTER TABLE dbo.MyTable
ADD CONSTRAINT PK_MyTable
PRIMARY KEY NONCLUSTERED (PKGUID)

CREATE UNIQUE CLUSTERED INDEX CIX_MyTable ON dbo.MyTable(MyINT)

Basicamente: você só precisa dizer explicitamente a PRIMARY KEYrestrição de que está NONCLUSTERED(caso contrário, é criado como seu índice clusterizado, por padrão) - e então cria um segundo índice definido comoCLUSTERED

Isso funcionará - e é uma opção válida se você tiver um sistema existente que precise ser "reprojetado" para obter desempenho. Para um novo sistema, se você começar do zero e não estiver em um cenário de replicação, sempre escolheria ID INT IDENTITY(1,1)como minha chave primária em cluster - muito mais eficiente do que qualquer outra coisa!

marc_s
fonte
2
Essa é uma ótima resposta, uma coisa que eu mencionaria é que ser capaz de gerar a chave antes da inserção é frequentemente útil. O uso de "newsequentialid ()" pode ajudar no armazenamento em cluster, mas isso requer uma ida e volta adicional ao SQL. Portanto, outro benefício da abordagem da "chave substituta" é que você pode gerar novos IDs, no lado do cliente, com menos preocupações com a fragmentação do índice.
Andrew Theken 26/02
2
A maneira que eu li isso é que, tendo uma coluna uniqueidentifier não agrupada e a coluna int identity, as FK também devem ser uniqueidentifier? Se você fizer isso, quando você realmente usaria a coluna de identidade diretamente ou não?
amigos estão
2
Pouca pergunta: o GUID agora deve ser usado em junções ou no ID int? Meu instinto me diz que o GUID deve ser usado, mas não vejo um problema técnico com o id int ...
Nicolas Belley
3
@marc_s, mas em um cenário de replicação, se a coluna int for identidade, não devemos usar o GUID, pois a coluna int pode se repetir entre os dispositivos?
Nicolas Belley
6
@Kipei: o principal problema é se você tem um valor tão natural - então sim, você pode usá-lo como chave primária. MAS : valores como, DATETIMEpor exemplo, NÃO são úteis para uma chave de cluster, pois eles têm uma precisão de 3,33ms e, portanto, podem existir duplicatas. Portanto, nesse caso, você * ainda precisa de um INT IDENTITY- portanto, eu normalmente o uso por padrão, pois desde os meus mais de 20 anos de experiência, uma chave natural realmente utilizável quase nunca existe ...
marc_s
51

Uso GUIDs como PKs desde 2005. Nesse mundo de banco de dados distribuído, é absolutamente a melhor maneira de mesclar dados distribuídos. Você pode disparar e esquecer mesclar tabelas sem a preocupação de ints correspondentes nas tabelas unidas. As junções de GUIDs podem ser copiadas sem qualquer preocupação.

Esta é minha configuração para usar GUIDs:

  1. PK = GUID. Os GUIDs são indexados de maneira semelhante às seqüências de caracteres, portanto, tabelas de linhas altas (mais de 50 milhões de registros) podem precisar de particionamento de tabelas ou outras técnicas de desempenho. O SQL Server está ficando extremamente eficiente, portanto, as preocupações com o desempenho são cada vez menos aplicáveis.

  2. O PK Guid é um índice NÃO agrupado. Nunca indexe um GUID por cluster, a menos que seja NewSequentialID. Mas, mesmo assim, uma reinicialização do servidor causará grandes interrupções no pedido.

  3. Adicione ClusterID Int a todas as tabelas. Este é o seu Índice CLUSTERED ... que ordena sua mesa.

  4. A associação aos ClusterIDs (int) é mais eficiente, mas eu trabalho com 20 a 30 milhões de tabelas de registros, portanto, a associação aos GUIDs não afeta visivelmente o desempenho. Se você deseja desempenho máximo, use o conceito ClusterID como sua chave primária e participe do ClusterID.

Aqui está a minha tabela de e-mail ...

CREATE TABLE [Core].[Email] (
    [EmailID]      UNIQUEIDENTIFIER CONSTRAINT [DF_Email_EmailID] DEFAULT (newsequentialid()) NOT NULL,        
    [EmailAddress] NVARCHAR (50)    CONSTRAINT [DF_Email_EmailAddress] DEFAULT ('') NOT NULL,        
    [CreatedDate]  DATETIME         CONSTRAINT [DF_Email_CreatedDate] DEFAULT (getutcdate()) NOT NULL,      
    [ClusterID] INT NOT NULL IDENTITY,
    CONSTRAINT [PK_Email] PRIMARY KEY NonCLUSTERED ([EmailID] ASC)
);
GO

CREATE UNIQUE CLUSTERED INDEX [IX_Email_ClusterID] ON [Core].[Email] ([ClusterID])
GO

CREATE UNIQUE NONCLUSTERED INDEX [IX_Email_EmailAddress] ON [Core].[Email] ([EmailAddress] Asc)
Robert J. Good
fonte
Você poderia explicar a restrição PK_Email? Por que você tem ... Não clusterizado (EmailID ASC) em vez de ... Não clusterizado (ClusterID ASC)?
Phil
2
Pode apostar. Duas coisas principais acontecendo com os índices: 1. Clustered on ClusterID - Ordena sua tabela no disco (0% de fragmentação). 2. NonClustered on EmailID - indexa o campo EmailID para acelerar as pesquisas de ID de GUID. Uma pesquisa de campo GUID se comporta como string-ish, portanto, uma pesquisa por EmailID seria lenta sem o índice.
Robert J. Bom
@ RobertJ.Good Já vi esse método discutido antes, ou seja, adicionar uma chave int substituta para agrupar. Mas não consigo encontrar nenhum lugar que mostre o ganho de desempenho ao ter um índice clusterizado de chaves substitutas usando um heap. Você tem algum link para comparar dados?
Dale K
11
Olá @DaleBurrell, o índice clusterizado é para evitar a fragmentação da tabela. O ganho de desempenho ocorre à medida que a tabela cresce naturalmente em ordem no disco, com baixa fragmentação.
Robert J. Bom
@ RobertJ.Good Isso é uma aplicação web? O que você está usando em urls / hrefs? guid ou int?
dariol
10

Atualmente, estou desenvolvendo um aplicativo Web com o EF Core e aqui está o padrão que eu uso:

Todas as minhas aulas (tabelas) e um int PK e FK. Eu tenho uma coluna adicional com o tipo Guid (gerado pelo construtor c #) com um índice não clusterizado.

Todas as junções da tabela no EF são gerenciadas através das teclas int, enquanto todo o acesso de fora (controladores) é feito com os Guids.

Essa solução permite não mostrar as chaves int nos URLs, mas manter o modelo organizado e rápido.

EricImhauser
fonte
Há algo que você precisa fazer para configurar o número inteiro pK como agrupado, como anotações de dados ou é apenas configurado automaticamente?
Allen Wang
Qual o nome da propriedade que você usa para o Guid one?
Trong Phan
3

Se você usar GUID como chave primária e criar um índice clusterizado, sugiro usar o valor padrão NEWSEQUENTIALID () para ele

AnandPhadke
fonte
Por que você faria isso?
genuinefafa
3

Este link diz que é melhor do que eu pude e ajudou na minha tomada de decisão. Normalmente, opto por um int como chave primária, a menos que tenha uma necessidade específica e também permita que o SQL Server gere / mantenha automaticamente esse campo, a menos que tenha algum motivo específico para não fazê-lo. Na realidade, as preocupações com o desempenho precisam ser determinadas com base no seu aplicativo específico. Existem muitos fatores em jogo aqui, incluindo, entre outros, o tamanho esperado do banco de dados, a indexação adequada, a consulta eficiente e muito mais. Embora as pessoas possam discordar, acho que em muitos cenários você não notará diferença em nenhuma das opções e deve escolher o que é mais apropriado para seu aplicativo e o que permite que você se desenvolva de maneira mais fácil, rápida e eficaz (se você nunca concluir o aplicativo que diferença faz o resto :).

https://web.archive.org/web/20120812080710/http://databases.aspfaq.com/database/what-should-i-choose-for-my-primary-key.html

PS: Não sei por que você usaria um PK composto ou que benefício você acredita que isso lhe daria.

Matt
fonte
Concordo plenamente!! Mas isso significa que, se eu tiver um GUID como PK ou um PK composto com GUID e outro campo, será o mesmo, certo?
Vaaa
11
O PK (índice) seria composto pelas duas colunas, mas, a menos que você tenha algum motivo específico para isso, parece desnecessário.
Matt
11
BTW, essa pergunta é uma das perguntas mais polêmicas e debatidas do mercado e, portanto, extremamente difícil de obter uma resposta para a qual você se sentirá 100% à vontade. Qualquer método vem com trade-offs, então boa sorte :)
Matt
0

Ter IDs seqüenciais facilita muito para um hacker ou minerador de dados comprometer seu site e dados. Lembre-se disso ao escolher um PK para um site.

DaBlue
fonte
Você pode fornecer alguma lógica ou evidência para fazer backup dessa reivindicação? Estou lutando para ver como um ID seqüencial pode comprometer a segurança.
jonaglon 28/01
Claro, se você souber que os números de identificação são inteiros, é possível adivinhar registros sequenciais em um banco de dados. Portanto, se você consultar um único item, poderá dizer que o próximo item é pk + 1. Se você tiver GUIDS aleatórios, ele não seguirá um padrão. Seria quase impossível consultar outros registros além do que você consultou anteriormente (E conheça o PK).
DaBlue 28/01
11
Se um hacker pode consultar seu banco de dados, você já está comprometido, não vejo como os IDs seqüenciais pioram a situação.
jonaglon 29/01
11
Se um usuário pode trocar o 1012 por outro número e ver os dados que não deveriam, então há um problema de segurança muito sério, esse problema não é causado pela escolha da chave primária, mas é exacerbado por ele. Entendo seu ponto de vista, obrigado por esclarecer.
jonaglon 30/01
2
Você pode usar um GUID para localizar um registro na página da Web, que não é o PK da tabela. O uso do parâmetro de consulta em um site não deve definir como você estrutura seu esquema de banco de dados. O PK não tem nada a ver com entrada e parâmetros na interface do usuário ou no sistema de back-end.
Panos Roditakis 30/01