Comment gérer 3,1 milliards de lignes de données?

14

Je suis actuellement chargé d'implémenter un schéma de stockage pour une quantité relativement importante de données. Les données seront principalement accessibles pour déterminer une data pointvaleur actuelle , mais je suis également tenu de suivre les six derniers mois de l'historique des tendances / analyses des données.

Une exigence récente a été ajoutée pour suivre la valeur min/ max/ sumpour la dernière heure.

REMARQUE: Idéalement, je voudrais envisager une option MongoDB, mais je dois d'abord démontrer que j'ai épuisé les options SQL-Server.

Les données

Le tableau suivant représente la source de données principale (interrogée le plus fréquemment). Le tableau comportera environ cinq millions de lignes. Les modifications de données seront principalement des UPDATEinstructions avec des instructions très occasionnelles INSERTaprès le chargement initial des données. J'ai choisi de regrouper les données dataPointIdcar vous serez toujours en train de sélectionner all values for a given data point.

// Simplified Table
CREATE TABLE [dbo].[DataPointValue](
    [dataPointId]  [int] NOT NULL,
    [valueId]      [int] NOT NULL,
    [timestamp]    [datetime] NOT NULL,
    [minimum]      [decimal](18, 0) NOT NULL,
    [hourMinimum]  [decimal](18, 0) NOT NULL,
    [current]      [decimal](18, 0) NOT NULL,
    [currentTrend] [decimal](18, 0) NOT NULL,
    [hourMaximum]  [decimal](18, 0) NOT NULL,
    [maximum]      [decimal](18, 0) NOT NULL

    CONSTRAINT [PK_MeterDataPointValue] PRIMARY KEY CLUSTERED ([dataPointId],[valueId])
)

Le deuxième tableau est sensiblement plus grand, avec environ 3,1 milliards de lignes (représentant les six derniers mois de données). Les données de plus de six mois seront purgées; sinon, strictement des INSERTdéclarations de données (~ 200 lignes / sec, 720 000 lignes / heure, 17 millions de lignes / semaine).

// Simplified Table
CREATE TABLE [dbo].[DataPointValueHistory](
    [dataPointId] [int]            NOT NULL,
    [valueId]     [int]            NOT NULL,
    [timestamp]   [datetime]       NOT NULL,
    [value]       [decimal](18, 0) NOT NULL,
    [delta]       [decimal](18, 0) NOT NULL

    CONSTRAINT [PK_MeterDataPointHistory] PRIMARY KEY CLUSTERED ([dataPointId], [valueId], [timestamp])

)

On s'attend à ce que ce tableau double de taille à mesure que le nombre de valeurs de points de données suivies augmente à 400 lignes / s (donc atteindre ~ 10 milliards n'est pas hors de question).

La ou les questions ( oui, j'en pose plus d'une ... elles sont étroitement liées).

J'utilise actuellement une base de données SQL-Server 2008 R2 Standard Edition. Je vais probablement plaider en faveur de la mise à niveau vers Enterprise Edition si je peux obtenir le niveau de performance souhaité avec les partitions de table (ou MongoDB s'il ne peut pas atteindre les niveaux de performance requis avec SQL-Server). J'aimerais avoir votre avis sur les points suivants:


1) Étant donné que je dois calculer le min, maxet sumpour la dernière heure (comme dans now - 60 minutes). Quelle est la meilleure approche pour suivre les données récentes:

  • Conserver les données récentes en mémoire du service de données. Écrivez la valeur min / max / moyenne calculée avec chaque MISE À JOUR des données.

  • Recherchez l'historique récent de la table d'historique (impact sur la question suivante?) Lors de chaque instruction UPDATE. La requête accéderait aux dernières données pour une valeur de point de données et ne devrait balayer que le dernier million d'enregistrements ou plus?

  • Stocker l'historique récent dans la ligne DataPointValue elle-même pour éviter la recherche dans la table d'historique? Peut-être stocké sous forme de chaîne délimitée et traité dans le processus UPDATE?

  • Autre option que je n'ai pas envisagée?


2) Pour DataPointValueHistory, les requêtes sur la datable seront toujours par dataPointIdet un ou plusieurs valueId. Les données interrogées seront généralement pour le dernier jour, la semaine ou le mois, mais peuvent être pour les six mois complets dans certains cas.

Je suis en train de générer un exemple de jeu de données pour tester s'il est plus judicieux de regrouper par dataPointId / valueId / timeStamp ou timeStamp / dataPointId / valueId. Si quelqu'un a de l'expérience avec une table de cette taille et souhaite offrir son point de vue, ce serait apprécié. Je penche vers cette dernière option pour éviter la fragmentation de l'index, mais les performances des requêtes sont essentielles.

  • Cluster DataPointValueHistorypar dataPointId -> valueId -> timeStamp

  • Cluster DataPointValueHistorypar timeStamp -> dataPointId -> valueId


3) Enfin, comme mentionné ci-dessus, je pense qu'il sera judicieux de partitionner la DataPointValueHistorytable. Toute suggestion sur la meilleure façon de partitionner les données d'historique serait grandement appréciée.

  • Si groupé par horodatage d'abord, je pense que les données devraient être partitionnées par semaine (27 partitions au total). La partition la plus ancienne serait purgée après la semaine 27.

  • Si clustered par dataPointId d'abord, je pense que les données devraient être partitionnées par un module de l'id?

Comme j'ai une expérience très limitée avec le partitionnement de table, votre expertise serait appréciée.

Calgary Coder
la source
Avez-vous supprimé la version de cette question sur StackOverflow?
Taryn
@bluefeet - Oui, il a été signalé comme hors sujet ... j'ai donc supprimé la question SO et recréé ici (j'aurais probablement dû attendre sa migration).
Calgary Coder
Pas de problème, je m'assurais juste que nous n'avions pas de questions croisées.
Taryn
Sur Standard Edition, vous pouvez toujours partitionner les données à l'aide de vues partitionnées et de plusieurs tables de base. Je ne sais pas si vous y avez pensé.
Jon Seigel
@ Jon - Oui, j'ai envisagé des partitions de table manuelles (ce choix particulier sera basé sur la disponibilité ou non d'une licence Enterprise ... si oui, pourquoi avoir mon propre rôle).
Calgary Coder

Réponses: