Conseils relatifs à la mise à l'échelle et à l'amélioration des temps d'exécution d'une & # 8220; requête basée sur un pivot & # 8221; sur une table de milliards de lignes, augmentant d'un million par jour

StackOverflow https://stackoverflow.com/questions/1002086

Question

Notre société développe un projet interne d’analyse de fichiers texte. Ces fichiers texte sont composés de métadonnées extraites à l'aide d'expressions régulières. Dix ordinateurs analysent les fichiers texte 24 heures sur 24, 7 jours sur 7 et alimentent une base de données haut de gamme Intel Xeon SQL Server 2005 avec les métadonnées extraites.

Le schéma de base de données simplifié se présente comme suit:

Items

| Id | Name   |
|----|--------|
| 1  | Sample |
Items_Attributes

| ItemId | AttributeId |
|--------|-------------|
| 1      | 1           |
| 1      | 2           |
Attributes

| Id | AttributeTypeId | Value |
|----|-----------------|-------|
| 1  | 1               | 500mB |
| 2  | 2               | 1.0.0 |
AttributeTypes

| Id | Name    |
|----|---------|
| 1  | Size    |
| 2  | Version |

Il existe de nombreux types de fichiers texte distincts contenant des métadonnées distinctes. Pour chaque fichier texte, nous avons un élément et pour chaque valeur de métadonnées extraites, un attribut .

Items_Attributes nous permettent d'éviter les valeurs d'attribut en double, ce qui évite d'augmenter la taille de la base de données de x ^ 10.

Ce schéma particulier nous permet d’ajouter de manière dynamique de nouvelles expressions régulières et d’obtenir de nouvelles métadonnées à partir de nouveaux fichiers traités, quelle que soit leur structure interne.

