Datenbankdesign für die Verarbeitung von 1 Milliarde Zeilen und das Zählen

10

Wir erhalten Echtzeit-GPS-Daten mit einer Rate von ca. 5000 PR. Minute (von 4 TCP-Servern). Jeder Server verwendet eine einzelne Verbindung zum Einfügen der Daten und puffert Daten zwischen Einfügungen. Etwa alle 15 Minuten ruft ein Dienst diese Daten ab und verarbeitet sie zu Fahrten. Sobald die Fahrten generiert wurden, sind die tatsächlichen GPS-Daten normalerweise nicht so wichtig, nur wenn der Benutzer die Route auf einer Karte sehen möchte.

Das Problem ist, dass die Datenbank anscheinend Probleme hat, mit der Dateneinfügungsrate Schritt zu halten. Manchmal, wenn die Last zunimmt, steigt die Einfügezeit plötzlich drastisch an (> 30 Sekunden), wodurch wiederum mehr Daten gepuffert werden können, was wiederum zu größeren Einfügungen und einer längeren Einfügedauer führt.

Ich hoffe, einige Kommentare zum aktuellen Design und einige der Ideen zu erhalten, die wir zur Verbesserung der Leistung benötigen, sowie Antworten auf einige unserer Fragen - und alle anderen Tipps, die Menschen möglicherweise haben!

Aktuelles Design

Die Daten sind derzeit in Tabellen unterteilt, die eine Woche darstellen, und Daten, die älter als ein Jahr sind, werden in einer sekundären Datenbank archiviert. Das Ganze wird in einer bearbeitbaren Ansicht zusammengefügt, die sowohl für Einfügungen als auch für Lesevorgänge verwendet wird.

Tischgestaltung

  • ID (PK, eindeutige Kennung)
  • DeviceId (FK, int)
  • PersonId (FK, int)
  • VehicleId (FK, int)
  • TokenId (FK, int)
  • UtcTime (PK, datetime2 (3))
  • Breitengrad (float)
  • Längengrad (float)
  • Geschwindigkeit (smallint)
  • Überschrift (smallint)
  • Satelliten (tinyint)
  • IOData (varbinary (100))
  • IgnitionState (tinyint)
  • UserInput (tinyint)
  • CreateTimeUtc (datetime2 (3))

Indizes

  • DeviceId_CreateTimeUtc_Desc
  • DeviceId_UtcTime_Desc (Clustered)
  • PersonId_UtcTime_Desc
  • TokenId_UtcTime_Desc
  • VehicleId_UtcTime_Desc

Derzeit nimmt jede Woche etwa 10 GB einschließlich Indizes ein, und derzeit befinden sich etwa 300 GB Daten in der Hauptdatenbank.

Die Datentabellen in der Hauptdatenbank haben eine eigene Dateigruppe mit 1 Datei, befinden sich jedoch auf derselben Festplatte wie alle anderen Tabellen in der Hauptdatenbank. Die sekundäre Datenbank befindet sich auf einer anderen Festplatte, jedoch auf demselben Computer.

Ich denke, wir führen auch wöchentlich einen Indexwiederherstellungsjob aus, wenn eine neue Tabellenpartition (Woche) verwendet wird. Es wird kein Schrumpfen durchgeführt.

Der Computer ist ein 8-Kern-HP mit 12 GB Speicher, und auf der Festplatte mit der Hauptdatenbank wird RAID 10 ausgeführt.

Ideen

  • Begrenzen Sie die in der Primärdatenbank gespeicherte Datenmenge auf maximal 1 Monat. Zumindest würde dies die Verwaltung der Datenbank für die Sicherung / Wiederherstellung erleichtern, aber könnten wir damit eine Leistungsverbesserung erwarten?
  • Erstellen Sie 2 Dateien in der Dateigruppe für aktuelle Daten und verteilen Sie sie auf 2 verschiedene physische Partitionen
  • Erstellen Sie Master-Slave-Datenbanken mit aktuellen Daten, sodass Einfügungen und Lesevorgänge für verschiedene Datenbanken ausgeführt werden
  • Legen Sie Dateien für aktuelle Daten auf SSD-Festplatten ab (würde die Spiegelung bei SSD-Festplatten einen Leistungsunterschied bewirken?)

Bitte lassen Sie mich wissen, wenn weitere Informationen benötigt werden. Es gibt schrecklich viele Faktoren, die die Leistung beeinflussen, und wahrscheinlich ebenso viele Möglichkeiten, sie zu optimieren.

Sondergard
quelle
Kommentare sind nicht für eine ausführliche Diskussion gedacht. Dieses Gespräch wurde in den Chat verschoben .
Paul White 9

Antworten:

8

5000 Einsätze pro Minute sind ungefähr 83 Einsätze pro Sekunde. Mit 5 Indizes werden 400 physische Zeilen pro Sekunde eingefügt. Wenn die Arbeitslast im Arbeitsspeicher wäre, wäre dies selbst für den kleinsten Server kein Problem. Selbst wenn dies eine zeilenweise Einfügung auf die ineffizienteste Art und Weise war, die ich mir vorstellen kann. 83 triviale Abfragen pro Sekunde sind aus CPU-Sicht einfach nicht interessant.

Wahrscheinlich sind Sie festplattengebunden. Sie können dies überprüfen, indem Sie sich die Wartestatistiken oder ansehen STATISTICS IO.

Ihre Abfragen berühren wahrscheinlich viele verschiedene Seiten, sodass der Pufferpool nicht für alle Seiten Platz bietet. Dies führt zu häufigen Seitenlesevorgängen und wahrscheinlich auch zu zufälligen Schreibvorgängen auf der Festplatte.

