Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Ich schlage vor, dass Sie das Transkript des Berichts von Alexander Valyalkin Ende 2019 „Go-Optimierungen in VictoriaMetrics“ lesen.

VictoriaMetrics — ein schnelles und skalierbares DBMS zum Speichern und Verarbeiten von Daten in Form einer Zeitreihe (der Datensatz bildet die Zeit und einen dieser Zeit entsprechenden Satz von Werten, die beispielsweise durch periodische Abfrage des Status von Sensoren oder Sammlung von Daten erhalten werden). Metriken).

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Hier ist ein Link zum Video dieses Berichts - https://youtu.be/MZ5P21j_HLE

Folien

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Erzählen Sie uns etwas über sich. Ich bin Alexander Valyalkin. Hier mein GitHub-Konto. Ich interessiere mich leidenschaftlich für Go und Leistungsoptimierung. Ich habe viele nützliche und weniger nützliche Bibliotheken geschrieben. Sie beginnen mit beidem fast, oder mit quick Präfix.

Ich arbeite derzeit an VictoriaMetrics. Was ist das und was mache ich dort? Darüber werde ich in dieser Präsentation sprechen.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Die Gliederung des Berichts lautet wie folgt:

  • Zuerst erzähle ich Ihnen, was VictoriaMetrics ist.
  • Dann erkläre ich Ihnen, was Zeitreihen sind.
  • Dann erkläre ich Ihnen, wie eine Zeitreihendatenbank funktioniert.
  • Als nächstes erzähle ich Ihnen etwas über die Datenbankarchitektur: woraus sie besteht.
  • Und dann kommen wir zu den Optimierungen, die VictoriaMetrics bietet. Dies ist eine Optimierung für den invertierten Index und eine Optimierung für die Bitset-Implementierung in Go.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Weiß jemand im Publikum, was VictoriaMetrics ist? Wow, viele wissen es schon. Das ist eine gute Nachricht. Für diejenigen, die es nicht wissen: Dies ist eine Zeitreihendatenbank. Es basiert auf der ClickHouse-Architektur und einigen Details der ClickHouse-Implementierung. Zum Beispiel auf: MergeTree, parallele Berechnung auf allen verfügbaren Prozessorkernen und Leistungsoptimierung durch Arbeiten an Datenblöcken, die im Prozessor-Cache abgelegt werden.

VictoriaMetrics bietet eine bessere Datenkomprimierung als andere Zeitreihendatenbanken.

Es skaliert vertikal – das heißt, Sie können mehr Prozessoren und mehr RAM auf einem Computer hinzufügen. VictoriaMetrics wird diese verfügbaren Ressourcen erfolgreich nutzen und die lineare Produktivität verbessern.

VictoriaMetrics skaliert auch horizontal – das heißt, Sie können dem VictoriaMetrics-Cluster zusätzliche Knoten hinzufügen und die Leistung steigt nahezu linear.

Wie Sie vermutet haben, ist VictoriaMetrics eine schnelle Datenbank, da ich andere nicht schreiben kann. Und es ist in Go geschrieben, also spreche ich bei diesem Treffen darüber.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Wer weiß, was eine Zeitreihe ist? Er kennt auch viele Leute. Eine Zeitreihe ist eine Reihe von Paaren (timestamp, значение), wobei diese Paare nach Zeit sortiert sind. Der Wert ist eine Gleitkommazahl – float64.

Jede Zeitreihe wird durch einen Schlüssel eindeutig identifiziert. Woraus besteht dieser Schlüssel? Es besteht aus einer nicht leeren Menge von Schlüssel-Wert-Paaren.

Hier ist ein Beispiel für eine Zeitreihe. Der Schlüssel dieser Serie ist eine Liste von Paaren: __name__="cpu_usage" ist der Name der Metrik, instance="my-server" - Dies ist der Computer, auf dem diese Metrik erfasst wird. datacenter="us-east" - Dies ist das Rechenzentrum, in dem sich dieser Computer befindet.

Am Ende hatten wir einen Zeitreihennamen, der aus drei Schlüssel-Wert-Paaren bestand. Dieser Schlüssel entspricht einer Liste von Paaren (timestamp, value). t1, t3, t3, ..., tN - das sind Zeitstempel, 10, 20, 12, ..., 15 — die entsprechenden Werte. Dies ist die CPU-Auslastung zu einem bestimmten Zeitpunkt für eine bestimmte Zeile.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Wo können Zeitreihen verwendet werden? Hat jemand eine Idee?

  • In DevOps können Sie CPU, RAM, Netzwerk, RPS, Anzahl der Fehler usw. messen.
  • IoT – wir können Temperatur, Druck, Geokoordinaten und mehr messen.
  • Auch Finanzen – wir können die Preise für alle Arten von Aktien und Währungen überwachen.
  • Darüber hinaus können Zeitreihen zur Überwachung von Produktionsprozessen in Fabriken eingesetzt werden. Wir haben Benutzer, die VictoriaMetrics zur Überwachung von Windkraftanlagen für Roboter verwenden.
  • Zeitreihen eignen sich auch zum Sammeln von Informationen von Sensoren verschiedener Geräte. Zum Beispiel für einen Motor; zur Messung des Reifendrucks; zur Messung von Geschwindigkeit, Distanz; zur Messung des Benzinverbrauchs etc.
  • Zeitreihen können auch zur Überwachung von Flugzeugen verwendet werden. Jedes Flugzeug verfügt über eine Blackbox, die Zeitreihen für verschiedene Parameter des Flugzeugzustands sammelt. Zeitreihen werden auch in der Luft- und Raumfahrtindustrie verwendet.
  • Gesundheitsversorgung umfasst Blutdruck, Puls usw.

Möglicherweise gibt es noch weitere Anwendungen, die ich vergessen habe, aber ich hoffe, Sie verstehen, dass Zeitreihen in der modernen Welt aktiv genutzt werden. Und der Umfang ihrer Nutzung nimmt jedes Jahr zu.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Warum benötigen Sie eine Zeitreihendatenbank? Warum können Sie keine reguläre relationale Datenbank zum Speichern von Zeitreihen verwenden?

