Nous recevons des données GPS en temps réel à un taux d'environ 5000 pr. minute (à partir de 4 serveurs TCP). Chaque serveur utilise une connexion unique pour insérer les données et met en mémoire tampon les données entre les insertions. Toutes les 15 minutes environ, un service récupère ces données et les transforme en trajets. Une fois que les trajets ont été générés, les données GPS réelles ne sont généralement pas si importantes, uniquement si l'utilisateur souhaite voir l'itinéraire sur une carte.
Le problème est qu'il semble que la base de données ait du mal à suivre le rythme des données insérées. Parfois, lorsque la charge augmente, le temps d'insertion augmente soudainement de façon drastique (> 30 secondes), ce qui permet à son tour de stocker davantage de données, ce qui entraîne à son tour des insertions plus grandes et une durée d'insertion plus longue.
J'espère avoir quelques commentaires sur la conception actuelle, et certaines des idées que nous avons pour améliorer les performances, et des réponses à certaines de nos questions - et tout autre conseil que les gens pourraient avoir!
Conception actuelle
Les données sont actuellement séparées en tableaux représentant une semaine et les données datant de plus d'un an sont archivées dans une base de données secondaire. Le tout est réuni dans une vue modifiable, qui est utilisée à la fois pour les insertions et les lectures.
Conception de table
- Id (PK, uniqueidentifier)
- DeviceId (FK, int)
- PersonId (FK, int)
- VehicleId (FK, int)
- TokenId (FK, int)
- UtcTime (PK, datetime2 (3))
- Latitude (flottant)
- Longitude (flotteur)
- Vitesse (smallint)
- En-tête (smallint)
- Satellites (tinyint)
- IOData (varbinaire (100))
- IgnitionState (tinyint)
- UserInput (tinyint)
- CreateTimeUtc (datetime2 (3))
Indices
- DeviceId_CreateTimeUtc_Desc
- DeviceId_UtcTime_Desc (Clustered)
- PersonId_UtcTime_Desc
- TokenId_UtcTime_Desc
- VehicleId_UtcTime_Desc
Chaque semaine occupe actuellement environ 10 Go, y compris les indices, et il y a actuellement environ 300 Go de données dans la base de données principale.
Les tables de données de la base de données principale ont leur propre groupe de fichiers avec 1 fichier, mais il se trouve sur le même disque que toutes les autres tables de la base de données principale. La base de données secondaire se trouve sur un disque différent, mais sur la même machine.
Je pense que nous exécutons également un travail de reconstruction d'index chaque semaine, lorsqu'une nouvelle partition de table (semaine) est utilisée. Aucun rétrécissement n'est effectué.
La machine est un HP à 8 cœurs avec 12 Go de mémoire et le disque contenant la base de données principale exécute RAID 10.
Des idées
- Limitez la quantité de données stockées dans la base de données primaire à par exemple 1 mois maximum. À tout le moins, cela rendrait la base de données plus gérable pour la sauvegarde / restauration, mais pourrions-nous nous attendre à une amélioration des performances en faisant cela?
- Créez 2 fichiers dans un groupe de fichiers pour les données actuelles et distribuez-les sur 2 partitions physiques différentes
- Créez des bases de données maître-esclave contenant les données actuelles, afin que les insertions et les lectures soient effectuées sur différentes bases de données
- Placez les fichiers pour les données actuelles sur les disques SSD (la mise en miroir ferait-elle une différence de performances avec les disques SSD?)
Veuillez me faire savoir si plus d'informations sont nécessaires. Il existe horriblement de nombreux facteurs qui influencent les performances, et probablement autant de façons de les modifier.
la source
Réponses:
5000 inserts par minute sont environ 83 inserts par seconde. Avec 5 index c'est 400 lignes physiques insérées par seconde. Si la charge de travail était en mémoire, cela ne poserait pas de problème, même au plus petit des serveurs. Même s'il s'agissait d'un encart ligne par ligne utilisant la manière la plus inefficace que je puisse imaginer. 83 requêtes triviales par seconde ne sont tout simplement pas intéressantes du point de vue du processeur.
Vous êtes probablement lié au disque. Vous pouvez le vérifier en consultant les statistiques d'attente ou
STATISTICS IO
.Vos requêtes touchent probablement un grand nombre de pages différentes afin que le pool de tampons n'ait pas d'espace pour toutes. Cela provoque des lectures de pages fréquentes et des écritures sur disque probablement aléatoires également.
Imaginez une table où vous n'insérez physiquement qu'à la fin en raison d'une clé toujours croissante. L'ensemble de travail serait une page: la dernière. Cela générerait des E / S séquentielles aussi bien lorsque l'écrivain paresseux ou le processus de point de contrôle écrit la «fin» de la table sur le disque.
Imaginez une table avec des insertions placées au hasard (exemple classique: une clé guid). Ici, toutes les pages sont le jeu de travail car une page aléatoire sera touchée pour chaque insert. Les entrées-sorties sont aléatoires. C'est le pire des cas quand il s'agit de travailler ensemble.
Tu es au milieu. Vos index sont de la structure
(SomeValue, SequentialDateTime)
. Le premier composant randomise partiellement la séquentialité fournie par le second. Je suppose qu'il y a pas mal de valeurs possibles pour "SomeValue
" afin que vous ayez de nombreux points d'insertion placés au hasard dans vos index.Vous dites que les données sont divisées en tables de 10 Go par semaine. C'est un bon point de départ car l'ensemble de travail est désormais limité à 10 Go (sans tenir compte des lectures que vous pourriez faire). Avec 12 Go de mémoire serveur, il est peu probable, cependant, que toutes les pages pertinentes puissent rester en mémoire.
Si vous pouviez réduire la taille des "partitions" hebdomadaires ou augmenter un peu la mémoire du serveur, tout va bien.
Je m'attendrais à ce que les insertions au début de la semaine soient plus rapides qu'à la fin. Vous pouvez tester cette théorie sur un serveur de développement en exécutant un benchmark avec une certaine taille de données et en réduisant progressivement la mémoire du serveur jusqu'à ce que vous voyiez un réservoir de performances.
Maintenant, même si toutes les lectures et écritures tiennent dans la mémoire, vous pouvez toujours avoir des E / S de vidage de page sale aléatoires. La seule façon de s'en débarrasser est d'écrire dans des positions colocalisées dans vos index. Si vous pouvez convertir vos index pour utiliser (plus) de clés séquentielles, cela aiderait beaucoup.
Comme solution rapide, j'ajouterais une couche tampon entre les clients et la table principale. Peut-être accumuler 15 minutes d'écritures dans une table intermédiaire et la vider périodiquement. Cela supprime les pics de charge et utilise un plan plus efficace pour écrire sur la grande table.
la source