Un système multi-locataire avec SQL Server 2016, Shard ou l'isolement du locataire via une base de données distincte par locataire?


12

Étant donné le cas d'utilisation:

  • Les données des locataires ne doivent pas interférer, un locataire n'a pas besoin des données d'un autre locataire.
  • Chaque locataire peut potentiellement avoir un grand volume de données historiques.
  • SQL Server est hébergé dans une instance AWS EC2.
  • Chaque locataire est géographiquement éloigné.
  • Il est prévu d'utiliser des outils de visualisation tiers tels que PowerBI Embedded
  • Le volume de données devrait augmenter avec le temps
  • Le coût du système est limité.
  • La solution doit être maintenable sans un DBA de production 24/7
  • La solution doit pouvoir évoluer horizontalement.
  • Le nombre total de locataires est inférieur à 50

Quelle serait une architecture recommandée, existe-t-il des implémentations de référence pour ce cas d'utilisation? Je pense que de nombreuses personnes ont peut-être déjà été confrontées à ce problème pour le développement de logiciels d'entreprise.

Je pense que c'est une situation différente de la gestion d'un nombre croissant de locataires dans l'architecture de base de données multi-locataires . Le cas d'utilisation mentionné dans cette question concerne un nombre plus élevé de locataires, ce qui est très différent d'avoir très peu (50) de grands locataires. L'architecture mentionnée pourrait être une solution ici, et c'est ce que je veux en savoir plus.

Réponses:


16

Le problème avec le partage est que l'application doit savoir quel fragment à interroger. Généralement, cela se fait en partageant quelque chose comme client. Je vais adapter l' un de mes anciens articles de blog pour l'utiliser comme réponse.

Lorsque vous créez une application pour de nombreux clients, il existe deux façons courantes de concevoir la ou les bases de données:

  • Option A: placer tous les clients dans la même base de données
  • Option 2: créer une base de données par client

Mettre tous les clients dans la même base de données

C'est simple: ajoutez simplement une table Client en haut du schéma, ajoutez une table ClientUsers pour vous assurer que les gens ne voient que leurs propres données, et c'est parti.

Avantages de cette approche:

Gestion simplifiée des schémas. Lorsque les développeurs déploient une nouvelle version de l'application, ils n'ont qu'à effectuer des modifications de schéma dans une seule base de données. Il n'y a aucun souci à ce que différents clients soient désynchronisés ou sur la mauvaise version.

Optimisation des performances plus facile. Nous pouvons vérifier l'utilisation et les statistiques des index en un seul endroit, implémenter facilement les améliorations et voir immédiatement les effets sur tous nos clients. Avec des centaines ou des milliers de bases de données, même le plus petit changement peut être difficile à coordonner. Nous pouvons vérifier le contenu de notre cache de procédures et savoir avec certitude quelles requêtes ou procédures stockées sont les plus intensives dans l'ensemble de notre application, alors que si nous utilisons des bases de données distinctes par client, nous pouvons avoir plus de difficulté à agréger l'utilisation des requêtes sur différents plans d'exécution.

Plus facile de créer une API externe. Si nous devons accorder l'accès à l'ensemble de notre base de données aux étrangers pour créer des produits, nous pouvons le faire plus facilement si toutes les données se trouvent dans une seule base de données. Si l'API doit gérer le regroupement des données de plusieurs bases de données sur plusieurs serveurs, elle ajoute du temps de développement et de test. (D'un autre côté, cette chose «plusieurs serveurs» commence à faire allusion à une restriction pour le scénario d'une seule base de données pour les gouverner tous: une base de données signifie généralement que toute notre charge affecte un seul serveur de base de données.) Dans votre cas , avec PowerBI, avoir tout le monde dans une base de données facilitera la gestion des connexions.

Haute disponibilité et reprise après sinistre plus faciles. Il est vraiment, vraiment simple de gérer la mise en miroir de bases de données, l'envoi de journaux, la réplication et le clustering si tout ce dont nous avons à nous soucier est d'une seule base de données. Nous pouvons rapidement construire une sacrée infrastructure.