Denn Zeitreihen enthalten meist eine große Menge an Informationen, die sich in herkömmlichen Datenbanken nur schwer speichern und verarbeiten lassen. Daher entstanden spezielle Datenbanken für Zeitreihen. Diese Basen speichern effektiv Punkte (timestamp, value) mit dem angegebenen Schlüssel. Sie stellen eine API zum Lesen gespeicherter Daten nach Schlüssel, nach einem einzelnen Schlüssel-Wert-Paar oder nach mehreren Schlüssel-Wert-Paaren oder nach regulärem Ausdruck bereit. Wenn Sie beispielsweise die CPU-Auslastung aller Ihrer Dienste in einem Rechenzentrum in Amerika ermitteln möchten, müssen Sie diese Pseudoabfrage verwenden.

Typischerweise stellen Zeitreihendatenbanken spezielle Abfragesprachen bereit, da Zeitreihen-SQL nicht sehr gut geeignet ist. Obwohl es Datenbanken gibt, die SQL unterstützen, ist es nicht sehr geeignet. Abfragesprachen wie PromQL, ZustromQL, Fluss, Q. Ich hoffe, dass jemand mindestens eine dieser Sprachen gehört hat. Viele Leute haben wahrscheinlich schon von PromQL gehört. Dies ist die Prometheus-Abfragesprache.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

So sieht eine moderne Zeitreihen-Datenbankarchitektur am Beispiel von VictoriaMetrics aus.

Es besteht aus zwei Teilen. Dies ist der Speicher für den invertierten Index und der Speicher für Zeitreihenwerte. Diese Repositorys sind getrennt.

Wenn ein neuer Datensatz in der Datenbank eintrifft, greifen wir zunächst auf den invertierten Index zu, um die Zeitreihenkennung für einen bestimmten Satz zu finden label=value für eine bestimmte Metrik. Wir finden diese Kennung und speichern den Wert im Datenspeicher.

Wenn eine Anfrage zum Abrufen von Daten aus der TSDB eingeht, gehen wir zuerst zum invertierten Index. Lass uns alles holen timeseries_ids Datensätze, die diesem Satz entsprechen label=value. Und dann erhalten wir alle notwendigen Daten aus dem Data Warehouse, indiziert von timeseries_ids.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Schauen wir uns ein Beispiel an, wie eine Zeitreihendatenbank eine eingehende Auswahlabfrage verarbeitet.

  • Erstens bekommt sie alles timeseries_ids aus einem invertierten Index, der die angegebenen Paare enthält label=valueoder einen bestimmten regulären Ausdruck erfüllen.
  • Anschließend werden in einem bestimmten Zeitintervall alle gefundenen Datenpunkte aus dem Datenspeicher abgerufen timeseries_ids.
  • Anschließend führt die Datenbank je nach Wunsch des Benutzers einige Berechnungen an diesen Datenpunkten durch. Und danach gibt es die Antwort zurück.

In dieser Präsentation erzähle ich Ihnen vom ersten Teil. Dies ist eine Suche timeseries_ids durch invertierten Index. Den zweiten und dritten Teil können Sie sich später ansehen VictoriaMetrics-Quellen, oder warten Sie, bis ich andere Berichte vorbereite :)

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Kommen wir zum invertierten Index. Viele denken vielleicht, das sei einfach. Wer weiß, was ein invertierter Index ist und wie er funktioniert? Oh, nicht mehr so ​​viele Leute. Versuchen wir zu verstehen, was es ist.

Es ist eigentlich einfach. Es ist einfach ein Wörterbuch, das einen Schlüssel einem Wert zuordnet. Was ist ein Schlüssel? Dieses Paar label=valueWo label и value - Das sind Zeilen. Und die Werte sind eine Menge timeseries_ids, die das angegebene Paar enthält label=value.

Mit dem umgekehrten Index können Sie alles schnell finden timeseries_ids, die gegeben haben label=value.

Außerdem ermöglicht es Ihnen, schnell zu finden timeseries_ids Zeitreihe für mehrere Paare label=value, oder für Paare label=regexp. Wie kommt es dazu? Indem man den Schnittpunkt der Menge findet timeseries_ids für jedes Paar label=value.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Schauen wir uns verschiedene Implementierungen des invertierten Index an. Beginnen wir mit der einfachsten naiven Implementierung. Sie sieht so aus.

Funktion getMetricIDs Ruft eine Liste von Zeichenfolgen ab. Jede Zeile enthält label=value. Diese Funktion gibt eine Liste zurück metricIDs.

Wie es funktioniert? Hier haben wir eine globale Variable namens invertedIndex. Dies ist ein reguläres Wörterbuch (map), wodurch der String den Slice-Ints zugeordnet wird. Die Zeile enthält label=value.

Funktionsimplementierung: get metricIDs zum ersten label=value, dann gehen wir alles andere durch label=value, wir kapieren es metricIDs für Sie. Und rufen Sie die Funktion auf intersectInts, worauf weiter unten eingegangen wird. Und diese Funktion gibt den Schnittpunkt dieser Listen zurück.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Wie Sie sehen, ist die Implementierung eines invertierten Indexes nicht sehr kompliziert. Aber das ist eine naive Implementierung. Welche Nachteile hat es? Der Hauptnachteil der naiven Implementierung besteht darin, dass ein solcher invertierter Index im RAM gespeichert wird. Nach dem Neustart der Anwendung verlieren wir diesen Index. Dieser Index wird nicht auf der Festplatte gespeichert. Ein solcher invertierter Index dürfte für eine Datenbank nicht geeignet sein.

Der zweite Nachteil hängt auch mit dem Speicher zusammen. Der invertierte Index muss in den RAM passen. Wenn es die Größe des RAM überschreitet, erhalten wir offensichtlich einen Fehler wegen nicht genügend Speicher. Und das Programm wird nicht funktionieren.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Dieses Problem kann mit vorgefertigten Lösungen gelöst werden, z LevelDBOder RocksDB.

Kurz gesagt, wir benötigen eine Datenbank, die es uns ermöglicht, drei Vorgänge schnell durchzuführen.

  • Der erste Vorgang ist die Aufnahme ключ-значение zu dieser Datenbank. Sie macht das sehr schnell, wo ключ-значение sind beliebige Zeichenfolgen.
  • Die zweite Operation ist eine schnelle Suche nach einem Wert mithilfe eines bestimmten Schlüssels.
  • Und die dritte Operation ist eine schnelle Suche nach allen Werten nach einem bestimmten Präfix.

LevelDB und RocksDB – diese Datenbanken wurden von Google und Facebook entwickelt. Zuerst kam LevelDB. Dann haben die Jungs von Facebook LevelDB genommen und angefangen, es zu verbessern, sie haben RocksDB erstellt. Mittlerweile funktionieren fast alle internen Datenbanken auf RocksDB innerhalb von Facebook, einschließlich derjenigen, die auf RocksDB und MySQL übertragen wurden. Sie gaben ihm einen Namen MyRocks.

