Conception de base de données SQL Server pour les données «archivées mais disponibles»

12

Nous avons cette grande base de données (> 1 To) que nous avons l'intention de "réduire". La base de données s'articule autour d'une entité principale, appelons-la "Visite". Pour la discussion, disons qu'il s'agit d'une base de données pour un cabinet médical.

Il existe un total de 30 "types" de visites, telles que la procédure, annuelle, de suivi, de vaccination, etc., chacune étant un tableau de subvention pour "Visite", par exemple "visit_immuno".

La base de données a accumulé environ 12 ans de données depuis 2000. Quelqu'un a proposé que nous conservions environ 3 ans de données dans la version "en direct" et que le reste se trouve dans une base de données "old_data". La date est stockée UNIQUEMENT dans la table "Visite" car elle est normalisée. La table Visit contient également une ROWVERSIONcolonne et une colonne de BIGINTpseudo-identité (en cluster). À toutes fins utiles, disons que la clé de clustering est remplie par une SEQUENCE (SQL Server 2012 Enterprise) - nous la nommerons cid.

La visit.dateclé n'est pas toujours dans le même ordre que la clé de clustering, par exemple lorsqu'un médecin effectue des visites prolongées et revient avec sa "mallette" de données, elle est fusionnée dans la table principale. Il y a aussi quelques mises à jour de la table "visit" qui entraîneront la ROWVERSIONdésynchronisation de la colonne avec les colonnes cidet date- pour le dire simplement, ni ROWVERSIONne cidferaient des clés de partition appropriées pour cette raison.

La règle commerciale pour supprimer des données du "live" est que le visit.datedoit être supérieur à 36 mois et qu'un visit_paymentenregistrement enfant doit exister. En outre, la base de données "old_data" ne contient aucune des tables de base sauf visit%.

On se retrouve donc avec:

Live DB (utilisation quotidienne) - Toutes les tables Old-Data DB - anciennes données pour les visit%tables

La proposition appelle une base de données combinée qui est un interpréteur de commandes contenant des synonymes de TOUTES les tables de base dans Live DB(sauf visit%) plus des vues qui UNIONS TOUTES sur les visit%tables des deux bases de données.

En supposant que les mêmes index sont créés dans la base de Old-Datadonnées, les requêtes fonctionneront-elles bien sur les vues UNION-ALL ? Quels types de modèles de requête peuvent déclencher le plan d'exécution pour les vues UNION-ALL ?

孔夫子
la source
3
Quelle est la motivation pour a) archiver les anciennes données et b) les garder accessibles? Frais généraux de maintenance? Problèmes de performances? Les données archivées doivent-elles être accessibles de manière transparente à l'application? Avec ou sans modification de l'application?
Mark Storey-Smith
(a) Garder la base de données principale petite. Il est répliqué sur 3 autres environnements - dev, pré-test, test. Il y a aussi les miroirs et les sauvegardes répliqués, tous soutenus par un stockage coûteux. (b) Parce que les systèmes en aval ont actuellement accès à toutes les données, cela maintient donc le statu quo. (c) Une instance de l'application pourrait s'exécuter sur la base de données "combinée" avec toutes les vues, mais je soupçonne qu'elle ne fonctionnera pas du tout.
Juste pour clarifier, les données archivées sont toujours en lecture-écriture, n'est-ce pas? Ou est-ce en lecture seule?
Jon Seigel
Les anciennes données deviendraient des pages en lecture seule et remplies à 100%. L'instance de l'application qui se connecte aux vues combinées peut générer des erreurs si quelqu'un essaie quelque chose de stupide sur d'anciennes données - peu nous importe.
Je pense qu'un groupe de fichiers en lecture seule pour les données historiques et la sauvegarde / restauration partielle couvrirait cela, sans la boue ajoutée de la base de données shell et des synonymes. Je dis pense que je ne l' ai pas touché à la mécanique de celui - ci pendant un certain temps et le besoin de me rafraîchir la mémoire. Je ne sais pas comment cela s'intégrerait à la réplication, mais je me demande pourquoi vous répliquez une base de données en direct dans des environnements en aval de toute façon.
Mark Storey-Smith

Réponses:

4

