Pour vérifier votre demande, j'ai créé 2 tables suivant ce schéma:
- 7,9 millions d’enregistrements représentant des informations sur le solde.
- un champ d'identité comptant de 1 à 7,9 millions
- un champ numérique regroupant les enregistrements dans environ 500 000 groupes.
La première table appelée heap
a un index non clusterisé sur le champ group
. La deuxième table appelée a clust
obtenu un index clusterisé sur le champ séquentiel appelé key
et un index non clusterisé sur le champgroup
Les tests ont été exécutés sur un processeur I5 M540 avec 2 cœurs hyperthreaded, 4 Go de mémoire et fenêtres 7 bits 64 bits.
Microsoft SQL Server 2008 R2 (RTM) - 10.50.1600.1 (X64)
Apr 2 2010 15:48:46
Developer Edition (64-bit) on Windows NT 6.1 <X64> (Build 7601: Service Pack 1)
Mise à jour le 9 mars 2011 : j'ai effectué un deuxième test d'évaluation plus complet en exécutant le code .net suivant et en enregistrant la durée, le processeur, les lectures, les écritures et les montants en colonnes dans Sql Server Profiler. (Le CommandText utilisé sera mentionné dans les résultats.)
REMARQUE: La CPU et la durée sont exprimés en millisecondes.
- 1000 requêtes
- zéro requête CPU est éliminée des résultats
- 0 lignes affectées sont éliminées des résultats
int[] idList = new int[] { 6816588, 7086702, 6498815 ... }; // 1000 values here.
using (var conn = new SqlConnection(@"Data Source=myserver;Initial Catalog=mydb;Integrated Security=SSPI;"))
{
conn.Open();
using (var cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandType = CommandType.Text;
cmd.CommandText = "select * from heap where common_key between @id and @id+1000";
cmd.Parameters.Add("@id", SqlDbType.Int);
cmd.Prepare();
foreach (int id in idList)
{
cmd.Parameters[0].Value = id;
using (var reader = cmd.ExecuteReader())
{
int count = 0;
while (reader.Read())
{
count++;
}
Console.WriteLine(String.Format("key: {0} => {1} rows", id, count));
}
}
}
}
Fin de la mise à jour le 9 mars 2011 .
SELECT performance
Pour vérifier les numéros de performance, j'ai effectué les requêtes suivantes une fois sur la table des tas et une fois sur la table clust:
select * from heap/clust where group between 5678910 and 5679410
select * from heap/clust where group between 6234567 and 6234967
select * from heap/clust where group between 6455429 and 6455729
select * from heap/clust where group between 6655429 and 6655729
select * from heap/clust where group between 6955429 and 6955729
select * from heap/clust where group between 7195542 and 7155729
Les résultats de cette référence sont les suivants heap
:
rows reads CPU Elapsed
----- ----- ----- --------
1503 1510 31ms 309ms
401 405 15ms 283ms
2700 2709 0ms 472ms
0 3 0ms 30ms
2953 2962 32ms 257ms
0 0 0ms 0ms
Mise à jour du 9 mars 2011 :
cmd.CommandText = "select * from heap where group between @id and @id+1000";
- 721 lignes ont> 0 CPU et affectent plus de 0 lignes
Counter Minimum Maximum Average Weighted
--------- ------- ---------- ------- ---------
RowCounts 1001 69788 6368 -
Cpu 15 374 37 0.00754
Reads 1069 91459 7682 1.20155
Writes 0 0 0 0.00000
Duration 0.3716 282.4850 10.3672 0.00180
Fin de la mise à jour le 9 mars 2011 .
pour le tableau clust
les résultats sont:
rows reads CPU Elapsed
----- ----- ----- --------
1503 4827 31ms 327ms
401 1241 0ms 242ms
2700 8372 0ms 410ms
0 3 0ms 0ms
2953 9060 47ms 213ms
0 0 0ms 0ms
Mise à jour du 9 mars 2011 :
cmd.CommandText = "select * from clust where group between @id and @id+1000";
- 721 lignes ont> 0 CPU et affectent plus de 0 lignes
Counter Minimum Maximum Average Weighted
--------- ------- ---------- ------- ---------
RowCounts 1001 69788 6056 -
Cpu 15 468 38 0.00782
Reads 3194 227018 20457 3.37618
Writes 0 0 0 0.0
Duration 0.3949 159.6223 11.5699 0.00214
Fin de la mise à jour le 9 mars 2011 .
Performances SELECT SELECT WITH JOIN
cmd.CommandText = "select * from heap/clust h join keys k on h.group = k.group where h.group between @id and @id+1000";
Les résultats de cette référence sont les suivants heap
:
873 lignes ont> 0 CPU et affectent plus de 0 lignes
Counter Minimum Maximum Average Weighted
--------- ------- ---------- ------- ---------
RowCounts 1009 4170 1683 -
Cpu 15 47 18 0.01175
Reads 2145 5518 2867 1.79246
Writes 0 0 0 0.00000
Duration 0.8215 131.9583 1.9095 0.00123
Les résultats de cette référence sont les suivants clust
:
865 lignes ont> 0 CPU et affectent plus de 0 lignes
Counter Minimum Maximum Average Weighted
--------- ------- ---------- ------- ---------
RowCounts 1000 4143 1685 -
Cpu 15 47 18 0.01193
Reads 5320 18690 8237 4.97813
Writes 0 0 0 0.00000
Duration 0.9699 20.3217 1.7934 0.00109
UPDATE performance
Le deuxième lot de requêtes sont des instructions de mise à jour:
update heap/clust set amount = amount + 0 where group between 5678910 and 5679410
update heap/clust set amount = amount + 0 where group between 6234567 and 6234967
update heap/clust set amount = amount + 0 where group between 6455429 and 6455729
update heap/clust set amount = amount + 0 where group between 6655429 and 6655729
update heap/clust set amount = amount + 0 where group between 6955429 and 6955729
update heap/clust set amount = amount + 0 where group between 7195542 and 7155729
les résultats de ce repère pour heap
:
rows reads CPU Elapsed
----- ----- ----- --------
1503 3013 31ms 175ms
401 806 0ms 22ms
2700 5409 47ms 100ms
0 3 0ms 0ms
2953 5915 31ms 88ms
0 0 0ms 0ms
Mise à jour du 9 mars 2011 :
cmd.CommandText = "update heap set amount = amount + @id where group between @id and @id+1000";
- 811 lignes ont> 0 CPU et affectent plus de 0 lignes
Counter Minimum Maximum Average Weighted
--------- ------- ---------- ------- ---------
RowCounts 1001 69788 5598 811
Cpu 15 873 56 0.01199
Reads 2080 167593 11809 2.11217
Writes 0 1687 121 0.02170
Duration 0.6705 514.5347 17.2041 0.00344
Fin de la mise à jour le 9 mars 2011 .
les résultats de ce repère pour clust
:
rows reads CPU Elapsed
----- ----- ----- --------
1503 9126 16ms 35ms
401 2444 0ms 4ms
2700 16385 31ms 54ms
0 3 0ms 0ms
2953 17919 31ms 35ms
0 0 0ms 0ms
Mise à jour du 9 mars 2011 :
cmd.CommandText = "update clust set amount = amount + @id where group between @id and @id+1000";
- 853 lignes ont> 0 processeur et affectent plus de 0 lignes
Counter Minimum Maximum Average Weighted
--------- ------- ---------- ------- ---------
RowCounts 1001 69788 5420 -
Cpu 15 594 50 0.01073
Reads 6226 432237 33597 6.20450
Writes 0 1730 110 0.01971
Duration 0.9134 193.7685 8.2919 0.00155
Fin de la mise à jour le 9 mars 2011 .
SUPPRIMER les repères
le troisième lot de requêtes que j'ai exécuté sont des instructions de suppression
delete heap/clust where group between 5678910 and 5679410
delete heap/clust where group between 6234567 and 6234967
delete heap/clust where group between 6455429 and 6455729
delete heap/clust where group between 6655429 and 6655729
delete heap/clust where group between 6955429 and 6955729
delete heap/clust where group between 7195542 and 7155729
Le résultat de cette référence pour le heap
:
rows reads CPU Elapsed
----- ----- ----- --------
1503 10630 62ms 179ms
401 2838 0ms 26ms
2700 19077 47ms 87ms
0 4 0ms 0ms
2953 20865 62ms 196ms
0 4 0ms 9ms
Mise à jour du 9 mars 2011 :
cmd.CommandText = "delete heap where group between @id and @id+1000";
- 724 lignes ont> 0 processeur et affectent plus de 0 lignes
Counter Minimum Maximum Average Weighted
--------- ------- ---------- ------- ---------
RowCounts 192 69788 4781 -
Cpu 15 499 45 0.01247
Reads 841 307958 20987 4.37880
Writes 2 1819 127 0.02648
Duration 0.3775 1534.3383 17.2412 0.00349
Fin de la mise à jour le 9 mars 2011 .
le résultat de cette référence pour le clust
:
rows reads CPU Elapsed
----- ----- ----- --------
1503 9228 16ms 55ms
401 3681 0ms 50ms
2700 24644 46ms 79ms
0 3 0ms 0ms
2953 26955 47ms 92ms
0 3 0ms 0ms
Mise à jour du 9 mars 2011 :
cmd.CommandText = "delete clust where group between @id and @id+1000";
- 751 lignes ont> 0 processeur et affectent plus de 0 lignes
Counter Minimum Maximum Average Weighted
--------- ------- ---------- ------- ---------
RowCounts 144 69788 4648 -
Cpu 15 764 56 0.01538
Reads 989 458467 30207 6.48490
Writes 2 1830 127 0.02694
Duration 0.2938 2512.1968 24.3714 0.00555
Fin de la mise à jour le 9 mars 2011 .
INSÉRER des repères
La dernière partie de l’indice de référence est l’exécution des instructions insert.
insérer dans tas / clust (...) les valeurs (...), (...), (...), (...), (...), (...)
Le résultat de cette référence pour le heap
:
rows reads CPU Elapsed
----- ----- ----- --------
6 38 0ms 31ms
Mise à jour du 9 mars 2011 :
string str = @"insert into heap (group, currency, year, period, domain_id, mtdAmount, mtdAmount, ytdAmount, amount, ytd_restated, restated, auditDate, auditUser)
values";
for (int x = 0; x < 999; x++)
{
str += string.Format(@"(@id + {0}, 'EUR', 2012, 2, 0, 100, 100, 1000 + @id,1000, 1000,1000, current_timestamp, 'test'), ", x);
}
str += string.Format(@"(@id, 'CAD', 2012, 2, 0, 100, 100, 1000 + @id,1000, 1000,1000, current_timestamp, 'test') ", 1000);
cmd.CommandText = str;
- 912 instructions ont> 0 CPU
Counter Minimum Maximum Average Weighted
--------- ------- ---------- ------- ---------
RowCounts 1000 1000 1000 -
Cpu 15 2138 25 0.02500
Reads 5212 7069 6328 6.32837
Writes 16 34 22 0.02222
Duration 1.6336 293.2132 4.4009 0.00440
Fin de la mise à jour le 9 mars 2011 .
Le résultat de cette référence pour le clust
:
rows reads CPU Elapsed
----- ----- ----- --------
6 50 0ms 18ms
Mise à jour du 9 mars 2011 :
string str = @"insert into clust (group, currency, year, period, domain_id, mtdAmount, mtdAmount, ytdAmount, amount, ytd_restated, restated, auditDate, auditUser)
values";
for (int x = 0; x < 999; x++)
{
str += string.Format(@"(@id + {0}, 'EUR', 2012, 2, 0, 100, 100, 1000 + @id,1000, 1000,1000, current_timestamp, 'test'), ", x);
}
str += string.Format(@"(@id, 'CAD', 2012, 2, 0, 100, 100, 1000 + @id,1000, 1000,1000, current_timestamp, 'test') ", 1000);
cmd.CommandText = str;
- 946 instructions ont> 0 CPU
Counter Minimum Maximum Average Weighted
--------- ------- ---------- ------- ---------
RowCounts 1000 1000 1000 -
Cpu 15 2403 21 0.02157
Reads 6810 8997 8412 8.41223
Writes 16 25 19 0.01942
Duration 1.5375 268.2571 6.1463 0.00614
Fin de la mise à jour le 9 mars 2011 .
Conclusions
Bien qu'il y ait davantage de lectures logiques en cours lors de l'accès à la table avec l'index clusterisé et non clusterisé (tout en utilisant l'index non clusterisé), les résultats de performance sont les suivants:
- Les instructions SELECT sont comparables
- Les instructions UPDATE sont plus rapides avec un index clusterisé en place
- Les instructions DELETE sont plus rapides avec un index clusterisé en place
- Les instructions INSERT sont plus rapides avec un index clusterisé en place
Bien sûr, mon repère était très limité sur un type de table spécifique et avec un ensemble de requêtes très limité, mais je pense que sur la base de ces informations, nous pouvons déjà commencer à dire qu'il est pratiquement toujours préférable de créer un index clusterisé sur votre table.
Mise à jour du 9 mars 2011 :
Comme nous pouvons le constater grâce aux résultats ajoutés, les conclusions sur les tests limités n’étaient pas correctes dans tous les cas.
Les résultats indiquent maintenant que les seules instructions bénéficiant de l'index clusterisé sont les instructions de mise à jour. Les autres instructions sont environ 30% plus lentes sur la table avec index clusterisé.
Quelques graphiques supplémentaires où j'ai tracé la durée pondérée par requête pour tas vs clust.
Comme vous pouvez le constater, le profil de performance des instructions d’insertion est très intéressant. Les pointes sont causées par quelques points de données qui prennent beaucoup plus de temps à compléter.
Fin de la mise à jour le 9 mars 2011 .
Comme l'explique bien Kimberly Tripp - la reine de l'indexation - dans son billet de blog The Clustered Index Debate ... , le fait de disposer d'une clé de clustering sur une table de base de données accélère sensiblement toutes les opérations, et pas seulement
SELECT
.Les commandes SELECT sont généralement plus lentes sur un segment comparé à une table en cluster, à condition que vous sélectionniez une bonne clé de mise en cluster - quelque chose comme un
INT IDENTITY
. Si vous utilisez une clé de clusterage vraiment mauvaise, comme un GUID ou une clé composée avec beaucoup de composants de longueur variable, un segment de mémoire peut être plus rapide, mais seulement à ce moment-là. Mais dans ce cas, vous devez vraiment nettoyer la conception de votre base de données en premier lieu ...Donc, en général, je ne pense pas qu’il soit inutile de choisir une bonne clé de regroupement et vous devriez en tirer profit à tous les égards.
la source
Je viens de tomber sur cet article de Joe Chang qui répond à cette question. Collé ses conclusions ci-dessous.
la source