Ein invertierter Index kann mit LevelDB implementiert werden. Wie kann man das machen? Wir speichern als Schlüssel label=value. Und der Wert ist die Kennung der Zeitreihe, in der das Paar vorhanden ist label=value.

Wenn wir viele Zeitreihen mit einem bestimmten Paar haben label=value, dann gibt es in dieser Datenbank viele Zeilen mit demselben Schlüssel und unterschiedlichen timeseries_ids. Um eine Liste aller zu erhalten timeseries_ids, die damit beginnen label=prefix, führen wir einen Bereichsscan durch, für den diese Datenbank optimiert ist. Das heißt, wir wählen alle Zeilen aus, die mit beginnen label=prefix und das Notwendige besorgen timeseries_ids.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Hier ist eine Beispielimplementierung, wie es in Go aussehen würde. Wir haben einen invertierten Index. Das ist LevelDB.

Die Funktion ist dieselbe wie bei der naiven Implementierung. Es wiederholt die naive Umsetzung fast Zeile für Zeile. Der einzige Punkt ist, dass man sich nicht an wendet map Wir greifen auf den invertierten Index zu. Wir erhalten alle Werte für den ersten label=value. Dann gehen wir alle verbleibenden Paare durch label=value und erhalten Sie die entsprechenden Sätze von Metrik-IDs für sie. Dann finden wir den Schnittpunkt.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Alles scheint in Ordnung zu sein, aber diese Lösung hat Nachteile. VictoriaMetrics implementierte zunächst einen invertierten Index basierend auf LevelDB. Aber am Ende musste ich es aufgeben.

Warum? Weil LevelDB langsamer ist als die naive Implementierung. In einer naiven Implementierung rufen wir bei gegebenem Schlüssel sofort das gesamte Slice ab metricIDs. Dies ist ein sehr schneller Vorgang – das gesamte Slice ist gebrauchsfertig.

In LevelDB jedes Mal, wenn eine Funktion aufgerufen wird GetValues Sie müssen alle Zeilen durchgehen, die mit beginnen label=value. Und erhalten Sie den Wert für jede Zeile timeseries_ids. Von solchen timeseries_ids Sammle ein Stück davon timeseries_ids. Offensichtlich ist dies viel langsamer als der einfache Zugriff auf eine normale Karte per Schlüssel.

Der zweite Nachteil besteht darin, dass LevelDB in C geschrieben ist. Der Aufruf von C-Funktionen aus Go ist nicht sehr schnell. Es dauert Hunderte von Nanosekunden. Das ist nicht sehr schnell, denn im Vergleich zu einem regulären in go geschriebenen Funktionsaufruf, der 1-5 Nanosekunden dauert, beträgt der Leistungsunterschied das Zehnfache. Für VictoriaMetrics war das ein fataler Fehler :)

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Also habe ich meine eigene Implementierung des invertierten Index geschrieben. Und er rief sie an Mergeset.

Mergeset basiert auf der MergeTree-Datenstruktur. Diese Datenstruktur ist von ClickHouse entlehnt. Offensichtlich sollte Mergeset für eine schnelle Suche optimiert werden timeseries_ids entsprechend dem angegebenen Schlüssel. Mergeset ist vollständig in Go geschrieben. Du kannst sehen VictoriaMetrics-Quellen auf GitHub. Die Implementierung von mergeset befindet sich im Ordner /lib/mergeset. Sie können versuchen herauszufinden, was dort vor sich geht.

Die Mergeset-API ist LevelDB und RocksDB sehr ähnlich. Das heißt, Sie können dort schnell neue Datensätze speichern und Datensätze schnell anhand eines bestimmten Präfixes auswählen.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Wir werden später über die Nachteile von Mergeset sprechen. Lassen Sie uns nun darüber sprechen, welche Probleme mit VictoriaMetrics in der Produktion bei der Implementierung eines invertierten Indexes aufgetreten sind.

Warum sind sie entstanden?

Der erste Grund ist die hohe Abwanderungsrate. Ins Russische übersetzt ist dies eine häufige Änderung in Zeitreihen. Dies ist der Zeitpunkt, an dem eine Zeitreihe endet und eine neue Reihe beginnt, oder wenn viele neue Zeitreihen beginnen. Und das passiert oft.

Der zweite Grund ist die große Anzahl an Zeitreihen. Zu Beginn, als die Überwachung immer beliebter wurde, war die Anzahl der Zeitreihen gering. Beispielsweise müssen Sie für jeden Computer die CPU-, Speicher-, Netzwerk- und Festplattenlast überwachen. 4 Zeitreihen pro Computer. Nehmen wir an, Sie haben 100 Computer und 400 Zeitreihen. Das ist sehr wenig.

Im Laufe der Zeit haben die Leute herausgefunden, dass sie detailliertere Informationen messen können. Messen Sie beispielsweise die Auslastung nicht des gesamten Prozessors, sondern separat für jeden Prozessorkern. Wenn Sie über 40 Prozessorkerne verfügen, stehen Ihnen 40-mal mehr Zeitreihen zur Messung der Prozessorlast zur Verfügung.

Aber das ist noch nicht alles. Jeder Prozessorkern kann mehrere Zustände haben, z. B. Leerlauf, wenn er sich im Leerlauf befindet. Und arbeiten Sie auch im Benutzerbereich, im Kernelbereich und in anderen Zuständen. Und jeder dieser Zustände kann auch als separate Zeitreihe gemessen werden. Dadurch erhöht sich die Anzahl der Reihen zusätzlich um das 7-8-fache.

Aus einer Metrik haben wir 40 x 8 = 320 Metriken für nur einen Computer erhalten. Mit 100 multipliziert erhalten wir 32 statt 000.

Dann kam Kubernetes. Und es wurde noch schlimmer, weil Kubernetes viele verschiedene Dienste hosten kann. Jeder Dienst in Kubernetes besteht aus vielen Pods. Und das alles muss überwacht werden. Darüber hinaus stellen wir ständig neue Versionen Ihrer Dienste bereit. Für jede neue Version müssen neue Zeitreihen erstellt werden. Dadurch wächst die Anzahl der Zeitreihen exponentiell und wir stehen vor dem Problem einer großen Anzahl von Zeitreihen, das als hohe Kardinalität bezeichnet wird. VictoriaMetrics meistert dies im Vergleich zu anderen Zeitreihendatenbanken erfolgreich.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Werfen wir einen genaueren Blick auf die hohe Abwanderungsrate. Was verursacht eine hohe Fluktuationsrate in der Produktion? Weil sich einige Bedeutungen von Etiketten und Tags ständig ändern.

