Après avoir posé cette question en comparant les GUID séquentiels et non séquentiels, j'ai essayé de comparer les performances INSERT sur 1) une table avec une clé primaire GUID initialisée séquentiellement avec newsequentialid()
, et 2) une table avec une clé primaire INT initialisée séquentiellement avecidentity(1,1)
. Je m'attendrais à ce que ce dernier soit le plus rapide en raison de la largeur réduite des nombres entiers, et il semble également plus simple de générer un entier séquentiel qu'un GUID séquentiel. Mais à ma grande surprise, les INSERT de la table avec la clé entière étaient beaucoup plus lents que la table GUID séquentielle.
Cela montre le temps moyen d'utilisation (ms) pour les tests:
NEWSEQUENTIALID() 1977
IDENTITY() 2223
Quelqu'un peut-il expliquer cela?
L'expérience suivante a été utilisée:
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
UPDATE: Modification du script pour effectuer les insertions basées sur une table TEMP, comme dans les exemples de Phil Sandler, Mitch Wheat et Martin ci-dessous, je trouve également que IDENTITY est plus rapide que prévu. Mais ce n’est pas la méthode conventionnelle d’insertion de lignes et je ne comprends toujours pas pourquoi l’expérience a mal tourné au début: même si j’ignore GETDATE () de mon exemple initial, IDENTITY () est toujours beaucoup plus lent. Il semble donc que le seul moyen de rendre IDENTITY () supérieur à NEWSEQUENTIALID () est de préparer les lignes à insérer dans une table temporaire et d'effectuer les nombreuses insertions sous forme de batch-insert à l'aide de cette table temporaire. Au total, je ne pense pas que nous ayons trouvé d'explication au phénomène, et IDENTITY () semble toujours être plus lent pour la plupart des usages pratiques. Quelqu'un peut-il expliquer cela?
la source
INT IDENTITY
IDENTITY
ne nécessite pas de verrou de table. Conceptuellement, je pouvais voir que vous pourriez vous attendre à ce qu'il prenne MAX (id) + 1, mais en réalité, la valeur suivante est stockée. En fait, cela devrait être plus rapide que de trouver le prochain GUID.Réponses:
J'ai modifié le code de @Phil Sandler pour supprimer l'effet de l'appel de GETDATE () (il peut y avoir des effets matériels / des interruptions impliqués ??) et j'ai créé des lignes de la même longueur.
[Plusieurs articles ayant été publiés depuis SQL Server 2000 sur les problèmes de minutage et les minuteries haute résolution, je voulais donc minimiser cet effet.]
Dans un modèle de récupération simple avec données et fichier journal, les temps (en secondes) sont les suivants: (Mise à jour avec de nouveaux résultats basés sur le code exact ci-dessous)
Le code utilisé:
Après avoir lu l’enquête de @ Martin, j’ai répété avec le TOP suggéré (@num) dans les deux cas, c’est-à-dire
et voici les résultats de chronométrage:
Je n'ai pas pu obtenir le plan d'exécution réel, car la requête n'est jamais retournée! Il semble qu'un bug est probable. (Exécution de Microsoft SQL Server 2008 R2 (RTM) - 10.50.1600.1 (X64))
la source
SORT
opérateur pour les GUID?NEWSEQUENTIALID
toute façon. Il approfondira l’index, utilisera 20% de pages de données supplémentaires dans le cas du PO et ne sera garanti d’augmenter que jusqu’à ce que la machine soit redémarrée, ce qui présente de nombreux inconvénients par rapport à unidentity
. Il semble simplement que, dans ce cas, le plan de requête en ajoute un autre inutile!Sur une nouvelle base de données dans un modèle de récupération simple avec un fichier de données de 1 Go et un fichier journal de 3 Go (ordinateur portable, les deux fichiers se trouvant sur le même lecteur) et un intervalle de récupération défini sur 100 minutes (pour éviter un point de contrôle faussant les résultats), je vois. des résultats similaires à ceux de la rangée unique
inserts
.J'ai testé trois cas: pour chaque cas, j'ai effectué 20 lots d'insertion de 100 000 lignes individuellement dans les tableaux suivants. Les scripts complets se trouvent dans l'historique des révisions de cette réponse .
Pour la troisième table, le test a inséré des lignes avec une
Id
valeur d' incrémentation , mais celle-ci a été calculée automatiquement en incrémentant la valeur d'une variable dans une boucle.La moyenne du temps pris sur les 20 lots a donné les résultats suivants.
Conclusion
Donc, il semble vraiment être des frais généraux de la
identity
processus de création qui est responsable des résultats. Pour l'entier incrémentant auto-calculé, les résultats sont bien plus en ligne avec ce que l'on pourrait s'attendre à voir si on ne considère que le coût des entrées-sorties.Lorsque je mets le code d'insertion décrit ci-dessus dans des procédures stockées et que
sys.dm_exec_procedure_stats
je le passe en revue, il donne les résultats suivantsDonc, dans ces résultats
total_worker_time
est environ 30% plus élevé. Cela représenteIl semble donc simplement que le code qui génère la
IDENTITY
valeurNEWSEQUENTIALID()
consomme plus de ressources de processeur que celui qui génère le (La différence entre les 2 chiffres est de 10231308 ce qui correspond à une moyenne de 5µs par insertion.) Et que pour cette définition de table ce coût de processeur fixe était suffisamment élevé pour compenser les lectures et écritures logiques supplémentaires dues à la plus grande largeur de la clé. (NB: Itzik Ben Gan a fait des tests similaires ici et a trouvé une pénalité de 2µs par insertion)Alors, pourquoi est
IDENTITY
plus intensive en CPU queUuidCreateSequential
?Je crois que cela est expliqué dans cet article . Pour chaque dixième
identity
valeur générée, SQL Server doit écrire la modification dans les tables système sur le disque.Qu'en est-il des inserts MultiRow?
Lorsque les 100 000 lignes sont insérées dans une seule déclaration, j'ai constaté que la différence disparaissait avec encore peut-être un léger avantage pour le
GUID
cas, mais aucun résultat aussi net. La moyenne pour 20 lots dans mon test étaitLa raison pour laquelle il n'y a pas de pénalité apparente dans le code de Phil et dans le premier ensemble de résultats de Mitch est parce qu'il est arrivé que le code que j'ai utilisé pour effectuer l'insertion à plusieurs lignes soit utilisé
SELECT TOP (@NumRows)
. Cela empêchait l'optimiseur d'estimer correctement le nombre de lignes à insérer.Cela semble être un avantage, car il existe un certain point de basculement auquel une nouvelle opération de tri sera ajoutée pour les (supposément séquentielles!)
GUID
.Cette opération de tri n'est pas requise à partir du texte explicatif dans BOL .
Il me semblait donc un bogue ou une optimisation manquante que SQL Server ne reconnaît pas que la sortie du scalaire de calcul sera déjà triée comme elle le fait apparemment pour la
identity
colonne. ( Edit j'ai signalé ceci et le problème de tri inutile est maintenant résolu dans Denali )la source
C'est très simple: avec le GUID, il est moins coûteux de générer le numéro suivant dans la ligne que pour IDENTITY (la valeur actuelle du GUID ne doit pas être stockée, mais IDENTITY). Cela est vrai même pour NEWSEQUENTIALGUID.
Vous pouvez rendre le test plus équitable et utiliser un SEQUENCER avec un grand CACHE, ce qui est meilleur marché qu’IDENTITY.
Mais comme le dit MR, les GUID présentent des avantages majeurs. En fait, elles sont BEAUCOUP plus évolutives que les colonnes IDENTITY (mais uniquement si elles ne sont PAS séquentielles).
Voir: http://blog.kejser.org/2011/10/05/boosting-insert-speed-by-generating-scalable-keys/
la source
IDENTITY
. d'où les plaintes iciJe suis fasciné par ce type de question. Pourquoi avez-vous dû le poster un vendredi soir? :)
Je pense que même si votre test est UNIQUEMENT destiné à mesurer les performances INSERT, vous avez (peut-être) introduit un certain nombre de facteurs pouvant induire en erreur (bouclage, transaction de longue durée, etc.)
Je ne suis pas complètement convaincu que ma version prouve quoi que ce soit, mais identité fonctionne mieux que les GUID qu'il contient (3,2 secondes contre 6,8 secondes sur un PC domestique):
la source
J'ai exécuté votre exemple de script à plusieurs reprises en apportant quelques modifications au nombre et à la taille des lots (et merci beaucoup de nous l'avoir fourni).
Tout d'abord, je dirai que vous ne mesurez qu'un seul aspect de la performance des touches: la
INSERT
vitesse. Donc, à moins que vous ne cherchiez spécifiquement à importer des données dans les tableaux le plus rapidement possible, cet animal est bien plus riche.Mes découvertes étaient en général similaires aux vôtres. Cependant, je mentionnerais que la variance de
INSERT
vitesse entreGUID
etIDENTITY
(int) est légèrement plus grande avecGUID
queIDENTITY
- peut-être +/- 10% entre les essais. Les lots utilisésIDENTITY
variaient de moins de 2 à 3% à chaque fois.Il faut également noter que mon boîtier de test est nettement moins puissant que le vôtre. Je devais donc utiliser un nombre de lignes plus petit.
la source
Je vais faire référence à une autre conférence sur stackoverflow pour le même sujet: https://stackoverflow.com/questions/170346/what-are-the-performance-improvement-of-sequential-guid-over-standard-guid
Une chose que je sais, c’est que le fait d’avoir des GUID séquentiels, c’est que l’utilisation de l’index est meilleure en raison du très faible mouvement des feuilles et donc de la réduction de la recherche HD. Je pense que pour cette raison, les encarts seraient plus rapides, car il n’aurait pas à distribuer les clés sur un grand nombre de pages.
Mon expérience personnelle est que lorsque vous implémentez une base de données à fort trafic, il est préférable d’utiliser des GUID, car cela la rend beaucoup plus évolutive pour une intégration avec d’autres systèmes. Cela vaut pour la réplication, en particulier, et pour les limites int / bigint ... non pas que vous manquiez de bigints, mais que vous finissiez par le faire, et que vous retourniez en arrière.
la source