De plus, cela nous permet de filtrer les données et d’obtenir des rapports dynamiques basés sur les critères de l’utilisateur. Nous filtrons par Attribut , puis faisons pivoter le jeu de résultats ( http://msdn.microsoft.com/en-us/library/ms177410.aspx ). Donc, cet exemple de requête pseudo-SQL

SELECT FROM Items WHERE Size = @A AND Version = @B

renverrait un tableau pivotant comme celui-ci

| ItemName | Size  | Version |
|----------|-------|---------|
| Sample   | 500mB | 1.0.0   |

L'application est en cours d'exécution depuis des mois et les performances ont terriblement diminué, mais ce dernier n'est plus utilisable. Les rapports ne devraient pas prendre plus de 2 secondes et la table Items_Attributes augmente en moyenne de 10 000 000 lignes par semaine. Tout est correctement indexé et nous avons passé beaucoup de temps à analyser et à optimiser les plans d’exécution des requêtes.

Ma question est donc la suivante: comment adapteriez-vous cela afin de réduire les temps d'exécution des rapports?

Nous sommes venus avec ces solutions possibles:

  • Achetez plus de matériel et configurez un cluster SQL Server. (nous avons besoin de conseils sur la bonne stratégie de "regroupement")
  • Utilisez une base de données clé / valeur comme HBase (nous ne savons pas vraiment si cela résoudrait notre problème)
  • Utilisez un ODBMS plutôt qu'un SGBDR (nous avons envisagé db4o)
  • Déplacez notre logiciel vers le cloud (nous n’avons aucune expérience)
  • Générer de manière statique des rapports au moment de l'exécution. (nous ne voulons pas vraiment)
  • Vues indexées statiques pour les rapports courants (performances presque identiques)
  • Dénormaliser le schéma (certains de nos rapports impliquent jusqu'à 50 tables dans une seule requête)
Était-ce utile?

La solution

Peut-être que ce livre blanc rédigé par l'équipe SQL Server CAT sur les pièges du modèle de base de données Entity-Attribute-Value peut vous aider: http://sqlcat.com/whitepapers/archive/2008/09/03/best-practices- pour-sémantique-modélisation-de-données-pour-performance-et-scalability.aspx

Autres conseils

Je commencerais par poster les métadonnées exactes des tables (avec les détails d'indexation), le texte exact de la requête et le plan d'exécution.

Avec votre structure de table actuelle, la requête est semblable à ceci:

SELECT FROM Items WHERE Size = @A AND Version = @B

ne peut tirer parti de l'utilisation d'un index composite sur (Taille, Version) , car il est impossible de créer un tel index.

Vous ne pouvez même pas créer de vue indexée, car elle contiendrait une jointure automatique sur attributs .

La meilleure décision serait probablement de dénormaliser le tableau comme suit:

id  name  size  version

et créez un index sur (taille, version)

Travaillé avec de tels schémas beaucoup de temps. Ils ne fonctionnent jamais bien. La meilleure chose à faire est simplement de stocker les données selon vos besoins, sous la forme:

| ItemName | Taille | Version | | ---------- | ------- | --------- | | Échantillon | 500mB | 1.0.0 |

Ensuite, vous n’avez pas besoin de pivoter. Et BTW, veuillez ne pas appeler votre schéma EAV original "normalisé". - ce n'est pas normalisé.

Cela ressemble à émettre des requêtes OLAP sur une base de données optimisée pour les transactions OLTP. Ne connaissant pas les détails, je vous conseillerais de créer un & data; datawarehouse " séparé optimisé pour le type de requêtes que vous faites. Cela impliquerait d'agréger des données (si possible), de dénormaliser et également de disposer d'une base de données vieille d'un jour environ. Vous souhaitez mettre à jour les données de manière incrémentielle chaque jour ou à l’intervalle de votre choix.

Veuillez publier le DDL et les index exacts. Si vous avez des index sur les colonnes ID, votre requête donnera lieu à une analyse

au lieu de quelque chose comme ça

SELECT FROM Items WHERE Size = @A AND Version = @B

vous devez le faire

SELECT FROM Items WHERE ID = 1

En d'autres termes, vous devez récupérer les valeurs de texte, rechercher les identifiants sur lesquels vous indexez, puis les utiliser comme requête pour renvoyer les résultats à la place

Probablement aussi une bonne idée de regarder la fonction de partitionnement pour distribuer vos données

la mise en cluster est effectuée pour la disponibilité et non pour les performances, si un nœud meurt (le cluster actif), l'autre nœud (le cluster passif) devient actif ... Bien sûr, il existe également un clustering actif, mais c'est une autre histoire

Une solution à court terme peut être d’utiliser partitionnement horizontal . Je suppose que votre plus grand tableau est Items_Attributes . Vous pouvez partitionner cette table horizontalement, en plaçant chaque partition dans un groupe de fichiers distinct sur un contrôleur de disque distinct.

Cela suppose que vous n'essayez pas de générer des rapports sur tous les ItemId en même temps.

Vous mentionnez 50 tables dans une seule requête. Alors que SQL Server prend en charge jusqu'à 256 tables dans une même requête monolithique, cette approche réduit les chances que l'optimiseur produise un plan efficace.

Si vous êtes attaché au schéma actuel, envisagez de diviser vos requêtes de génération de rapports en une série d'étapes qui matérialisent leurs résultats dans des tables (#) temporaires. Cette approche vous permet d'exécuter les parties les plus sélectives de la requête de manière isolée et peut, selon mon expérience, offrir d'importants gains de performances. Les requêtes sont généralement plus faciles à gérer.

De plus, vous ne dites pas quelle version du serveur SQL vous utilisez; mais si vous utilisez SQL 2005, étant donné le nombre de tables impliquées dans vos rapports et le volume de données, il est utile de vérifier que votre serveur SQL est corrigé à au moins SP2.

J'ai travaillé sur un projet ETL en utilisant des tables contenant des centaines de millions de lignes, ce qui nous a permis de constater que l'optimiseur de requête de SQL 2005 RTM / SP1 ne pouvait pas produire de manière cohérente des plans efficaces pour des requêtes rejoignant plus de 5 tables dans lesquelles un ou plusieurs des les tables étaient de cette envergure. Ce problème a été résolu dans SP2.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top