Nehmen Sie zum Beispiel Kubernetes, das das Konzept hat deployment, also wenn eine neue Version Ihrer Anwendung ausgerollt wird. Aus irgendeinem Grund haben die Kubernetes-Entwickler beschlossen, der Bezeichnung die Bereitstellungs-ID hinzuzufügen.

Wozu hat das geführt? Darüber hinaus werden bei jedem neuen Einsatz alle alten Zeitreihen unterbrochen und stattdessen beginnen neue Zeitreihen mit einem neuen Labelwert deployment_id. Es kann Hunderttausende und sogar Millionen solcher Zeilen geben.

Das Wichtige an all dem ist, dass die Gesamtzahl der Zeitreihen wächst, die Zahl der Zeitreihen, die gerade aktiv sind und Daten empfangen, jedoch konstant bleibt. Dieser Zustand wird als hohe Abwanderungsrate bezeichnet.

Das Hauptproblem einer hohen Abwanderungsrate besteht darin, eine konstante Suchgeschwindigkeit für alle Zeitreihen für einen bestimmten Satz von Etiketten über einen bestimmten Zeitraum hinweg sicherzustellen. Typischerweise ist dies das Zeitintervall für die letzte Stunde oder den letzten Tag.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Wie kann dieses Problem gelöst werden? Hier ist die erste Option. Dies dient dazu, den invertierten Index im Laufe der Zeit in unabhängige Teile aufzuteilen. Das heißt, nach einiger Zeit beenden wir die Arbeit mit dem aktuellen invertierten Index. Und erstellen Sie einen neuen invertierten Index. Eine weitere Zeitspanne vergeht, wir erschaffen eine weitere und eine weitere.

Und wenn wir aus diesen invertierten Indizes Stichproben ziehen, finden wir eine Reihe invertierter Indizes, die in das angegebene Intervall fallen. Und dementsprechend wählen wir dort die ID der Zeitreihe aus.

Dies spart Ressourcen, da wir nicht nach Teilen suchen müssen, die nicht in das vorgegebene Intervall fallen. Das heißt, wenn wir Daten für die letzte Stunde auswählen, überspringen wir normalerweise Anfragen für frühere Zeitintervalle.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Es gibt eine andere Möglichkeit, dieses Problem zu lösen. Dadurch wird für jeden Tag eine separate Liste der IDs der Zeitreihen gespeichert, die an diesem Tag aufgetreten sind.

Der Vorteil dieser Lösung gegenüber der vorherigen Lösung besteht darin, dass wir keine Zeitreiheninformationen duplizieren, die mit der Zeit nicht verschwinden. Sie sind ständig präsent und verändern sich nicht.

Der Nachteil besteht darin, dass eine solche Lösung schwieriger zu implementieren und schwieriger zu debuggen ist. Und VictoriaMetrics hat sich für diese Lösung entschieden. So geschah es historisch. Auch diese Lösung schneidet im Vergleich zur vorherigen gut ab. Denn diese Lösung wurde nicht umgesetzt, da es notwendig ist, Daten in jeder Partition für Zeitreihen zu duplizieren, die sich nicht ändern, d. h. die im Laufe der Zeit nicht verschwinden. VictoriaMetrics wurde in erster Linie für den Speicherplatzverbrauch optimiert, und die vorherige Implementierung verschlechterte den Speicherplatzverbrauch. Diese Implementierung eignet sich jedoch besser zur Minimierung des Speicherplatzverbrauchs und wurde daher ausgewählt.

Ich musste gegen sie kämpfen. Das Problem bestand darin, dass Sie bei dieser Implementierung immer noch eine viel größere Zahl wählen müssen timeseries_ids für Daten als wenn der invertierte Index zeitlich partitioniert ist.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Wie haben wir dieses Problem gelöst? Wir haben es auf originelle Weise gelöst – indem wir in jedem invertierten Indexeintrag mehrere Zeitreihen-Identifikatoren anstelle eines Identifikators gespeichert haben. Das heißt, wir haben einen Schlüssel label=value, was in jeder Zeitreihe vorkommt. Und jetzt sparen wir mehrere timeseries_ids in einem Eintrag.

Hier ist ein Beispiel. Früher hatten wir N Einträge, aber jetzt haben wir einen Eintrag, dessen Präfix mit allen anderen identisch ist. Für den vorherigen Eintrag enthält der Wert alle Zeitreihen-IDs.

Dadurch war es möglich, die Scangeschwindigkeit eines solchen invertierten Indexes auf das Zehnfache zu erhöhen. Und es ermöglichte uns, den Speicherverbrauch für den Cache zu reduzieren, da wir jetzt die Zeichenfolge speichern label=value nur einmal im Cache zusammen N-mal. Und diese Zeile kann groß sein, wenn Sie lange Zeilen in Ihren Tags und Labels speichern, die Kubernetes gerne dorthin schiebt.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Eine weitere Möglichkeit, die Suche in einem invertierten Index zu beschleunigen, ist Sharding. Erstellen mehrerer invertierter Indizes anstelle eines einzigen und Aufteilung der Daten zwischen ihnen nach Schlüssel. Dies ist ein Set key=value Dampf. Das heißt, wir erhalten mehrere unabhängige invertierte Indizes, die wir auf mehreren Prozessoren parallel abfragen können. Bisherige Implementierungen erlaubten nur den Betrieb im Einzelprozessormodus, also das Scannen von Daten auf nur einem Kern. Mit dieser Lösung können Sie Daten auf mehreren Kernen gleichzeitig scannen, wie es ClickHouse gerne tut. Das ist es, was wir umsetzen wollen.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Kehren wir nun zu unserem Schaf zurück – zur Schnittpunktfunktion timeseries_ids. Lassen Sie uns überlegen, welche Implementierungen es geben könnte. Mit dieser Funktion können Sie suchen timeseries_ids für einen gegebenen Satz label=value.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Die erste Option ist eine naive Implementierung. Zwei verschachtelte Schleifen. Hier erhalten wir die Funktionseingabe intersectInts Zwei Scheiben - a и b. Am Ausgang sollte es uns den Schnittpunkt dieser Scheiben zurückgeben.

