L'évolutivité consiste à précalculer (mettre en cache), à répartir ou à simplifier le travail répété au strict nécessaire, afin de minimiser l'utilisation des ressources par unité de travail. Pour bien évoluer, vous ne faites rien en volume si ce n'est nécessaire, et vous vous assurez d'effectuer les tâches réellement nécessaires de la manière la plus efficace possible.
Dans ce contexte, il est évident que joindre deux sources de données distinctes est relativement lent, du moins par rapport à ne pas les joindre, car c'est un travail que vous devez effectuer en direct au moment où l'utilisateur en fait la demande.
Mais souvenez-vous que l'alternative est de ne plus disposer du tout de deux morceaux de données distincts; vous devez mettre les deux points de données disparates dans le même enregistrement. Vous ne pouvez pas combiner deux éléments de données différents sans conséquence quelque part, donc assurez-vous de comprendre le compromis.
La bonne nouvelle est que les bases de données relationnelles modernes sont bonnes pour les jointures. Vous ne devriez pas vraiment considérer les jointures comme lentes avec une bonne base de données utilisée correctement. Il existe plusieurs façons favorables à l'évolutivité de rendre les jointures brutes beaucoup plus rapides :
- Joindre sur une clé substitutive (colonne autonumérisée/identité) plutôt que sur une clé naturelle. Cela signifie des comparaisons plus petites (et donc plus rapides) pendant l'opération de jointure
- Index
- Vues matérialisées/indexées (pensez à cela comme une jointure précalculée ou une dénormalisation gérée)
- Colonnes calculées. Vous pouvez l'utiliser pour hacher ou précalculer les colonnes clés d'une jointure, de sorte que ce qui aurait été une comparaison compliquée pour une jointure est maintenant beaucoup plus petite et potentiellement pré-indéxé.
- Partitions de table (aide avec de grands ensembles de données en répartissant la charge sur plusieurs disques, ou limitant ce qui aurait pu être un balayage de table à un balayage de partition)
- OLAP (précalcule les résultats de certains types de requêtes/jointures. Ce n'est pas tout à fait vrai, mais vous pouvez le considérer comme une dénormalisation générique)
- Réplication, groupes de disponibilité, envoi de journaux, ou d'autres mécanismes permettant à plusieurs serveurs de répondre aux requêtes de lecture pour la même base de données, et ainsi répartir votre charge de travail entre plusieurs serveurs.
- Utilisation d'une couche de mise en cache comme Redis pour éviter de rerun les requêtes nécessitant des jointures complexes.
J'irais même jusqu'à dire que la principale raison pour laquelle les bases de données relationnelles existent est de vous permettre de réaliser des jointures efficacement*. Ce n'est certainement pas uniquement pour stocker des données structurées (vous pourriez le faire avec des structures de fichiers plats comme csv ou xml). Quelques-unes des options que j'ai énumérées vous permettront même de construire entièrement votre jointure à l'avance, de sorte que les résultats soient déjà prêts avant que vous ne lanciez la requête - tout comme si vous aviez dénormalisé les données (avec des opérations d'écriture plus lentes, il est vrai).
Si vous avez une jointure lente, vous n'utilisez probablement pas votre base de données correctement.
La dénormalisation ne devrait être effectuée qu'après l'échec de ces autres techniques. Et la seule façon de véritablement juger de "l'échec" est de définir des objectifs de performance significatifs et de mesurer par rapport à ces objectifs. Si vous n'avez pas mesuré, il est trop tôt pour même penser à la dénormalisation.
* C'est-à-dire exister en tant qu'entités distinctes des simples collections de tables. Une raison supplémentaire pour une véritable base de données relationnelle est l'accès concurrentiel sécurisé.