Stellen Sie sich eine Tabelle vor, in die Sie aufgrund eines immer größer werdenden Schlüssels nur am Ende physisch einfügen. Der Arbeitssatz wäre eine Seite: die letzte. Dies würde auch sequentielle E / A erzeugen, wenn der Lazy Writer oder Checkpoint-Prozess das "Ende" der Tabelle auf die Festplatte schreibt.

Stellen Sie sich eine Tabelle mit zufällig platzierten Einfügungen vor (klassisches Beispiel: ein Guid-Schlüssel). Hier sind alle Seiten der Arbeitssatz, da für jede Einfügung eine zufällige Seite berührt wird. IOs sind zufällig. Dies ist der schlimmste Fall, wenn es um Arbeitssätze geht.

Du bist in der Mitte. Ihre Indizes sind von der Struktur (SomeValue, SequentialDateTime). Die erste Komponente randomisiert teilweise die von der zweiten bereitgestellte Sequenzialität. Ich denke, es gibt einige mögliche Werte für " SomeValue", so dass Sie viele zufällig platzierte Einfügepunkte in Ihren Indizes haben.

Sie sagen, dass die Daten in 10-GB-Tabellen pro Woche aufgeteilt werden. Dies ist ein guter Ausgangspunkt, da der Arbeitssatz jetzt auf 10 GB begrenzt ist (ohne Berücksichtigung etwaiger Lesevorgänge). Bei 12 GB Serverspeicher ist es jedoch unwahrscheinlich, dass alle relevanten Seiten im Speicher verbleiben.

Wenn Sie die Größe der wöchentlichen "Partitionen" reduzieren oder den Serverspeicher ein wenig erhöhen könnten, ist dies wahrscheinlich in Ordnung.

Ich würde erwarten, dass die Einsätze zu Beginn der Woche schneller sind als am Ende. Sie können diese Theorie auf einem Entwicklungsserver testen, indem Sie einen Benchmark mit einer bestimmten Datengröße ausführen und den Serverspeicher schrittweise reduzieren, bis Sie den Leistungstank sehen.

Selbst wenn alle Lese- und Schreibvorgänge in den Speicher passen, kann es dennoch zu zufälligen E-Mails zum Löschen schmutziger Seiten kommen. Die einzige Möglichkeit, dies zu beseitigen, besteht darin, an Positionen in Ihren Indizes zu schreiben. Wenn Sie Ihre Indizes überhaupt konvertieren können, um (mehr) sequentielle Schlüssel zu verwenden, würde dies sehr hilfreich sein.

Als schnelle Lösung würde ich eine Pufferschicht zwischen den Clients und der Haupttabelle hinzufügen. Sammeln Sie möglicherweise 15 Minuten Schreibvorgänge in einer Staging-Tabelle und leeren Sie sie regelmäßig. Das nimmt die Lastspitzen weg und verwendet einen effizienteren Plan, um auf den großen Tisch zu schreiben.

usr
quelle
1
@usr Danke für die sehr umfassende und gut erläuterte Antwort! Wir haben tatsächlich über die Erhöhung des Serverspeichers gesprochen, ohne zu wissen, wie stark sich dies auswirken würde - aber jetzt haben wir wirklich einen sehr überzeugenden Grund dafür :) Sie haben Recht, dass der "SomeValue" die Einfügepunkte teilweise randomisiert - es gibt wahrscheinlich rund 10000 Geräte-IDs. Ist Ihr Vorschlag in Bezug auf die Staging-Tabelle eine Tabelle ohne Indizes und dann ein Job, der alle X Minuten in die Haupttabelle eingefügt werden muss?
Sondergard
@usr Reg. Wenn Sie vorschlagen, den Clustered-Index sequentiell zu konvertieren, können Sie eine automatische Inkl. hinzufügen. Identitätsspalte (Ganzzahl), und ändern Sie den Clustered-Index in diese Spalte, um sie nur sequentiell zu halten. Es wäre nicht tabellenübergreifend eindeutig, aber solange der Primärschlüssel ist, sollte es uns gut gehen.
Sondergard
1
Wenn die Staging-Tabelle klein ist und Ihre Abfragen damit leben können, müssen Sie überhaupt nicht indizieren. Aber du könntest.; Eine Strategie wäre, das CI in einer Identitätsspalte zu erstellen (wie Sie sagen). Dies kann Wunder wirken, wenn das CI groß und die anderen Indizes klein sind. Da die CI-Schreibvorgänge jetzt sequentiell sind, tragen sie viel weniger zu Ihrem Problem bei. Diese Strategie ist am erfolgreichsten, wenn es einen bedeutenden Größenunterschied gibt.; Eine andere Idee wäre, einen Tisch pro Tag zu haben. Vielleicht monatlich zusammenführen.
usr
Ok, wir haben uns mit der Erstellung einer Identitätsspalte für CI befasst, aber in einer partitionierten Ansicht ist dies leider nicht möglich (keine Identitätsspalte zulässig, keine Standardwerte und alle Spalten müssen in der Einfügung enthalten sein). Vielleicht war die geteilte Ansicht ein schlecht gewähltes Design, obwohl es von einem Berater empfohlen wurde
sondergard
2
Im Ernst, wenn Sie mit dem gleichen Problem konfrontiert sind und viele Schreibvorgänge und nur wenige Lesevorgänge haben, möchten Sie am Ende wirklich anhängen und die Indizierung verzögern. Wenn Sie jedoch schnell lesen möchten und es Ihnen egal ist, wie lange das Einfügen dauert, benötigen Sie einen Clustered-Index.
Tiktak