Mettre chaque client dans sa propre base de données ou fragment

Vous avez toujours besoin d'une liste de clients, mais maintenant cela devient un répertoire - pour chaque client, vous suivez également le fragment dans lequel il vit. Au démarrage, votre application interroge ce tableau et le met en cache dans la RAM. Lorsqu'il a besoin de données pour un client, il se connecte directement à ce fragment (base de données et serveur).

Avantages de cette approche:

Restaurations client unique plus faciles. Les clients sont des sacs de viande peu fiables. (Sauf le mien - ce sont des sacs de viande fiables.) Ils ont toutes sortes de moments où ils veulent récupérer toutes leurs données à un moment donné, et c'est une énorme douleur à l'arrière si leurs données sont mélangées avec d'autres données client dans les mêmes tables. Les restaurations dans un scénario de base de données client unique sont extrêmement simples: restaurez simplement la base de données du client. Personne d'autre n'est affecté.

Exportations de données plus faciles. Les clients adorent mettre la main sur leurs données. Ils veulent la sécurité de savoir qu'ils peuvent extraire leurs données à tout moment, en évitant le scénario de verrouillage redouté des fournisseurs, et ils veulent faire leurs propres rapports. Les données de chaque client étant isolées dans leur propre base de données, nous pouvons simplement leur donner une copie de leur propre sauvegarde de base de données. Nous n'avons pas à créer d'API d'exportation de données.

Évolutivité multiserveur facilitée. Lorsque notre application a besoin de plus de puissance que ce que nous pouvons obtenir d'un seul serveur, nous pouvons diviser les bases de données entre plusieurs serveurs. Nous pouvons également répartir la charge géographiquement, en mettant les serveurs en Asie ou en Europe pour être plus proches des clients.

Optimisation des performances par client plus facile. Si certains clients utilisent des fonctionnalités ou des rapports différents, nous pouvons créer un ensemble spécialisé d'index ou de vues indexées uniquement pour ces clients sans augmenter la taille des données de tout le monde. Certes, il y a un certain risque ici - en permettant des différences de schéma entre les clients, nous venons de rendre nos déploiements de code un peu plus risqués et notre gestion des performances plus difficile.

Gestion de la sécurité simplifiée. Tant que nous avons correctement verrouillé la sécurité avec un utilisateur par base de données, nous n'avons pas à nous soucier de l'accès du client X aux données du client Y. Cependant, si nous n'utilisons qu'une seule connexion pour tout le monde, nous n'avons pas vraiment répondu à cette préoccupation.

Fenêtres d'entretien plus faciles. Dans un environnement mondial où les clients sont dispersés dans le monde entier, il est plus facile de mettre les clients hors ligne pour la maintenance si nous pouvons le faire en groupes ou en zones.

Lequel est bon pour toi?

Il n'y a pas un seul bon choix: vous devez connaître les forces et les faiblesses de votre propre entreprise. Prenons deux de mes clients comme exemples.

La société A excelle dans le réglage des performances matérielles. Ils sont vraiment, vraiment bons pour arracher le dernier bit de performances du matériel, et ils ne craignent pas de remplacer leur matériel SQL Server sur un cycle de 12 à 18 mois. (Ils rafraîchissent les serveurs Web tous les 4 à 6 mois!) Le talon d'Achille est une exigence de conformité et de sécurité extrême. Ils ont des besoins d'audit incroyables, et il est simplement plus facile pour eux d'implémenter des contrôles pare-balles sur un seul serveur, une seule base de données que de gérer ces exigences sur des milliers de bases de données sur des dizaines de serveurs. Ils ont choisi une base de données, un serveur, de nombreux clients.

La société 2 excelle dans les pratiques de développement. La gestion des modifications de schéma et des déploiements de code sur des milliers de bases de données n'est tout simplement pas un problème pour eux. Ils ont des clients dans le monde entier et traitent les transactions par carte de crédit pour ces clients 24h / 24. Ils ont besoin de pouvoir répartir la charge géographiquement et ils ne veulent pas remplacer les serveurs dans le monde tous les 12 à 18 mois. Ils ont choisi une base de données pour chaque client, et cela porte ses fruits alors qu'ils commencent à installer des serveurs SQL en Asie et en Europe pour leurs clients offshore.