Eine naive Implementierung sieht so aus. Wir iterieren über alle Werte aus Slice a, innerhalb dieser Schleife durchlaufen wir alle Werte von Slice b. Und wir vergleichen sie. Wenn sie übereinstimmen, haben wir einen Schnittpunkt gefunden. Und speichern Sie es result.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Was sind die Nachteile? Quadratische Komplexität ist ihr Hauptnachteil. Wenn es sich bei Ihren Abmessungen beispielsweise um Slice-Dimensionen handelt a и b eine Million auf einmal, dann wird Ihnen diese Funktion nie eine Antwort zurückgeben. Denn es müssen eine Billion Iterationen durchgeführt werden, was selbst für moderne Computer eine Menge ist.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Die zweite Implementierung basiert auf einer Karte. Wir erstellen eine Karte. Wir haben alle Werte von Slice in diese Karte eingefügt a. Dann durchlaufen wir Slice in einer separaten Schleife b. Und wir prüfen, ob dieser Wert aus Slice stammt b in der Karte. Wenn es existiert, fügen Sie es dem Ergebnis hinzu.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Was sind die Vorteile? Der Vorteil besteht darin, dass es nur lineare Komplexität gibt. Das heißt, die Funktion wird bei größeren Slices viel schneller ausgeführt. Bei einem Slice der Größe einer Million wird diese Funktion in 2 Millionen Iterationen ausgeführt, im Gegensatz zu den Billionen Iterationen der vorherigen Funktion.

Der Nachteil besteht darin, dass diese Funktion mehr Speicher benötigt, um diese Karte zu erstellen.

Der zweite Nachteil ist der große Aufwand für das Hashing. Dieser Nachteil ist nicht sehr offensichtlich. Und für uns war es auch nicht sehr offensichtlich, sodass die Implementierung von Schnittmengen in VictoriaMetrics zunächst über eine Karte erfolgte. Doch dann zeigte die Profilerstellung, dass die Hauptprozessorzeit damit verbracht wird, in die Karte zu schreiben und zu prüfen, ob in dieser Karte ein Wert vorhanden ist.

Warum wird an diesen Stellen CPU-Zeit verschwendet? Weil Go eine Hashing-Operation für diese Zeilen durchführt. Das heißt, es berechnet den Hash des Schlüssels, um dann an einem bestimmten Index in der HashMap darauf zuzugreifen. Der Hash-Berechnungsvorgang ist in einigen zehn Nanosekunden abgeschlossen. Für VictoriaMetrics ist das langsam.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Ich habe mich entschieden, ein speziell für diesen Fall optimiertes Bitset zu implementieren. So sieht nun der Schnittpunkt zweier Scheiben aus. Hier erstellen wir ein Bitset. Wir fügen Elemente aus dem ersten Slice hinzu. Dann prüfen wir das Vorhandensein dieser Elemente im zweiten Slice. Und fügen Sie sie dem Ergebnis hinzu. Das heißt, es unterscheidet sich fast nicht vom vorherigen Beispiel. Das Einzige hier ist, dass wir den Zugriff auf die Karte durch benutzerdefinierte Funktionen ersetzt haben add и has.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Auf den ersten Blick scheint es, dass dies langsamer funktionieren sollte, wenn dort zuvor eine Standardkarte verwendet wurde und dann einige andere Funktionen aufgerufen werden. Die Profilerstellung zeigt jedoch, dass dieses Ding im Fall von VictoriaMetrics zehnmal schneller arbeitet als die Standardkarte.

Darüber hinaus verbraucht es im Vergleich zur Kartenimplementierung viel weniger Speicher. Weil wir hier Bits anstelle von Acht-Byte-Werten speichern.

Der Nachteil dieser Implementierung besteht darin, dass sie nicht so offensichtlich und nicht trivial ist.

Ein weiterer Nachteil, der vielen möglicherweise nicht auffällt, besteht darin, dass diese Implementierung in manchen Fällen möglicherweise nicht gut funktioniert. Das heißt, es ist für einen bestimmten Fall optimiert, nämlich für diesen Fall der Schnittmenge von VictoriaMetrics-Zeitreihen-IDs. Dies bedeutet nicht, dass es für alle Fälle geeignet ist. Bei falscher Verwendung kommt es nicht zu einer Leistungssteigerung, sondern zu einem Speicherfehler und einer Leistungsverlangsamung.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Betrachten wir die Implementierung dieser Struktur. Wenn Sie nachsehen möchten, befindet es sich in den VictoriaMetrics-Quellen im Ordner lib/uint64set. Es ist speziell für den VictoriaMetrics-Fall optimiert, wo timeseries_id ist ein 64-Bit-Wert, bei dem die ersten 32 Bit grundsätzlich konstant sind und sich nur die letzten 32 Bit ändern.

Diese Datenstruktur wird nicht auf der Festplatte gespeichert, sondern nur im Speicher.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Hier ist seine API. Es ist nicht sehr kompliziert. Die API ist speziell auf ein bestimmtes Beispiel für die Verwendung von VictoriaMetrics zugeschnitten. Das heißt, hier gibt es keine unnötigen Funktionen. Hier sind die Funktionen, die explizit von VictoriaMetrics verwendet werden.

Es gibt Funktionen add, was neue Werte hinzufügt. Es gibt eine Funktion has, das nach neuen Werten sucht. Und es gibt eine Funktion del, wodurch Werte entfernt werden. Es gibt eine Hilfsfunktion len, was die Größe der Menge zurückgibt. Funktion clone klont viel. Und Funktion appendto wandelt diesen Satz in einen Slice um timeseries_ids.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

So sieht die Implementierung dieser Datenstruktur aus. Set besteht aus zwei Elementen:

  • ItemsCount ist ein Hilfsfeld, um schnell die Anzahl der Elemente in einer Menge zurückzugeben. Auf dieses Hilfsfeld könnte zwar verzichtet werden, es musste aber hier hinzugefügt werden, da VictoriaMetrics in seinen Algorithmen häufig die Bitset-Länge abfragt.

  • Das zweite Feld ist buckets. Dies ist ein Ausschnitt aus der Struktur bucket32. Jede Struktur speichert hi Feld. Dies sind die oberen 32 Bit. Und zwei Scheiben - b16his и buckets von bucket16 Strukturen.

Hier werden die obersten 16 Bit des zweiten Teils der 64-Bit-Struktur gespeichert. Und hier werden Bitsätze für die unteren 16 Bits jedes Bytes gespeichert.

