Existe-t-il des directives ou des règles générales pour déterminer quand stocker les valeurs agrégées et quand les calculer à la volée?
Par exemple, supposons que j'ai des widgets que les utilisateurs peuvent évaluer (voir le schéma ci-dessous). Chaque fois que j'affiche un widget, je peux calculer la note moyenne des utilisateurs à partir du tableau Ratings
. Alternativement, je pourrais stocker la note moyenne sur la table Widget
. Cela m'éviterait de devoir calculer la note chaque fois que j'affiche le widget, mais je devrais alors recalculer la note moyenne chaque fois qu'un utilisateur a évalué un widget.
Ratings Widgets
--------- -------
widget_id widget_id
user_id name
rating avg_rating <--- The column in question
Cela dépend. Le précalcul des valeurs agrégées impose une charge plus importante sur les écritures, les dériver rend les lectures plus difficiles
Si vous accédez fréquemment à une valeur dérivée, le pré-calcul est une étape de dénormalisation valide. Cependant, dans ce cas, je recommande d'utiliser une vue matérialisée (une vue, écrite sur disque, liée par déclencheur aux tables parentes). La vue matérialisée est conçue pour stocker des données fréquemment demandées mais fastidieuses à dériver, et est utile pour un nombre élevé d'écritures et un faible nombre de lectures.
Dans un scénario à écriture élevée et à lecture élevée, envisagez d'avoir une tâche en arrière-plan qui imite les effets d'une vue matérialisée, mais en moins de temps réel. Cela présentera une moyenne "assez bonne" tout en préservant les performances d'écriture et de lecture.
En aucun cas, vous ne devez traiter la colonne dérivée comme une colonne "normale": assurez-vous que les données présentées dans la "vue" Widgets sont présentes ailleurs dans le tableau, de sorte que l'intégralité du tuple puisse être dérivée par tous les processus que vous placez. Cette question est également fortement spécifique à la base de données (et à la version de la base de données), je recommande donc de tester les performances de l'agrégat (avec les index appropriés) par rapport à un ensemble de données de taille normale et à la vue matérialisée.
Fréquence à laquelle vous devez calculer/afficher les valeurs par rapport à la fréquence à laquelle les nombres sous-jacents sont modifiés/mis à jour.
Donc, si vous avez un site Web avec 10 000 visites quotidiennes qui affiche une valeur qui ne changera qu'une fois par heure, je le calculerais lorsque les valeurs sous-jacentes changent (pourrait être un déclencheur de base de données, peu importe).
Si vous avez un outil pour consulter les statistiques, où les statistiques changent à la seconde près, mais que vous n'avez que trois personnes qui y ont accès, et qu'elles ne le consultent que quelques fois par jour, je serais plus susceptible de calculer à la volée. (à moins que cela prenne quelques minutes pour calculer qu'avoir eu des données périmées en premier lieu n'est pas un gros problème ... et mon patron me dit de simplement générer la chose à partir de cron toutes les heures, donc il n'a pas attendre quand il veut le regarder.)
Utilisez la table StaleWidgets comme file d'attente de widgets "invalides" (à recalculer). Utilisez une autre tâche de thread (asynchrone) qui peut recalculer ces valeurs. La période ou le moment des recalculs dépend des exigences du système:
Je suggérerais de calculer à la volée si le calcul n'est pas trop lourd et dans le cas où vous avez des calculs complexes et des mises à jour fréquentes mais pas une lecture fréquente, vous pouvez stocker des données calculées et avoir une colonne supplémentaire (bool) qui enregistrera si un recalcul est nécessaire ou non . par exemple. définissez cette colonne sur true chaque fois que le recalcul doit être effectué, mais ne faites pas de recalcul et lorsque vous effectuez le recalcul, définissez cette colonne sur false (cela représentera la valeur calculée est la plus récente et non périmée).
De cette façon, vous n'avez pas à recalculer à chaque fois, vous ne calculez que lorsque vous devez lire et que la valeur de la colonne de recalcul est vraie. De cette façon, vous économiserez beaucoup de recalcul.
Pour le cas en particulier, il existe une solution différente où vous n'avez pas à ajouter toutes les notes et à la diviser par le total pour trouver la moyenne. Au lieu de cela, vous pouvez avoir un autre champ qui contient le total des avis, donc chaque fois que vous ajoutez une note, vous calculez la nouvelle moyenne en utilisant (avg_rating × total + new_rating)/total, c'est beaucoup plus rapide que l'agrégat et réduit les lectures de disque depuis que vous ne pas avoir besoin d'accéder à toutes les valeurs de notation. Des solutions similaires pourraient s'appliquer à d'autres cas.
L'inconvénient est que ce n'est pas une transaction acide, vous pourriez donc vous retrouver avec une note obsolète. Mais vous pouvez toujours résoudre ce problème en utilisant des déclencheurs dans la base de données. L'autre problème est que la base de données n'est plus normalisée, mais n'ayez pas peur de dénormaliser les données en échange de performances.