Étant donné l'exemple suivant:
IF OBJECT_ID('dbo.my_table') IS NOT NULL
DROP TABLE [dbo].[my_table];
GO
CREATE TABLE [dbo].[my_table]
(
[id] int IDENTITY (1,1) NOT NULL PRIMARY KEY,
[foo] int NULL,
[bar] int NULL,
[nki] int NOT NULL
);
GO
/* Insert some random data */
INSERT INTO [dbo].[my_table] (foo, bar, nki)
SELECT TOP (100000)
ABS(CHECKSUM(NewId())) % 14,
ABS(CHECKSUM(NewId())) % 20,
n = CONVERT(INT, ROW_NUMBER() OVER (ORDER BY s1.[object_id]))
FROM
sys.all_objects AS s1
CROSS JOIN
sys.all_objects AS s2
GO
CREATE UNIQUE NONCLUSTERED INDEX [IX_my_table]
ON [dbo].[my_table] ([nki] ASC);
GO
Si je récupère tous les enregistrements classés par [nki]
(index non cluster):
SET STATISTICS TIME ON;
SELECT id, foo, bar, nki FROM my_table ORDER BY nki;
SET STATISTICS TIME OFF;
SQL Server Execution Times: CPU time = 266 ms, elapsed time = 493 ms
L'optimiseur choisit l'index clusterisé puis applique un algorithme de tri.
Mais si je le force à utiliser l'index non clusterisé:
SET STATISTICS TIME ON;
SELECT id, foo, bar, nki FROM my_table WITH(INDEX(IX_my_TABLE));
SET STATISTICS TIME OFF;
SQL Server Execution Times: CPU time = 311 ms, elapsed time = 188 ms
Ensuite, il utilise un index non cluster avec une recherche de clé:
Évidemment, si l'index non clusterisé est transformé en index couvrant:
CREATE UNIQUE NONCLUSTERED INDEX [IX_my_table]
ON [dbo].[my_table] ([nki] ASC)
INCLUDE (id, foo, bar);
GO
Ensuite, il utilise uniquement cet index:
SET STATISTICS TIME ON;
SELECT id, foo, bar, nki FROM my_table ORDER BY nki;
SET STATISTICS TIME OFF;
SQL Server Execution Times: CPU time = 32 ms, elapsed time = 106 ms
Question
- Pourquoi SQL Server utilise-t-il l'index clusterisé plus un algorithme de tri au lieu d'utiliser un index non clusterisé même si le temps d'exécution est 38% plus rapide dans ce dernier cas?
Réponses:
Parce que SQL Server utilise un optimiseur basé sur les coûts basé sur des statistiques et non sur des informations d'exécution.
Au cours du processus d'estimation des coûts pour cette requête, il évalue réellement le plan de recherche, mais estime qu'il faudra plus d'efforts. (Notez le «coût estimé des sous-arbres» lorsque vous survolez SELECT dans le plan d'exécution). Ce n'est pas nécessairement une mauvaise hypothèse non plus - sur ma machine de test, le plan de recherche prend 6 fois le processeur du tri / scan.
Consultez la réponse de Rob Farley pour savoir pourquoi SQL Server pourrait coûter plus cher au plan de recherche.
la source
Si vous deviez comparer le nombre de lectures requises dans 100 000 recherches avec ce qui est impliqué dans le tri, vous pourriez rapidement vous faire une idée de pourquoi l'Optimiseur de requête estime que le tri CIX + serait le meilleur choix.
L'exécution de la recherche finit par être plus rapide car les pages lues sont en mémoire (même si vous videz le cache, vous avez beaucoup de lignes par page, donc vous lisez les mêmes pages encore et encore, mais avec différentes quantités de fragmentation ou une pression de mémoire différente d'une autre activité, cela pourrait ne pas être le cas). Cela ne prendrait pas beaucoup de temps pour que CIX + Sort soit plus rapide, mais ce que vous voyez est que le coût d'une lecture ne prend pas en compte le coût relativement bas de frapper les mêmes pages à plusieurs reprises.
la source
J'ai décidé de creuser un peu sur cette question et j'ai découvert des documents intéressants parlant de comment et quand utiliser ou peut-être mieux, pas (forcer) l'utilisation d'un index non clusterisé.
Comme suggéré par les commentaires de John Eisbrener , l'un des articles les plus référencés, même dans d'autres blogs, est cet article intéressant de Kimberly L. Tripp:
mais ce n'est pas le seul, si vous êtes intéressé, vous pouvez jeter un œil à ces pages:
Comme vous pouvez le voir, tous se déplacent autour du concept du point de basculement .
Extrait d'un article de KL Tripp
Lorsque SQL Server utilise un index non cluster sur un segment de mémoire, il obtient essentiellement une liste de pointeurs vers les pages de la table de base. Il utilise ensuite ces pointeurs pour récupérer les lignes avec une série d'opérations appelées Row ID Lookups (RID). Cela signifie qu'au moins, il utilisera autant de pages lues que le nombre de lignes renvoyées, et peut-être plus. Le processus est quelque peu similaire avec un index clusterisé comme la table de base, avec le même résultat: plus de lectures.
Mais, quand ce point de basculement se produit?
Bien sûr, comme la plupart des choses dans cette vie, cela dépend ...
Non sérieusement, cela se produit entre 25% et 33% du nombre de pages du tableau, selon le nombre de lignes par page. Mais il y a plus de facteurs à considérer:
Extrait d'un article d'ITPRoToday
Maintenant, si j'exécute à nouveau mes requêtes à l'aide de statistiques IO:
La deuxième requête nécessite plus de lectures logiques que la première.
Dois-je éviter l'index non cluster?
Non, un index clusterisé peut être utile, mais cela vaut la peine de prendre du temps et de faire un effort supplémentaire pour analyser ce que vous essayez de réaliser avec.
Extrait d'un article de KL Tripp
la source