web-dev-qa-db-fra.com

Faire des requêtes et un bcp plus vite sur une très grande table

Cette question est quelque peu continue pour cela: données énormes et performances dans SQL Server , mais suffisamment différente pour mériter sa propre question. Je vais copier et coller une partie du texte de cette question ici pour la lisibilité.

J'ai écrit une application avec un backend SQL Server qui recueille et stocke une quantité extrêmement importante d'enregistrements. J'ai calculé que, au maximum, le montant moyen des enregistrements est quelque part dans l'avenue de 3 à 4 milliards par jour (20 heures d'exploitation).

Ma candidature reçoit des données, le punks en lots d'~ 100k enregistrements et crée des fichiers de mise en scène de BCP, les déplace vers le serveur SQL et SQL Server utilise BCP pour déplacer les données dans une table de production. Avec cette solution, j'ai déplacé avec succès 5,2 milliards de lignes dans ma table de production avec peu ou aucun arriéré de fichiers BCP. J'ai BCP fonctionnant continuellement comme un travail d'agent SQL.

Ma table de production a le schéma suivant:

CREATE TABLE [dbo].[SignalValues](
    [Timestamp] [datetime] NOT NULL,
    [SignalId] [uniqueidentifier] NOT NULL,
    [SignalValue] [nvarchar](4000) NULL,
    [SignalValueId] [uniqueidentifier] NOT NULL
)

En raison du volume des enregistrements, la table est partitionnée sur [Timestamp] Toutes les demi-heures de 2016-01-20 21:00:00.000 Sur 2016-06-15 08:00:00.000. (~ 11K partitions totales).

Mon problème est double:

  1. Interroger la table de quelque manière que ce soit prend des eons.
  2. Alors que les requêtes fonctionnent contre la table, le traitement BCP s'arrête essentiellement. C'est comme si la puissance de traitement va à la requête et je reçois un arriéré de fichiers.

Comment accélérer la performance de la requête? Les utilisateurs vont surtout interroger en fonction de [Timestamp] Et SignalId. J'ai créé un index non clusterné sur [Timestamp] Mais cela ne semblait pas accélérer beaucoup de choses. Semble être que plus d'indexation que je postule, plus les inserts BCP sont plus lents qui crée un arriéré de fichier. Toute suggestions d'index?

Mon hypothèse des partitions est que, en interne, ils agissent comme des tables séparées, donc si je voudrais interroger [Timestamp] Pour 2016-01-25 22:01:00.000, Il ne doit verrouiller que la partition qu'il a besoin pour la requête et laisser les autres partitions déverrouillées . Mon hypothèse est-elle incorrecte? Cela semblerait si avec le comportement vu dans 2) ci-dessus. Comment puis-je forcer SQL Server à ne pas verrouiller pendant une requête SELECT (WITH (READPAST) et WITH (NOLOCK) n'a pas aidé)?

Je dois noter que les lignes de cette table ne seront jamais mises à jour ou supprimées. De plus, il est possible que les données puissent être insérées à partir d'une heure dans le passé, en fonction des applications fournissant mon application avec des données définies comme horodatage pour leurs lignes. Cela malheureusement nie toute possibilité de commutation de partition à moins que je fasse une magie folle.

Le nombre moyen de lignes par partition est de ~ 70 millions.

Sur la base des commentaires de ma question précédente (ci-dessus), j'ai été en mesure de renforcer considérablement mon serveur. Voici les informations système de mon serveur:

Info

En outre, j'ai configuré SQL Server comme suit en fonction des commentaires:

  • Tous les fichiers associés à la base de données sont connectés à un SAN qui est connecté au système via Fibre Channel
  • Le fichier de données de ma base de données est sur son propre entraînement seul - une matrice RAID 10 de 4 disques de filage
  • tempdb comprend 6 fichiers (la moitié du nombre de processeurs logiques), chacune de la taille de 512 Mo, tout sur un lecteur - une matrice RAID 0 de 2 disques de filage
  • Toutes les journaux de transaction vivent sur leur propre entraînement - un tableau RAID 1 de 2 disques de filage.

Le tableau n'a pas d'index en cluster. J'avais eu un index unique en cluster (le PK) avant, mais la contrainte d'unicité a provoqué des inserts de prendre 10 minutes pour 100 000 enregistrements dans une table avec des rangées de 800 m. Ma compréhension était qu'un index en cluster crée une contrainte d'unicité en interne par la rubrique "Pouvez-vous créer un index en cluster sur une colonne avec des valeurs en double?" De cette page:

14 questions d'indexation du serveur SQL que vous avez été trop timide pour demander

Je pourrais être complètement faux cependant.

Faites-moi savoir si je manque quelque chose.

4
Brandon

Ceci est un coup long mais vu comme vous exécutez SQL 2014 Enterprise, j'aurais un test avec l'utilisation d'un indice de colonne en cluster sur cette table et voyez si cela améliore la performance pour vous.

Considérant particulièrement que vous utilisez la table pour les sélectionneurs et les inserts en vrac uniquement - pas de mises à jour et pas de suppression.