Pour plus de commodité, supposons que la base de données en direct est appelée LiveDbet que la base de données Achive est appeléeArchiveDb

  • Ajoutez une vue UNION ALL en LiveDbpointant vers les tables de la ArchiveDbbase de données via un synonyme (il n'est pas nécessaire de faire une base de données combinée avec des synonymes)
  • "Partitionnez" visit.dateet dénormalisez également cette colonne visit_paymentssi elle n'y est pas déjà (cela améliore les performances de jointure colocalisées)
  • Archivez uniquement les deux grandes tables si possible (réduit les risques de déclenchement de l'optimiseur). Conservez la vue UNION ALL et les autres tables LiveDbafin que toutes les jointures aux petites tables soient conservées localement
  • Ajoutez une contrainte CHECK sur les tables dans les deux LiveDbet ArchiveDb qui décrit la plage de visit.datecontenus dans la table. Cela permet à l'optimiseur d'éliminer la table d'archive des recherches et des analyses qui contiennent la colonne visit.data. Vous devrez mettre à jour périodiquement cette contrainte.
  • Dans la vue UNION ALL, ajoutez un critère WHERE qui filtre visit.data. Ceci s'ajoute au conseil que vous avez déjà fourni dans la contrainte de vérification. Cela maximise les chances que les filtres soient enfoncés
  • Si vous avez EE, partitionnez la table dans la base de données d'archives (mais PAS dans la base de données en direct). Si vous voulez devenir vraiment sophistiqué, utilisez la sauvegarde / restauration au niveau du groupe de fichiers des bases de données d'archives pour économiser sur les temps de sauvegarde.
  • Pensez à mettre AchiveDben mode de récupération SIMPLE si ce n'est pas déjà fait. Il est peu probable que vous ayez besoin de sauvegardes du journal des transactions deArchiveDb
  • Utilisez INSERT ... WITH (TABLOCK) SELECT ... WITH (ROWLOCK) pour forcer une journalisation minimale sur la destination lors du déplacement de données entre LiveDbetArchiveDb

Tout ce qui précède ne garantit pas que l'optimiseur éliminera les tables d'archive des recherches et des analyses, mais cela le rend plus probable.

Quand l'élimination ne se produit pas. Ce sont les effets que vous pouvez voir (cette liste peut être incomplète). Pour les recherches, vous obtiendrez une recherche supplémentaire sur chaque requête (cela augmente les IOPS). Pour les analyses, les résultats peuvent être désastreux pour les performances car vous pouvez finir par analyser à la fois l'archive et les tables en direct. Voici les moyens typiques de déclencher l'optimiseur:

  • Si vous joignez les visit%tables ensemble et n'incluez pas le visit.datadans les critères de jointure (c'est pourquoi vous souhaitez dénormaliser). Pour cette raison, vous souhaiterez peut-être modifier certaines de vos requêtes
  • Si vous obtenez une jointure de hachage entre visit.dataune autre table (par exemple une dimension de date), vous risquez de ne pas obtenir la bonne élimination des tables
  • Si vous essayez d'agréger des données sur les tables archivées
  • Si vous filtrez sur n'importe quoi MAIS visit.data, par exemple une recherche directement sur la touche de la vue.

Pour le dernier scénario, vous pouvez vous prémunir contre les pires effets en ajoutant une autre contrainte de vérification sur le cid- si cela est possible. Vous avez mentionné que la séquence cidn'est pas "propre" en ce qui concerne les dates et la progression des lignes dans le tableau. Cependant, pourriez-vous conserver un tableau qui contient les informations: "Il n'y a pas cidau-dessus de ce nombre depuis cela visit.data" ou similaire? Cela pourrait alors entraîner une contrainte supplémentaire.

Une autre chose à laquelle faire attention est que les requêtes parallèles peuvent engendrer BEAUCOUP plus de threads une fois que vous interrogez la vue partitionnée (car les deux "sous-tables" seront exposées aux mêmes optimisations parallèles). Pour ces raisons, vous souhaiterez peut-être limiter MAXDOP sur le serveur ou les requêtes parallèles.

Soit dit en passant, si vous connaissez bien les requêtes - vous n'aurez peut-être même pas besoin des mêmes index dans les deux bases de données (cela suppose que vous êtes sûr à 100% que vous obtiendrez la bonne élimination des tables). Vous pouvez même envisager d'utiliser des magasins de colonnes pour ArchiveDb.

Thomas Kejser
la source
-1

La façon dont nous l'avons fait est d'écrire les anciennes données par lots dans une base de données nouvellement créée et de supprimer les anciennes données de la base de données en direct. De cette façon, les deux bases de données sont accessibles. Vous pouvez également sauvegarder la base de données nouvellement créée et la restaurer ailleurs pour supprimer la grande empreinte des serveurs de production. J'espère que c'est une solution acceptable à vos besoins.

Pantalon Subhash
la source
L'OP doit aller plus loin que cela pour maintenir la compatibilité des applications. Avez-vous lu la question?
Jon Seigel