Depois de fazer esta pergunta comparando GUIDs seqüenciais e não sequenciais, tentei comparar o desempenho INSERT em 1) uma tabela com uma chave primária GUID inicializada sequencialmente com newsequentialid()
e 2) uma tabela com uma chave primária INT inicializada sequencialmente com identity(1,1)
. Eu esperaria que o último fosse mais rápido devido à menor largura de números inteiros, e também parece mais simples gerar um número inteiro seqüencial do que um GUID seqüencial. Mas, para minha surpresa, os INSERTs na tabela com a chave inteira eram significativamente mais lentos que a tabela GUID seqüencial.
Isso mostra o tempo médio de uso (ms) para a execução do teste:
NEWSEQUENTIALID() 1977
IDENTITY() 2223
Alguém pode explicar isso?
A seguinte experiência foi usada:
SET NOCOUNT ON
CREATE TABLE TestGuid2 (Id UNIQUEIDENTIFIER NOT NULL DEFAULT NEWSEQUENTIALID() PRIMARY KEY,
SomeDate DATETIME, batchNumber BIGINT, FILLER CHAR(100))
CREATE TABLE TestInt (Id Int NOT NULL identity(1,1) PRIMARY KEY,
SomeDate DATETIME, batchNumber BIGINT, FILLER CHAR(100))
DECLARE @BatchCounter INT = 1
DECLARE @Numrows INT = 100000
WHILE (@BatchCounter <= 20)
BEGIN
BEGIN TRAN
DECLARE @LocalCounter INT = 0
WHILE (@LocalCounter <= @NumRows)
BEGIN
INSERT TestGuid2 (SomeDate,batchNumber) VALUES (GETDATE(),@BatchCounter)
SET @LocalCounter +=1
END
SET @LocalCounter = 0
WHILE (@LocalCounter <= @NumRows)
BEGIN
INSERT TestInt (SomeDate,batchNumber) VALUES (GETDATE(),@BatchCounter)
SET @LocalCounter +=1
END
SET @BatchCounter +=1
COMMIT
END
DBCC showcontig ('TestGuid2') WITH tableresults
DBCC showcontig ('TestInt') WITH tableresults
SELECT batchNumber,DATEDIFF(ms,MIN(SomeDate),MAX(SomeDate)) AS [NEWSEQUENTIALID()]
FROM TestGuid2
GROUP BY batchNumber
SELECT batchNumber,DATEDIFF(ms,MIN(SomeDate),MAX(SomeDate)) AS [IDENTITY()]
FROM TestInt
GROUP BY batchNumber
DROP TABLE TestGuid2
DROP TABLE TestInt
ATUALIZAÇÃO: Modificando o script para executar as inserções com base em uma tabela TEMP, como nos exemplos de Phil Sandler, Mitch Wheat e Martin abaixo, também acho que a IDENTITY é mais rápida do que deveria ser. Mas essa não é a maneira convencional de inserir linhas, e ainda não entendo por que o experimento deu errado no início: mesmo que eu omita GETDATE () do meu exemplo original, IDENTITY () ainda é muito mais lento. Portanto, parece que a única maneira de fazer com que IDENTITY () supere NEWSEQUENTIALID () é preparar as linhas para inserir em uma tabela temporária e executar muitas inserções como inserção em lote usando esta tabela temporária. Em suma, acho que não encontramos uma explicação para o fenômeno, e o IDENTITY () ainda parece ser mais lento para a maioria dos usos práticos. Alguém pode explicar isso?
fonte
INT IDENTITY
IDENTITY
não requer um bloqueio de tabela. Conceitualmente, pude ver que você espera que ele esteja usando MAX (id) + 1, mas, na realidade, o próximo valor é armazenado. Na verdade, deve ser mais rápido do que encontrar o próximo GUID.Respostas:
Modifiquei o código de @Phil Sandler para remover o efeito de chamar GETDATE () (pode haver efeitos / interrupções de hardware envolvidos ??) e criei linhas com o mesmo comprimento.
[Existem vários artigos desde o SQL Server 2000 relacionados a problemas de temporização e cronômetros de alta resolução, então eu queria minimizar esse efeito.]
No modelo de recuperação simples, com dados e arquivos de log em tamanho acima do necessário, eis os tempos (em segundos): (Atualizado com novos resultados com base no código exato abaixo)
O código usado:
Depois de ler a investigação de @ Martin, corri novamente com o TOP sugerido (@num) nos dois casos, ou seja,
e aqui estão os resultados do timing:
Não consegui obter o plano de execução real, pois a consulta nunca retornou! Parece que um erro é provável. (Executando o Microsoft SQL Server 2008 R2 (RTM) - 10.50.1600.1 (X64))
fonte
SORT
operador para os GUIDs?NEWSEQUENTIALID
qualquer maneira. Isso tornará o índice mais profundo, usará 20% mais páginas de dados no caso do OP e só será garantido um aumento até a reinicialização da máquina, por isso há muitas desvantagens em relação a umidentity
. Neste caso, parece que o Plano de Consulta adiciona outro desnecessário!Em um novo banco de dados no modelo de recuperação simples, com o arquivo de dados dimensionado em 1 GB e o arquivo de log em 3 GB (laptop, ambos arquivos na mesma unidade) e o intervalo de recuperação definido como 100 minutos (para evitar um ponto de verificação distorcido os resultados), vejo resultados semelhantes a você com a única linha
inserts
.Testei três casos: para cada caso, fiz 20 lotes de inserção de 100.000 linhas individualmente nas tabelas a seguir. Os scripts completos podem ser encontrados no histórico de revisões desta resposta .
Para a terceira tabela, o teste inseriu linhas com um
Id
valor incremental, mas este foi calculado automaticamente incrementando o valor de uma variável em um loop.A média do tempo gasto nos 20 lotes deu os seguintes resultados.
Conclusão
Definitivamente, parece haver uma sobrecarga no
identity
processo de criação responsável pelos resultados. Para o número inteiro incremental auto-calculado, os resultados são muito mais alinhados com o que seria esperado ver ao considerar apenas o custo de IO.Quando coloco o código de inserção descrito acima nos procedimentos armazenados e revisto,
sys.dm_exec_procedure_stats
ele fornece os seguintes resultadosPortanto, nesses resultados
total_worker_time
é cerca de 30% maior. Isto representaPortanto, simplesmente parece que o código que gera o
IDENTITY
valor é mais intensivo em CPU do que o que gera aNEWSEQUENTIALID()
(A diferença entre os dois números é 10231308, que calcula a média de cerca de 5µs por inserção) e que, para esta tabela, define esse custo fixo da CPU foi suficientemente alto para compensar as leituras e gravações lógicas adicionais incorridas devido à maior largura da chave. (Nota: Itzik Ben Gan fez testes semelhantes aqui e encontrou uma penalidade de 2µs por inserção)Então, por que é
IDENTITY
mais intensivo em CPU do queUuidCreateSequential
?Eu acredito que isso é explicado neste artigo . Para cada décimo
identity
valor gerado, o SQL Server precisa gravar a alteração nas tabelas do sistema em discoE as Pastilhas MultiRow?
Quando as 100.000 linhas são inseridas em uma única declaração, descobri que a diferença desapareceu, ainda com um pequeno benefício para o
GUID
caso, mas nem de longe com resultados claros. A média de 20 lotes no meu teste foiA razão pela qual não tem a penalidade aparente no código de Phil e no primeiro conjunto de resultados de Mitch é porque aconteceu que o código que eu costumava fazer a inserção de várias linhas usada
SELECT TOP (@NumRows)
. Isso impediu que o otimizador calculasse corretamente o número de linhas que serão inseridas.Isso parece ser benéfico, pois há um certo ponto de inflexão no qual ele adicionará uma operação de classificação adicional para os (supostamente seqüenciais!)
GUID
S.Esta operação de classificação não é necessária no texto explicativo em BOL .
Portanto, pareceu-me um erro ou falta de otimização que o SQL Server não reconhece que a saída do escalar de computação já estará pré-classificada, como aparentemente já faz para a
identity
coluna. ( Editar eu relatei isso e o problema desnecessário de classificação agora está corrigido no Denali )fonte
Muito simples: com o GUID, é mais barato gerar o próximo número na linha do que para o IDENTITY (o valor atual do GUID não precisa ser armazenado, o IDENTITY deve ser). Isso vale mesmo para NEWSEQUENTIALGUID.
Você poderia tornar o teste mais justo e usar um SEQUENCER com um CACHE grande - mais barato que o IDENTITY.
Mas, como o MR diz, existem algumas vantagens importantes nos GUIDs. Por uma questão de fato, eles são MUITO mais escalonáveis que as colunas IDENTITY (mas somente se NÃO forem seqüenciais).
Consulte: http://blog.kejser.org/2011/10/05/boosting-insert-speed-by-generating-scalable-keys/
fonte
IDENTITY
. daqui reclamações aquiEstou fascinado por esse tipo de pergunta. Por que você teve que publicá-lo na sexta à noite? :)
Acho que, mesmo que seu teste tenha como único objetivo medir o desempenho do INSERT, você (pode) introduziu uma série de fatores que podem ser enganosos (loop, uma transação de longa duração etc.)
Não estou totalmente convencido de que minha versão prove alguma coisa, mas a identidade tem um desempenho melhor que os GUIDs (3,2 segundos versus 6,8 segundos em um PC doméstico):
fonte
Eu executei seu exemplo de script várias vezes, fazendo alguns ajustes na contagem e no tamanho do lote (e muito obrigado por fornecê-lo).
Primeiro, direi que você está medindo apenas uma vez o aspecto do desempenho das teclas - a
INSERT
velocidade. Portanto, a menos que você esteja especificamente preocupado apenas em obter dados nas tabelas o mais rápido possível, há muito mais nesse animal.Minhas descobertas foram em geral semelhantes às suas. No entanto, eu mencionaria que a variação na
INSERT
velocidade entreGUID
eIDENTITY
(int) é um pouco maior com doGUID
que comIDENTITY
- talvez +/- 10% entre as execuções. Os lotes utilizadosIDENTITY
variaram menos de 2 a 3% a cada vez.Observe também que minha caixa de teste é claramente menos poderosa que a sua, então tive que usar contagens de linhas menores.
fonte
Vou me referir a outra conv. No stackoverflow para este mesmo tópico - https://stackoverflow.com/questions/170346/what-are-the-performance-improvement-of-sequential-guid-over-standard-guid
Uma coisa que eu sei é que ter GUIDs seqüenciais é que o uso do índice é melhor devido a muito pouco movimento de folhas e, portanto, reduzindo a busca por HD. Eu pensaria que, por isso, as inserções também seriam mais rápidas, pois não precisam distribuir as chaves por um grande número de páginas.
Minha experiência pessoal é que, quando você está implementando um banco de dados grande e de alto tráfego, é melhor usar GUIDs, porque o torna muito mais escalável para integração com outros sistemas. Isso vale para a replicação, especificamente, e os limites int / bigint ... não que você fique sem bigints, mas eventualmente acabará, e retornará.
fonte