Vous aurez l'avantage supplémentaire de prendre un peu de chargement des disques de filage et de le déplacer davantage vers la CPU/mémoire que vous semblez avoir une quantité décente de.

1
Lawrage

Va être difficile à trouver quelque chose qui excelle à la fois à la sauvegarde de données (accepter + 50k lignes par seconde) et Interrogation ad-hoc Série de temps EAV arbitraire (timestmap, signal_id, signal_value). Je donnerais un essai de colonne en regroupement. Columnstore en regroupement Effetez élimination du segment sur timestamp _ et colonnes en cluster ont également de meilleures caractéristiques de concurrence pour insert en vrac. Assurez-vous de lire SQL Server Columnstore Columnstore Tuple Mover et vous obtenez un taux de BCP suffisamment élevé pour créer des segments directement compressés. Sinon, la mise en scène, le BCP + reconstruit dans la mise en scène puis un commutateur de partition est un doit .

Avec la mise en page que vous avez, j'aurais beaucoup préféré avoir une index en cluster sur timestamp. Comme maintenant, votre meilleur espoir est de tirer parti de : élimination de la partition . Il y a beaucoup de sens que cela peut se rendre à ( pourquoi ne pas partitionner l'élimination du travail? ) et même quand il fonctionne, cela entraîne toujours des problèmes de concurrence. Spécialement, depuis la plupart des temps, les requêtes de l'utilisateur concernent des données récentes , cela conduit à bloquer contre les inserts BCP.

Je m'assurerais que mon BPC n'allage pas à la table des serrures X. le Guide de performance de chargement de données est une lecture obligatoire, mais sachez que le livre blanc est plutôt obsolète en ce qui concerne les colonnes, ne s'applique que la structure actuelle. Portez une attention particulière à la "Chargement en vrac Chargement d'une table partitionnée".

Je considérerais une granularité de partitionnement beaucoup plus grossière, peut-être une partition par jour. Avoir trop de partitions apporte un coût d'exécution significatif.

Et, comme pour tout problème de performance, mesure .

Je ne serais pas timide pour envisager des approches différentes radicales. Cassandra Par exemple, des données d'introduction à un taux très élevé et présentent l'avantage d'un "lancer plus de matériel au problème". La requête n'est pas la forte de Cassandra, mais une fois que vous achèterez dans cet écosystème, il y a une étincelle + la ruche + orc/parquet et ceux-ci peuvent interroger des données assez rapides.

1
Remus Rusanu
  • Interroger la table de quelque manière que ce soit prend des eons.

  • Alors que les requêtes fonctionnent contre la table, le traitement BCP s'arrête essentiellement. C'est comme tout pouvoir de traitement va à la requête et je reçois un arriéré de fichiers.

  • Comment accélérer la performance de la requête?

  • Je dois noter que les lignes de cette table ne seront jamais mises à jour ou supprimées. En outre, il est possible que les données puissent être insérées à partir d'une heure dans le passé

Tout d'abord, essayez quelques combinaisons d'index en cluster - même un indice en cluster non unique.

Deuxièmement, vous avez affaire à une énorme quantité et à un taux de données - si vous n'avez certainement pas besoin et que vous n'ayez pas besoin de Nvarchar, changez-vous à Varchar, et économisez beaucoup d'espace et de temps.

Troisièmement, si vos données de la table principale ne sont jamais mises à jour et que jamais supprimé, et si vous ne vous souciez pas si vous obtenez le contenu d'un fichier entier dans une heure ou non (comme votre chunking indique), alors envisagez de déplacer toutes les requêtes à lire Non engagé/avec (Nolock), puisque vous n'aurez aucune déclaration de mise à jour de toute façon.

Quatrièmement, considérons chaque charge de fichier BCP créant et remplissant une nouvelle table de mise en scène; Obtenez BCP loin de votre table principale. Vous pouvez même les diffuser sur différents ensembles de disques physiques plus tard pour exécuter plusieurs opérations de BCP en parallèle, puis. Vous aurez besoin de mettre dans un signal "Je suis prêt" sur ces tables de transfert, que ce soit par sp_rename après le FAère du BCP, ou une table de contrôle/journalisation (démarré sur la table X102 du fichier UYI, Tableau terminé X102, X102 est déplacé vers Main, X102 abandonné, etc.).

ETA: Cinquièmement, on dirait que vous avez des ressources disponibles - Ajout RAM était super. Assurez-vous que TEMPDB est sur différents disques que les tables principales et assurez-vous que les tables de mise en scène sont sur leur propres disques indépendants également. Peut-être aussi mettre des disques de l'état solides pour TEMPDB et ces tables de mise en scène BCP, qui devraient limiter la dépense. Si vous avez le budget, tout l'état solide, bien sûr. Notez que pour au moins Tempdb, vous Peut - et devrait - utiliser le SSD local, pas SAN SSD - Il est plus rapide pour la même charge, et il n'est pas nécessaire de répliquer TEMPDB dans la plupart des cas.

0
Anti-weakpasswords