Bucket64 besteht aus einem Array uint64. Mit diesen Konstanten wird die Länge berechnet. In Eins bucket16 maximal speicherbar 2^16=65536 bisschen. Wenn man dies durch 8 teilt, sind es 8 Kilobyte. Wenn man noch einmal durch 8 dividiert, ist es 1000 uint64 Bedeutung. Das ist Bucket16 – das ist unsere 8-Kilobyte-Struktur.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Schauen wir uns an, wie eine der Methoden dieser Struktur zum Hinzufügen eines neuen Werts implementiert wird.

Alles beginnt mit uint64 Bedeutungen. Wir berechnen die oberen 32 Bit, wir berechnen die unteren 32 Bit. Lass uns alles durchgehen buckets. Wir vergleichen die obersten 32 Bits in jedem Bucket mit dem Mehrwert. Und wenn sie übereinstimmen, rufen wir die Funktion auf add in Struktur b32 buckets. Und fügen Sie dort die unteren 32 Bit hinzu. Und wenn es zurückkam true, dann bedeutet das, dass wir dort einen solchen Wert hinzugefügt haben und wir keinen solchen Wert hatten. Wenn es zurückkommt false, dann existierte eine solche Bedeutung bereits. Dann erhöhen wir die Anzahl der Elemente in der Struktur.

Wenn wir nicht das gefunden haben, was Sie brauchen bucket mit dem erforderlichen Hochwert, dann rufen wir die Funktion auf addAlloc, was ein neues hervorbringen wird bucketund fügt es der Bucket-Struktur hinzu.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Dies ist die Implementierung der Funktion b32.add. Es ähnelt der vorherigen Implementierung. Wir berechnen die höchstwertigen 16 Bits, die niedrigstwertigen 16 Bits.

Dann gehen wir alle oberen 16 Bit durch. Wir finden Übereinstimmungen. Und wenn es eine Übereinstimmung gibt, rufen wir die Add-Methode auf, die wir auf der nächsten Seite betrachten werden bucket16.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Und hier ist die unterste Ebene, die so weit wie möglich optimiert werden sollte. Wir berechnen für uint64 ID-Wert im Slice-Bit und auch bitmask. Dies ist eine Maske für einen bestimmten 64-Bit-Wert, mit der das Vorhandensein dieses Bits überprüft oder gesetzt werden kann. Wir prüfen, ob dieses Bit gesetzt ist, setzen es und geben die Präsenz zurück. Dies ist unsere Implementierung, die es uns ermöglichte, den Vorgang sich überschneidender IDs von Zeitreihen im Vergleich zu herkömmlichen Karten um das Zehnfache zu beschleunigen.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Zusätzlich zu dieser Optimierung verfügt VictoriaMetrics über viele weitere Optimierungen. Die meisten dieser Optimierungen wurden aus einem bestimmten Grund hinzugefügt, jedoch nach der Profilierung des Codes in der Produktion.

Dies ist die Hauptregel der Optimierung: Fügen Sie keine Optimierung hinzu, wenn Sie davon ausgehen, dass es hier einen Engpass gibt, denn es könnte sich herausstellen, dass es dort keinen Engpass gibt. Eine Optimierung verschlechtert in der Regel die Qualität des Codes. Daher lohnt es sich, erst nach der Profilierung und am besten in der Produktion zu optimieren, damit es sich um echte Daten handelt. Wenn jemand interessiert ist, können Sie sich den VictoriaMetrics-Quellcode ansehen und andere Optimierungen erkunden, die dort vorhanden sind.

Go-Optimierungen in VictoriaMetrics. Alexander Waljalkin

Ich habe eine Frage zum Bitset. Sehr ähnlich der C++-Vektor-Bool-Implementierung, optimierter Bitsatz. Haben Sie die Implementierung von dort übernommen?

Nein, nicht von dort. Bei der Implementierung dieses Bitsatzes habe ich mich von der Kenntnis der Struktur dieser ID-Zeitreihen leiten lassen, die in VictoriaMetrics verwendet werden. Und ihre Struktur ist so, dass die oberen 32 Bit grundsätzlich konstant sind. Die unteren 32 Bits können sich ändern. Je niedriger das Bit, desto häufiger kann es sich ändern. Daher ist diese Implementierung speziell für diese Datenstruktur optimiert. Die C++-Implementierung ist meines Wissens für den allgemeinen Fall optimiert. Wenn Sie für den allgemeinen Fall optimieren, bedeutet dies, dass es für einen bestimmten Fall nicht optimal ist.

Ich empfehle Ihnen auch, sich den Bericht von Alexey Milovid anzusehen. Vor etwa einem Monat sprach er über die Optimierung in ClickHouse für bestimmte Spezialisierungen. Er sagt lediglich, dass im Allgemeinen eine C++-Implementierung oder eine andere Implementierung darauf zugeschnitten ist, im Durchschnitt in einem Krankenhaus gut zu funktionieren. Die Leistung ist möglicherweise schlechter als eine wissensspezifische Implementierung wie unsere, bei der wir wissen, dass die oberen 32 Bits größtenteils konstant sind.

Ich habe eine zweite Frage. Was ist der grundlegende Unterschied zu InfluxDB?

Es gibt viele grundlegende Unterschiede. In Bezug auf Leistung und Speicherverbrauch zeigt InfluxDB in Tests einen zehnmal höheren Speicherverbrauch für Zeitreihen mit hoher Kardinalität, wenn Sie viele davon haben, beispielsweise Millionen. Beispielsweise verbraucht VictoriaMetrics 10 GB pro Million aktiver Zeilen, während InfluxDB 1 GB verbraucht. Und das ist ein großer Unterschied.

Der zweite grundlegende Unterschied besteht darin, dass InfluxDB über seltsame Abfragesprachen verfügt – Flux und InfluxQL. Sie sind im Vergleich zu nicht sehr praktisch für die Arbeit mit Zeitreihen PromQL, das von VictoriaMetrics unterstützt wird. PromQL ist eine Abfragesprache von Prometheus.

Ein weiterer Unterschied besteht darin, dass InfluxDB über ein etwas seltsames Datenmodell verfügt, bei dem in jeder Zeile mehrere Felder mit unterschiedlichen Tags gespeichert werden können. Diese Zeilen sind weiter in verschiedene Tabellen unterteilt. Diese zusätzlichen Komplikationen erschweren die spätere Arbeit mit dieser Datenbank. Es ist schwer zu unterstützen und zu verstehen.

