Utilisation de vues indexées pour les agrégats - trop beau pour être vrai?

28

Nous avons un entrepôt de données avec un nombre d’enregistrements assez important (10 à 20 millions de lignes) et nous exécutons souvent des requêtes qui comptent les enregistrements entre certaines dates ou comptent des enregistrements avec certains indicateurs, par exemple

SELECT
    f.IsFoo,
    COUNT(*) AS WidgetCount
FROM Widgets AS w
JOIN Flags AS f
    ON f.FlagId = w.FlagId
WHERE w.Date >= @startDate
GROUP BY f.IsFoo

Les performances ne sont pas terribles, mais peuvent être relativement lentes (peut-être 10 secondes sur un cache froid).

Récemment, j'ai découvert que je pouvais l'utiliser GROUP BYdans des vues indexées et j'ai donc essayé quelque chose de similaire au suivant

CREATE VIEW TestView
WITH SCHEMABINDING
AS
    SELECT
        Date,
        FlagId,
        COUNT_BIG(*) AS WidgetCount
    FROM Widgets
    GROUP BY Date, FlagId;
GO

CREATE UNIQUE CLUSTERED INDEX PK_TestView ON TestView
(
    Date,
    FlagId
);

Par conséquent, les performances de ma première requête sont désormais <100 ms, et la vue et l'index résultants sont <100 Ko (bien que notre nombre de lignes soit élevé, la plage de dates et d'ID d'indicateur signifie que cette vue ne contient que 1000 à 2000 lignes).

Je pensais que cela réduirait peut-être les performances des écritures dans la table Widget, mais non - les performances des insertions et des mises à jour dans cette table sont à peu près inchangées pour autant que je sache (en plus, étant un entrepôt de données, cette table est mise à jour rarement. en tous cas)

Pour moi, cela semble beaucoup trop beau pour être vrai - n'est-ce pas? À quoi dois-je faire attention lorsque j'utilise des vues indexées de cette manière?

Justin
la source
2
Pouvez-vous réécrire vos scripts pour qu'ils soient réellement du SQL valide? Vos scripts SELECTet CREATE VIEWsont erronés, car je pense que c'est votre CREATE INDEXscript.
Mark Sinkinson
2
@MarkSinkinson Apologies, s'avère difficile d'essayer d'écrire du SQL valide pour des tables imaginaires
Justin
La partie `` trop beau pour être vrai '' est venue pour moi lorsque je voulais des vues plus avancées, telles que celles contenant MAX, des jointures auto ou externes, ou l'indexation d'une vue qui fait elle-même référence à une autre vue - qui dans SQL Server au moins ne le sont pas autorisé docs.microsoft.com/en-us/sql/relational-databases/views/… . Donc, je finis toujours par devenir trop ambitieux et ensuite devoir revoir les choses à la baisse. Mais pour les agrégations plus simples, elles sont vraiment excellentes - même SUM est pris en charge.
Simon_Weaver

Réponses:

29

Comme vous l'avez noté, la vue elle-même ne matérialise qu'un petit nombre de lignes - donc même si vous mettez à jour la table entière, les E / S supplémentaires impliquées dans la mise à jour de la vue sont négligeables. Vous avez probablement déjà ressenti la plus grande douleur que vous ressentirez lorsque vous aurez créé la vue. Le prochain plus proche sera si vous ajoutez un gazillion de lignes à la table de base avec un tas de nouveaux ID qui nécessitent de nouvelles lignes dans la vue.

Ce n'est pas trop beau pour être vrai. Vous utilisez les vues indexées exactement comment elles devaient être utilisées - ou au moins l'un des moyens les plus efficaces: payer pour les futures agrégations de requêtes au moment de l'écriture. Cela fonctionne mieux lorsque le résultat est beaucoup plus petit que la source et bien sûr lorsque les agrégations sont demandées plus souvent que les données sous-jacentes sont mises à jour (plus courantes en DW qu'en OLTP, en général).

Malheureusement, beaucoup de gens pensent que l'indexation d'une vue est magique - un index ne rendra pas toutes les vues plus efficaces, en particulier les vues qui joignent simplement des tables et / ou produisent le même nombre de lignes que la source (ou même se multiplient). Dans ces cas, les E / S de la vue sont identiques ou même pires que la requête d'origine, non seulement parce qu'il y a la même ou plusieurs lignes, mais souvent elles stockent et matérialisent également plus de colonnes. Ainsi, la matérialisation de ceux-ci à l'avance n'apporte aucun gain, car - même avec les SSD - les E / S, le réseau et le traitement / rendu client restent les principaux goulots d'étranglement dans le retour de grands ensembles de résultats au client. Les économies que vous obtenez en évitant la jointure lors de l'exécution ne sont tout simplement pas mesurables par rapport à toutes les autres ressources que vous utilisez toujours.

Comme les index non clusterisés, faites juste attention à ne pas trop le faire. Si vous ajoutez 10 vues indexées différentes à une table, vous allez voir plus d'impact sur la partie écriture de votre charge de travail, surtout si les colonnes de regroupement ne sont pas (dans) la clé de clustering.

Mon Dieu, je voulais bloguer sur ce sujet.

Aaron Bertrand
la source
19

Les réponses d'Aarons ont bien couvert cette question. Deux choses à ajouter:

  1. Les vues indexées d'agrégation peuvent entraîner des conflits et des blocages entre les lignes. Normalement, deux insertions ne bloquent pas (sauf pour des conditions plutôt rares telles que l'escalade de verrou ou les collisions de hachage de verrou). Mais si les deux insertions s'adressent au même groupe dans la vue, elles s'affronteront. Le même point représente tout ce qui prend des verrous (DML, conseils de verrouillage).
  2. Les vues indexées qui ne s'agrègent pas peuvent également être utiles. Ils vous permettent d'indexer sur des colonnes de plusieurs tables. De cette façon, vous pouvez filtrer efficacement sur une table et trier par une colonne d'une table jointe. Ce modèle peut convertir la jointure de table complète en minuscules requêtes à temps constant.

J'ai utilisé à la fois des vues d'agrégation et de jointure avec un avantage extrême.

Dans l'ensemble, votre cas d'utilisation semble être un cas parfait. Les vues indexées sont une technique largement sous-utilisée.

usr
la source