"Dans votre cas, avec PowerBI, avoir tout le monde dans une base de données facilitera la gestion des connexions". À l'heure actuelle, PowerBI Embedded ne dispose pas de sécurité au niveau des lignes et, par conséquent, le fait que chaque locataire dans une base de données suscite des doutes sur ce cas d'utilisation, voir: community.powerbi.com/t5/Developer/… , à la lumière de ces informations, pourriez-vous reformuler ou suggérer une alternative ou corriger ma compréhension?
DS

En outre, "Mettre chaque client dans sa propre base de données ou fragment" pourrait vous expliquer la différence ici entre ces deux suggestions
DS

Je dirai simplement que devoir déployer sur plus d'une base de données n'est pas aussi mauvais que vous le pensez. En 2017, nous avons de nombreuses options qui facilitent le déploiement des modifications sur 1, 5 ou 900 bases de données. Et lorsque vous avez des exceptions pour des clients spécifiques, celles-ci peuvent généralement être introduites dans ces bases de données de manière à ne pas interférer avec le code commun.
Aaron Bertrand

5

Une autre considération que je n'ai pas encore vue dans d'autres réponses.

Avoir une conception qui permet à de nombreux locataires dans une seule base de données donnera plus tard de la flexibilité. Si des demandes de chargement / évolutivité / sécurité / géolocalisation suggèrent ultérieurement qu'un locataire devrait avoir une base de données distincte, elle peut être créée en restaurant la base de données currect sur la nouvelle instance. Les données des autres locataires sont toujours protégées par les mécanismes en place. Les données désormais obsolètes peuvent être supprimées au fur et à mesure des anciennes et des nouvelles bases de données si le temps le permet.

L'inverse n'est pas vrai. La consolidation de nombreuses bases de données à locataire unique nécessitera beaucoup plus de travail.


4

Une pratique qui rend les modèles multi-locataires beaucoup plus faciles, même si elle rompt la normalisation *, consiste à inclure une colonne sur chaque table pour le locataire. Vous pourriez l'appeler TenantID. De cette façon, chaque requête exécutée sur la base de données peut filtrer sur TenantID sur chaque table, et vous pouvez utiliser le partitionnement de la base de données pour isoler les données de chaque locataire et accélérer les requêtes en ayant des partitions alignées. Il est beaucoup plus facile d'avoir tous les locataires dans une seule base de données de cette façon.

* Cela ne rompt pas toujours la normalisation, mais c'est possible. Par exemple, si vous avez un Personet une PersonAddresstable. La Persontable aura TenantID, PersonIDcomme clé primaire. Le PersonAddresstableau aura TenantID, PersonID, AddressTypeIDcomme clé primaire ce que je propose.

Normalement, PersonIDcela suffirait simplement , car vous pouvez le joindre à la Persontable pour trouver le fichier Tenant. Je vous suggère de reporter TenantIDà chaque table suivante, même lorsqu'une touche plus fine fonctionnerait.

Je croyais comprendre que le report de toute information dans un tableau pouvant être dérivé d'autres données était considéré comme une rupture de la normalisation. Mais peut-être que l'utilisation de touches minces n'est qu'une bonne pratique.


Merci, je suis d'accord avec la suggestion et pour ajouter en plus, je voudrais mentionner ce champ TenantID doit être un type entier et non un GUID, nous avons été brûlés de cette façon pour les performances.
DS

3
Mais même si vous choisissez de porter le TenantID dans les tables enfants, ce que vous n'avez pas à faire, une clé plus large ne signifie pas que la normalisation est «rompue». Tout comme le choix d'un GUID plutôt que l'IDENTITÉ (une clé plus large) ne rompt pas la normalisation, pas plus que le choix d'une clé naturelle plus large au lieu d'utiliser des substituts du tout.
Aaron Bertrand
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.