In VictoriaMetrics ist alles viel einfacher. Dort ist jede Zeitreihe ein Schlüsselwert. Der Wert ist eine Menge von Punkten - (timestamp, value), und der Schlüssel ist die Menge label=value. Es gibt keine Trennung zwischen Feldern und Messungen. Im Gegensatz zu InfluxDB, wo Berechnungen zwischen verschiedenen Zeilen meines Wissens noch nicht implementiert sind, können Sie damit beliebige Daten auswählen und dann kombinieren, addieren, subtrahieren, multiplizieren und dividieren. Selbst wenn sie implementiert werden, ist es schwierig, man muss viel Code schreiben.

Ich habe eine klärende Frage. Habe ich richtig verstanden, dass es ein Problem gab, über das Sie gesprochen haben, dass dieser invertierte Index nicht in den Speicher passt, also gibt es dort eine Partitionierung?

Zuerst habe ich eine naive Implementierung eines invertierten Index auf einer Standard-Go-Karte gezeigt. Diese Implementierung ist für Datenbanken nicht geeignet, da dieser invertierte Index nicht auf der Festplatte gespeichert wird und die Datenbank auf der Festplatte gespeichert werden muss, damit diese Daten beim Neustart verfügbar bleiben. In dieser Implementierung verschwindet Ihr invertierter Index, wenn Sie die Anwendung neu starten. Und Sie verlieren den Zugriff auf alle Daten, weil Sie sie nicht finden können.

Guten Tag! Danke für den Bericht! Mein Name ist Pavel. Ich komme aus Wildberries. Ich habe ein paar Fragen an Sie. Frage eins. Glauben Sie, dass Sie, wenn Sie beim Aufbau der Architektur Ihrer Anwendung ein anderes Prinzip gewählt und die Daten im Laufe der Zeit partitioniert hätten, möglicherweise in der Lage gewesen wären, Daten bei der Suche zu überschneiden, und zwar nur aufgrund der Tatsache, dass eine Partition Daten für eine enthält? Zeitraum, also in einem Zeitintervall, und Sie müssten sich keine Sorgen darüber machen, dass Ihre Stücke unterschiedlich verstreut sind? Frage Nummer 2: Da Sie einen ähnlichen Algorithmus mit Bitset und allem anderen implementieren, haben Sie es vielleicht versucht, Prozessoranweisungen zu verwenden? Vielleicht haben Sie solche Optimierungen schon ausprobiert?

Die zweite Frage beantworte ich gleich. An diesem Punkt sind wir noch nicht angekommen. Aber wenn nötig, kommen wir dorthin. Und die erste Frage: Wie lautete die Frage?

Sie haben zwei Szenarien besprochen. Und sie sagten, dass sie sich für die zweite Variante mit einer komplexeren Implementierung entschieden hätten. Und sie bevorzugten nicht die erste Variante, bei der die Daten nach Zeit partitioniert sind.

Ja. Im ersten Fall wäre das Gesamtvolumen des Index größer, da wir in jeder Partition doppelte Daten für die Zeitreihen speichern müssten, die sich über alle diese Partitionen erstrecken. Und wenn die Abwanderungsrate Ihrer Zeitreihen gering ist, d. h. ständig dieselben Zeitreihen verwendet werden, dann würden wir im ersten Fall viel mehr an belegtem Speicherplatz verlieren als im zweiten Fall.

Und so – ja, Zeitpartitionierung ist eine gute Option. Prometheus nutzt es. Aber Prometheus hat noch einen weiteren Nachteil. Beim Zusammenführen dieser Datenteile müssen Metainformationen für alle Beschriftungen und Zeitreihen im Speicher verbleiben. Wenn daher die zusammengeführten Datenmengen groß sind, steigt der Speicherverbrauch während der Zusammenführung im Gegensatz zu VictoriaMetrics stark an. Beim Zusammenführen verbraucht VictoriaMetrics überhaupt keinen Speicher; es werden nur ein paar Kilobyte verbraucht, unabhängig von der Größe der zusammengeführten Datenstücke.

Der von Ihnen verwendete Algorithmus verwendet Speicher. Es markiert Zeitreihen-Tags, die Werte enthalten. Und auf diese Weise prüfen Sie, ob in einem Datenarray und in einem anderen ein Paar vorhanden ist. Und Sie verstehen, ob eine Überschneidung stattgefunden hat oder nicht. Aufgrund der einfachen Komplexität dieser Vorgänge implementieren Datenbanken normalerweise Cursor und Iteratoren, die ihren aktuellen Inhalt speichern und die sortierten Daten durchlaufen.

Warum verwenden wir keine Cursor zum Durchlaufen von Daten?

Ja.

Wir speichern sortierte Zeilen in LevelDB oder Mergeset. Wir können den Cursor bewegen und den Schnittpunkt finden. Warum nutzen wir es nicht? Weil es langsam ist. Weil Cursor bedeuten, dass Sie für jede Zeile eine Funktion aufrufen müssen. Ein Funktionsaufruf dauert 5 Nanosekunden. Und wenn Sie 100 Zeilen haben, stellt sich heraus, dass wir eine halbe Sekunde damit verbringen, die Funktion aufzurufen.

So etwas gibt es, ja. Und meine letzte Frage. Die Frage mag etwas seltsam klingen. Warum ist es nicht möglich, beim Eintreffen der Daten alle notwendigen Aggregate auszulesen und in der gewünschten Form zu speichern? Warum in einigen Systemen wie VictoriaMetrics, ClickHouse usw. riesige Mengen speichern und dann viel Zeit damit verbringen?

Ich werde ein Beispiel geben, um es klarer zu machen. Sagen wir mal, wie funktioniert ein kleiner Spielzeugtacho? Es zeichnet die von Ihnen zurückgelegte Strecke auf und addiert sie ständig zu einem Wert und zum zweiten Mal. Und teilt. Und erreicht Durchschnittsgeschwindigkeit. Sie können ungefähr das Gleiche tun. Fassen Sie alle notwendigen Fakten im Handumdrehen zusammen.

Okay, ich verstehe die Frage. Ihr Beispiel hat seine Berechtigung. Wenn Sie wissen, welche Aggregate Sie benötigen, ist dies die beste Implementierung. Das Problem besteht jedoch darin, dass die Leute diese Metriken und einige Daten in ClickHouse speichern und noch nicht wissen, wie sie diese in Zukunft aggregieren und filtern werden, sodass sie alle Rohdaten speichern müssen. Aber wenn Sie wissen, dass Sie etwas im Durchschnitt berechnen müssen, warum berechnen Sie es dann nicht, anstatt dort eine Reihe von Rohwerten zu speichern? Aber das geht nur, wenn Sie genau wissen, was Sie brauchen.

Datenbanken zur Speicherung von Zeitreihen unterstützen übrigens die Zählung von Aggregaten. Beispielsweise unterstützt Prometheus Aufnahmeregeln. Das heißt, dies ist möglich, wenn Sie wissen, welche Einheiten Sie benötigen. VictoriaMetrics hat dies noch nicht, aber es wird normalerweise Prometheus vorangestellt, in dem dies in den Rekodierungsregeln erfolgen kann.

In meinem vorherigen Job musste ich beispielsweise die Anzahl der Ereignisse in einem Schiebefenster in der letzten Stunde zählen. Das Problem ist, dass ich eine benutzerdefinierte Implementierung in Go erstellen musste, also einen Dienst zum Zählen dieser Sache. Diese Leistung war letztendlich nicht trivial, da sie schwer zu berechnen ist. Die Implementierung kann einfach sein, wenn Sie einige Aggregate in festen Zeitintervallen zählen müssen. Wenn Sie Ereignisse in einem Schiebefenster zählen möchten, ist dies nicht so einfach, wie es scheint. Ich denke, dass dies noch nicht in ClickHouse oder in Zeitreihendatenbanken implementiert wurde, da es schwierig zu implementieren ist.

Und noch eine Frage. Wir haben gerade über die Mittelwertbildung gesprochen, und mir fiel ein, dass es einmal so etwas wie Graphite mit einem Carbon-Backend gab. Und er wusste, wie man alte Daten ausdünnt, also einen Punkt pro Minute, einen Punkt pro Stunde usw. übrig lässt. Im Prinzip ist das ganz praktisch, wenn wir relativ gesehen Rohdaten für einen Monat benötigen und alles andere kann ausgedünnt werden. Prometheus und VictoriaMetrics unterstützen diese Funktionalität jedoch nicht. Ist eine Unterstützung geplant? Wenn nicht, warum nicht?

Danke für die Frage. Unsere Benutzer stellen diese Frage regelmäßig. Sie fragen, wann wir Unterstützung für Downsampling hinzufügen werden. Hier gibt es mehrere Probleme. Erstens versteht jeder Benutzer downsampling etwas anderes: Jemand möchte einen beliebigen Punkt in einem bestimmten Intervall erhalten, jemand möchte Maximal-, Minimal- und Durchschnittswerte. Wenn viele Systeme Daten in Ihre Datenbank schreiben, können Sie sie nicht alle in einen Topf werfen. Es kann sein, dass jedes System eine andere Durchforstung erfordert. Und das ist schwer umzusetzen.

Und zweitens ist VictoriaMetrics wie ClickHouse für die Arbeit mit großen Mengen an Rohdaten optimiert, sodass es eine Milliarde Zeilen in weniger als einer Sekunde schaufeln kann, wenn Ihr System über viele Kerne verfügt. Scannen von Zeitreihenpunkten in VictoriaMetrics – 50 Punkte pro Sekunde und Kern. Und diese Leistung lässt sich auf vorhandene Kerne skalieren. Das heißt, wenn Sie beispielsweise über 000 Kerne verfügen, scannen Sie eine Milliarde Punkte pro Sekunde. Und diese Eigenschaft von VictoriaMetrics und ClickHouse reduziert die Notwendigkeit eines Downsamplings.

Ein weiteres Merkmal ist, dass VictoriaMetrics diese Daten effektiv komprimiert. Die durchschnittliche Komprimierung in der Produktion beträgt 0,4 bis 0,8 Byte pro Punkt. Jeder Punkt ist ein Zeitstempel + Wert. Und es wird im Durchschnitt auf weniger als ein Byte komprimiert.

Sergej. Ich habe eine Frage. Was ist die minimale Aufnahmezeit?

Eine Millisekunde. Wir hatten kürzlich ein Gespräch mit anderen Entwicklern von Zeitreihendatenbanken. Ihr minimaler Zeitanteil beträgt eine Sekunde. Und in Graphite beispielsweise ist es auch eine Sekunde. In OpenTSDB beträgt sie ebenfalls eine Sekunde. InfluxDB hat eine Genauigkeit im Nanosekundenbereich. In VictoriaMetrics ist es eine Millisekunde, weil es in Prometheus eine Millisekunde ist. Und VictoriaMetrics wurde ursprünglich als Remote-Speicher für Prometheus entwickelt. Aber jetzt können Daten von anderen Systemen gespeichert werden.

Die Person, mit der ich gesprochen habe, sagt, dass sie eine Genauigkeit von Sekunde zu Sekunde haben – das reicht für sie, weil es von der Art der Daten abhängt, die in der Zeitreihendatenbank gespeichert werden. Wenn es sich um DevOps-Daten oder Daten aus der Infrastruktur handelt, wo Sie sie in Abständen von 30 Sekunden pro Minute sammeln, dann reicht die Sekundengenauigkeit aus, weniger brauchen Sie nicht. Und wenn Sie diese Daten von Hochfrequenzhandelssystemen sammeln, benötigen Sie eine Genauigkeit im Nanosekundenbereich.

Die Millisekundengenauigkeit in VictoriaMetrics eignet sich auch für den DevOps-Fall und kann für die meisten Fälle geeignet sein, die ich am Anfang des Berichts erwähnt habe. Das Einzige, wofür es möglicherweise nicht geeignet ist, sind Hochfrequenzhandelssysteme.

Danke! Und eine andere Frage. Was ist Kompatibilität in PromQL?

Volle Abwärtskompatibilität. VictoriaMetrics unterstützt PromQL vollständig. Darüber hinaus fügt es zusätzliche erweiterte Funktionen in PromQL hinzu, die aufgerufen werden MetrikenQL. Auf YouTube gibt es einen Vortrag über diese erweiterte Funktionalität. Ich habe beim Monitoring Meetup im Frühjahr in St. Petersburg gesprochen.

Telegrammkanal VictoriaMetrics.

An der Umfrage können nur registrierte Benutzer teilnehmen. Einloggenbitte.

Was hält Sie davon ab, auf VictoriaMetrics als Langzeitspeicher für Prometheus umzusteigen? (Schreiben Sie es in die Kommentare, ich füge es der Umfrage hinzu))

  • 71,4%Ich verwende Prometheus5 nicht

  • 28,6%Ich wusste nichts über VictoriaMetrics2

7 Benutzer haben abgestimmt. 12 Benutzer enthielten sich der Stimme.

Source: habr.com

Kommentar hinzufügen