{"id":80733,"date":"2020-05-08T13:42:31","date_gmt":"2020-05-08T11:42:31","guid":{"rendered":"https:\/\/prohoster.info\/blog\/administrirovanie\/go-optimizations-in-victoriametrics-aleksandr-valyalkin"},"modified":"2020-05-08T13:42:31","modified_gmt":"2020-05-08T11:42:31","slug":"go-optimizations-in-victoriametrics-aleksandr-valyalkin","status":"publish","type":"post","link":"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/go-optimizations-in-victoriametrics-aleksandr-valyalkin","title":{"rendered":"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><strong>\u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u043a\u043e\u043d\u0446\u0430 2019 \u0433\u043e\u0434\u0430 \u0410\u043b\u0435\u043a\u0441\u0430\u043d\u0434\u0440\u0430 \u0412\u0430\u043b\u044f\u043b\u043a\u0438\u043d\u0430 &quot;Go optimizations in VictoriaMetrics&quot;<\/strong><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/victoriametrics.com\/\">VictoriaMetrics<\/a><\/noindex> \u2014 eine schnelle und skalierbare DBMS zur Speicherung und Verarbeitung von Daten in Form von Zeitreihen (ein Datensatz besteht aus einem Zeitpunkt und einer Menge von Werten, die diesem Zeitpunkt entsprechen, beispielsweise durch regelm\u00e4\u00dfige Abfragen von Sensordaten oder das Sammeln von Metriken erhalten).<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/dd13ec10594aeb138be29ce439cc476a.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<p>Hier ist der Link zum Video dieses Berichts \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/youtu.be\/MZ5P21j_HLE\">https:\/\/youtu.be\/MZ5P21j_HLE<\/a><\/noindex><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.google.com\/presentation\/d\/1k7OjHvxTHA7669MFwsNTCx8hII-a8lNvpmQetLxmrEU\/edit?usp=sharing\">Folien<\/a><\/noindex><\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/0073390d6dbcc6ab3e5305907ac6a229.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Ich erz\u00e4hle Ihnen ein wenig \u00fcber mich. Ich bin Alexander Valyalkin. Hier ist <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/valyala\">mein GitHub-Konto<\/a><\/noindex>. Ich interessiere mich f\u00fcr Go und Leistungsoptimierung. Ich habe viele n\u00fctzliche und weniger n\u00fctzliche Bibliotheken geschrieben. Diese beginnen entweder mit <code>fast<\/code>, oder mit <code>quick<\/code> als Pr\u00e4fix. <\/p>\n<p><\/p>\n<p>Derzeit arbeite ich an VictoriaMetrics. Was ist das und was mache ich dort? Dar\u00fcber werde ich in dieser Pr\u00e4sentation berichten. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/c9194bc3fee1f615d838979bec12f7d0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Der Plan des Berichts ist folgender:<\/p>\n<p><\/p>\n<ul>\n<li>Zuerst erz\u00e4hle ich Ihnen, was VictoriaMetrics ist. <\/li>\n<li>Dann erkl\u00e4re ich, was Zeitreihen sind. <\/li>\n<li>Danach erkl\u00e4re ich, wie die Zeitreihendatenbank funktioniert.<\/li>\n<li>Anschlie\u00dfend werde ich die Architektur der Datenbank erl\u00e4utern: Woraus sie besteht.<\/li>\n<li>Und dann kommen wir zu den Optimierungen, die in VictoriaMetrics vorhanden sind. Dazu geh\u00f6ren die Optimierung des invertierten Index und die Optimierung f\u00fcr die Bitset-Implementierung in Go.<\/li>\n<\/ul>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/3e0244b6b6fe952f660e4a094778921a.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Was ist VictoriaMetrics? Wei\u00df jemand aus dem Publikum Bescheid? Wow, viele Leute wissen already dar\u00fcber Bescheid. Das ist eine gute Nachricht. F\u00fcr diejenigen, die es nicht wissen \u2013 es ist eine Datenbank f\u00fcr Zeitreihen. Sie basiert auf der Architektur von ClickHouse, mit einigen Details der Implementierung von ClickHouse. Zum Beispiel: MergeTree, parallele Berechnungen auf allen verf\u00fcgbaren Prozessor-Kernen und Leistungsoptimierung durch die Arbeit mit Datenbl\u00f6cken, die im Prozessor-Cache platziert werden. <\/p>\n<p><\/p>\n<p>VictoriaMetrics bietet die beste Datenkompression im Vergleich zu anderen Datenbanken f\u00fcr Zeitreihen. <\/p>\n<p><\/p>\n<p>Sie kann vertikal skalieren \u2013 das hei\u00dft, Sie k\u00f6nnen zus\u00e4tzliche Prozessoren oder mehr Arbeitsspeicher auf einem Computer hinzuf\u00fcgen. VictoriaMetrics wird diese verf\u00fcgbaren Ressourcen erfolgreich ausnutzen und die lineare Leistung erh\u00f6hen.<\/p>\n<p><\/p>\n<p>VictoriaMetrics kann auch horizontal skalieren \u2013 das hei\u00dft, Sie k\u00f6nnen zus\u00e4tzliche Knoten zum VictoriaMetrics-Cluster hinzuf\u00fcgen, und ihre Leistung wird nahezu linear zunehmen.<\/p>\n<p><\/p>\n<p>Wie Sie wahrscheinlich erraten haben, ist VictoriaMetrics eine schnelle Datenbank, denn ich kann \u00fcber keine anderen sprechen. Au\u00dferdem ist sie in Go geschrieben, weshalb ich bei diesem Meetup dar\u00fcber referiere.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/0121c9baead722eb1fe22fb6f900d4ad.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Wer wei\u00df, was eine Zeitreihe ist? Viele Menschen wissen das ebenfalls. Eine Zeitreihe ist eine Serie von Paaren <code>(Zeitstempel, Wert)<\/code>, bei denen diese Paare zeitlich sortiert sind. Der Wert ist eine Flie\u00dfkomma-Zahl \u2013 float64.<\/p>\n<p><\/p>\n<p>Jede Zeitreihe wird eindeutig durch einen Schl\u00fcssel identifiziert. Woraus besteht dieser Schl\u00fcssel? Er besteht aus einer nicht leeren Menge von Schl\u00fcssel-Wert-Paaren. <\/p>\n<p><\/p>\n<p>Hier ist ein Beispiel f\u00fcr eine Zeitreihe. Der Schl\u00fcssel dieser Reihe ist eine Liste von Paaren: <code>__name__=&quot;cpu_usage&quot;<\/code> \u2013 das ist der Name der Metrik, <code>instance=&quot;my-server&quot;<\/code> \u2013 das ist der Computer, auf dem diese Metrik gesammelt wurde, <code>datacenter=&quot;us-east&quot;<\/code> \u2013 das ist das Rechenzentrum, in dem dieser Computer steht.<\/p>\n<p><\/p>\n<p>Wir haben den Namen einer Zeitreihe erhalten, der aus drei Schl\u00fcssel-Wert-Paaren besteht. Zu diesem Schl\u00fcssel geh\u00f6rt eine Liste von Paaren <code>(Zeitstempel, Wert)<\/code>. <code>t1, t3, t3, ..., tN<\/code> \u2013 das sind Zeitstempel, <code>10, 20, 12, ..., 15<\/code> \u2013 und die entsprechenden Werte. Das ist die cpu-usage zu diesem Zeitpunkt f\u00fcr diese Reihe.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/76cab9db0263318d355ac607ceb7e0f6.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Wo k\u00f6nnen Zeitreihen verwendet werden? Hat jemand Ideen? <\/p>\n<p><\/p>\n<ul>\n<li>In DevOps k\u00f6nnen die CPU-, RAM-, Netzwerkbelastungen, rps, die Anzahl der Fehler usw. gemessen werden. <\/li>\n<li>IoT \u2013 wir k\u00f6nnen Temperatur, Druck, geografische Koordinaten und noch einiges mehr messen.<\/li>\n<li>Auch im Finanzbereich \u2013 wir k\u00f6nnen die Preise von Aktien und W\u00e4hrungen \u00fcberwachen. <\/li>\n<li>Dar\u00fcber hinaus k\u00f6nnen Zeitreihen zur \u00dcberwachung von Produktionsprozessen in Fabriken verwendet werden. Wir haben Nutzer, die VictoriaMetrics zur \u00dcberwachung von Windkraftanlagen und Robotern einsetzen.<\/li>\n<li>Zeitreihen sind auch n\u00fctzlich, um Informationen von Sensoren verschiedener Ger\u00e4te zu sammeln. Zum Beispiel f\u00fcr den Motor; zur Messung des Reifendrucks; zur Messung von Geschwindigkeit, Entfernung; zur Erfassung des Benzinverbrauchs usw.<\/li>\n<li>Au\u00dferdem k\u00f6nnen Zeitreihen zur \u00dcberwachung von Flugzeugen verwendet werden. In jedem Flugzeug gibt es einen schwarzen Kasten, der Zeitreihen zu verschiedenen Gesundheitsparametern des Flugzeugs sammelt. Zeitreihen werden auch in der Luft- und Raumfahrtindustrie eingesetzt. <\/li>\n<li>Healthcare \u2013 das umfasst Blutdruck, Puls usw.<\/li>\n<\/ul>\n<p><\/p>\n<p>Es gibt vielleicht noch weitere Anwendungen, die ich vergessen habe, aber ich hoffe, Sie haben verstanden, dass Zeitreihen in der modernen Welt aktiv genutzt werden. Und das Volumen ihrer Verwendung w\u00e4chst von Jahr zu Jahr.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/f1da29d372163751268b6a8f86836d04.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Wof\u00fcr wird eine Datenbank f\u00fcr Zeitreihen ben\u00f6tigt? Warum kann man keine herk\u00f6mmliche relationale Datenbank zur Speicherung von Zeitreihen verwenden?<\/p>\n<p><\/p>\n<p>Das liegt daran, dass Zeitreihen in der Regel gro\u00dfe Datenmengen enthalten, die in herk\u00f6mmlichen Datenbanken schwer zu speichern und zu verarbeiten sind. Daher wurden spezialisierte Datenbanken f\u00fcr Zeitreihen entwickelt. Diese Datenbanken speichern effizient Punkte <code>(Zeitstempel, Wert)<\/code> mit einem bestimmten Schl\u00fcssel. Sie bieten eine API zum Lesen der gespeicherten Daten nach Schl\u00fcssel, entweder f\u00fcr ein Paar Schl\u00fcssel-Wert, f\u00fcr mehrere solcher Paare oder nach regexp. Wenn Sie beispielsweise die CPU-Auslastung all Ihrer Dienste im Rechenzentrum in den USA finden m\u00f6chten, m\u00fcssen Sie eine solche Pseudodatenanfrage verwenden.<\/p>\n<p><\/p>\n<p>Normalerweise bieten Zeitreihendatenbanken spezialisierte Abfragesprachen an, da SQL f\u00fcr Zeitreihen nicht sehr gut geeignet ist. Obwohl es Datenbanken gibt, die SQL unterst\u00fctzen, ist es nicht optimal. Besser geeignete Abfragesprachen sind <noindex><a rel=\"nofollow\" href=\"https:\/\/medium.com\/@valyala\/promql-tutorial-for-beginners-9ab455142085\">PromQL<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.influxdata.com\/influxdb\/v1.8\/query_language\/spec\/\">InfluxQL<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/www.influxdata.com\/products\/flux\/\">Flux<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/code.kx.com\/q\/\">Q<\/a><\/noindex>. Ich hoffe, dass jemand zumindest von einer dieser Sprachen geh\u00f6rt hat. Von PromQL haben wahrscheinlich viele geh\u00f6rt. Das ist die Abfragesprache von Prometheus.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/59e5b6d82a2f7861077bc5fe34519ee4.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>So sieht die Architektur einer modernen Zeitreihendatenbank am Beispiel von VictoriaMetrics aus.<\/p>\n<p><\/p>\n<p>Sie besteht aus zwei Teilen: einem Speicher f\u00fcr den invertierten Index und einem Speicher f\u00fcr die Werte der Zeitreihen. Diese Speicher sind voneinander getrennt. <\/p>\n<p><\/p>\n<p>Wenn ein neuer Datensatz in die Datenbank eingeht, greifen wir zuerst auf den invertierten Index zu, um die ID der Zeitreihe anhand eines bestimmten Satzes zu finden. <code>label=value<\/code> f\u00fcr die betreffende Kennzahl. Wir finden diese ID und speichern den Wert im Datenspeicher.<\/p>\n<p><\/p>\n<p>Wenn ein Abfrageantrag an die TSDB gestellt wird, greifen wir zun\u00e4chst auf den invertierten Index zu. Wir holen alle <code>timeseries_ids<\/code> Datens\u00e4tze, die dem angegebenen Satz entsprechen. <code>label=value<\/code>Dann holen wir alle erforderlichen Daten aus dem nach <code>timeseries_ids<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/eb9b36fa3c6de2188b97b56ad3839b2c.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Lassen Sie uns ein Beispiel betrachten, wie eine Zeitreihendatenbank einen eingehenden select-Antrag verarbeitet.<\/p>\n<p><\/p>\n<ul>\n<li>Zuerst werden alle <code>timeseries_ids<\/code> aus dem invertierten Index abgerufen, die die angegebenen Paare enthalten <code>label=value<\/code>oder einem bestimmten regul\u00e4ren Ausdruck entsprechen.<\/li>\n<li>Dann werden alle Datenpunkte aus dem Datenspeicher f\u00fcr den angegebenen Zeitraum f\u00fcr die gefundenen IDs abgerufen. <code>timeseries_ids<\/code>.<\/li>\n<li>Danach f\u00fchrt die Datenbank Berechnungen auf diesen Datenpunkten durch, entsprechend der Benutzeranfrage. Anschlie\u00dfend wird die Antwort zur\u00fcckgegeben.<\/li>\n<\/ul>\n<p><\/p>\n<p>In dieser Pr\u00e4sentation m\u00f6chte ich Ihnen den ersten Teil vorstellen. Es geht um die Suche <code>timeseries_ids<\/code> im umgekehrten Index. Den zweiten und dritten Teil k\u00f6nnen Sie sich sp\u00e4ter ansehen <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/VictoriaMetrics\/VictoriaMetrics\">die Quellcodes von VictoriaMetrics<\/a><\/noindex>, oder warten, bis ich weitere Vortr\u00e4ge vorbereitet habe \ud83d\ude42<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/3a7dab707dd1defb9bc674a342052a03.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Lassen Sie uns mit dem umgekehrten Index beginnen. Viele k\u00f6nnten denken, dass dies einfach ist. Wer wei\u00df, was ein umgekehrter Index ist und wie er funktioniert? Oh, es sind nicht mehr so viele Leute. Lassen Sie uns versuchen zu verstehen, was es ist. <\/p>\n<p><\/p>\n<p>Eigentlich ist alles ganz einfach. Es handelt sich um ein W\u00f6rterbuch, das Schl\u00fcssel auf Werte abbildet. Was ist ein Schl\u00fcssel? Dieses Paar <code>label=value<\/code>, wobei <code>Bezeichnung<\/code> und <code>value<\/code> ist ein String-Paar. Und die Werte sind eine Menge <code>timeseries_ids<\/code>, die das gegebene Paar enth\u00e4lt. <code>label=value<\/code>.<\/p>\n<p><\/p>\n<p>Der umgekehrte Index erm\u00f6glicht ein schnelles Auffinden aller <code>timeseries_ids<\/code>, die bestimmte enthalten. <code>label=value<\/code>.<\/p>\n<p><\/p>\n<p>Au\u00dferdem erm\u00f6glicht er das schnelle Finden von <code>timeseries_ids<\/code> Zeitreihen f\u00fcr mehrere Paare <code>label=value<\/code>, oder f\u00fcr Paare <code>label=regexp<\/code>. Wie geschieht das? Durch das Finden der Schnittmenge der Menge <code>timeseries_ids<\/code> f\u00fcr jedes Paar. <code>label=value<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/837a15a078e8c9422c721f3f072c0c09.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Betrachten wir verschiedene Implementierungen eines Inverted Index. Beginnen wir mit der einfachsten naiven Implementierung. Sie sieht so aus. <\/p>\n<p><\/p>\n<p>Die Funktion <code>getMetricIDs<\/code> erh\u00e4lt eine Liste von Zeilen. Jede Zeile enth\u00e4lt <code>label=value<\/code>. Diese Funktion gibt eine Liste zur\u00fcck <code>metricIDs<\/code>.<\/p>\n<p><\/p>\n<p>Wie funktioniert das? Hier haben wir eine globale Variable, die genannt wird <code>invertedIndex<\/code>. Das ist ein gew\u00f6hnliches W\u00f6rterbuch (<code>map<\/code>), das eine Zeichenfolge in eine Slice von int-Werten abbildet. Die Zeichenfolge enth\u00e4lt <code>label=value<\/code>.<\/p>\n<p><\/p>\n<p>Implementierung der Funktion: wir holen <code>metricIDs<\/code> f\u00fcr das erste <code>label=value<\/code>, dann gehen wir alle anderen durch <code>label=value<\/code>, holen wir <code>metricIDs<\/code> f\u00fcr sie. Und wir rufen die Funktion <code>intersectInts<\/code>, \u00fcber die sp\u00e4ter gesprochen wird. Diese Funktion gibt die Schnittmenge dieser Listen zur\u00fcck.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/709beca79884a9693098781974c26f4d.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Wie Sie sehen, ist die Implementierung des Inverted Index nicht besonders kompliziert. Aber es ist eine naive Implementierung. Welche Nachteile hat sie? Der Hauptnachteil der naiven Implementierung besteht darin, dass ein solcher Inverted Index im Arbeitsspeicher gespeichert wird. Nach dem Neustart der Anwendung verlieren wir diesen Index. Es gibt keine Speicherung dieses Index auf der Festplatte. F\u00fcr eine Datenbank ist ein solcher Inverted Index wahrscheinlich ungeeignet.<\/p>\n<p><\/p>\n<p>Ein weiteres Problem h\u00e4ngt ebenfalls mit dem Speicher zusammen. Der invertierte Index muss im Arbeitsspeicher abgelegt werden. Wenn er den Arbeitsspeicher \u00fcbersteigt, erhalten wir einen Out-of-Memory-Fehler. In diesem Fall wird das Programm nicht funktionieren.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/495394168ac03aa8cbcf56ccad1cb2b8.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Dieses Problem kann mit fertigen L\u00f6sungen wie <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/google\/leveldb\">LevelDB<\/a><\/noindex>, oder <noindex><a rel=\"nofollow\" href=\"https:\/\/rocksdb.org\/\">RocksDB<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>Zusammengefasst ben\u00f6tigen wir eine Datenbank, die schnell drei Operationen erm\u00f6glicht. <\/p>\n<p><\/p>\n<ul>\n<li>Die erste Operation ist das Einf\u00fcgen <code>Schl\u00fcssel-Wert-Paar<\/code> in diese Datenbank. Dies geschieht sehr schnell, wobei <code>Schl\u00fcssel-Wert-Paar<\/code> willk\u00fcrliche Zeichenfolgen sind. <\/li>\n<li>Die zweite Operation ist die schnelle Suche nach einem Wert anhand eines angegebenen Schl\u00fcssels.<\/li>\n<li>Und die dritte Operation ist die schnelle Suche nach allen Werten anhand eines angegebenen Pr\u00e4fixes. <\/li>\n<\/ul>\n<p><\/p>\n<p>LevelDB und RocksDB \u2013 diese Datenbanken wurden bei Google und Facebook entwickelt. Zuerst gab es LevelDB. Dann haben die Entwickler von Facebook LevelDB \u00fcbernommen und damit begonnen, RocksDB zu verbessern. Heute laufen fast alle internen Datenbanken bei Facebook auf RocksDB, einschlie\u00dflich der Migration von MySQL zu RocksDB. Sie haben es genannt <noindex><a rel=\"nofollow\" href=\"http:\/\/myrocks.io\/\">MyRocks<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>Der invertierte Index kann mit LevelDB implementiert werden. Wie geht das? Wir speichern als Schl\u00fcssel <code>label=value<\/code>. Und als Wert den Identifier der Zeitreihe, in der das Paar vorhanden ist. <code>label=value<\/code>.<\/p>\n<p><\/p>\n<p>Wenn wir viele Zeitreihen mit diesem Paar haben <code>label=value<\/code>, dann gibt es viele Zeilen in dieser Datenbank mit demselben Schl\u00fcssel und unterschiedlichen <code>timeseries_ids<\/code>. Um eine Liste aller <code>timeseries_ids<\/code>, die mit diesem beginnen <code>label=prefix<\/code>, f\u00fchren wir einen Bereichsscan durch, f\u00fcr den diese Datenbank optimiert ist. Das hei\u00dft, wir w\u00e4hlen alle Zeilen aus, die mit <code>label=prefix<\/code> beginnen und erhalten die ben\u00f6tigten <code>timeseries_ids<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/a40877ecb758ac3bc1979bcd568d7e83.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Hier ist eine ungef\u00e4hre Implementierung, wie sie in Go aussehen w\u00fcrde. Wir haben einen invertierten Index. Das ist LevelDB.<\/p>\n<p><\/p>\n<p>Die Funktion ist dieselbe wie f\u00fcr die na\u00efve Implementierung. Sie spiegelt die na\u00efve Implementierung fast zeilenweise wider. Der einzige Unterschied besteht darin, dass wir anstelle eines Zugriffs auf <code>map<\/code> auf den invertierten Index zugreifen. Wir ziehen alle Werte f\u00fcr das erste <code>label=value<\/code>. Dann gehen wir alle verbleibenden Paare durch <code>label=value<\/code> und holen die entsprechenden Mengen von metricIDs f\u00fcr sie. Anschlie\u00dfend finden wir die Schnittmenge. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/de11b15286816ada04b22727ff78e43d.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Es scheint alles gut zu sein, aber es gibt Nachteile in dieser L\u00f6sung. VictoriaMetrics implementierte zun\u00e4chst den invertierten Index auf Basis von LevelDB. Letztendlich musste man jedoch davon absehen.<\/p>\n<p><\/p>\n<p>Warum? Weil LevelDB langsamer ist als die na\u00efve Implementierung. In der na\u00efven Implementierung ziehen wir sofort den gesamten Slice f\u00fcr den angegebenen Schl\u00fcssel. <code>metricIDs<\/code>. Diese sehr schnelle Operation \u2013 der gesamte Slice ist einsatzbereit.<\/p>\n<p><\/p>\n<p>In LevelDB hingegen muss bei jedem Funktionsaufruf <code>GetValues<\/code> durch alle Zeilen gegangen werden, die mit <code>label=value<\/code>beginnen. Und f\u00fcr jede Zeile muss der Wert extrahiert werden <code>timeseries_ids<\/code>. Aus diesen <code>timeseries_ids<\/code> wird ein Slice dieser <code>timeseries_ids<\/code>. \u041e\u0447\u0435\u0432\u0438\u0434\u043d\u043e, \u0447\u0442\u043e \u044d\u0442\u043e \u043d\u0430\u043c\u043d\u043e\u0433\u043e \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u0435\u0439, \u0447\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u043e \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0435 \u043a \u043e\u0431\u044b\u0447\u043d\u043e\u043c\u0443 map&#8217;\u0443 \u043f\u043e \u043a\u043b\u044e\u0447\u0443.<\/p>\n<p><\/p>\n<p>Ein weiterer Nachteil ist, dass LevelDB in C geschrieben ist. Der Zugriff auf C-Funktionen aus Go ist nicht besonders schnell. Er dauert Hunderte von Nanosekunden. Das ist nicht sehr schnell, da im Vergleich zu einem normalen Funktionsaufruf, der in Go geschrieben ist und 1-5 Nanosekunden dauert, der Leistungsunterschied mehrere Zehnerfache betr\u00e4gt. F\u00fcr VictoriaMetrics war das ein fataler Nachteil \ud83d\ude42<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/47cace825807b7b062064c2e790dc9d3.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Deshalb habe ich meine eigene Implementierung eines invertierten Index geschrieben. Ich nannte sie <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/VictoriaMetrics\/VictoriaMetrics\/tree\/master\/lib\/mergeset\">mergeset.<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>Mergeset basiert auf der Datenstruktur MergeTree. Diese Datenstruktur stammt von ClickHouse. Offensichtlich muss mergeset f\u00fcr schnelle Suchen <code>timeseries_ids<\/code> nach einem bestimmten Schl\u00fcssel optimiert sein. Mergeset ist vollst\u00e4ndig in Go geschrieben. Sie k\u00f6nnen die <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/VictoriaMetrics\/VictoriaMetrics\">Quellcodes von VictoriaMetrics auf GitHub<\/a><\/noindex>einsehen. Die Implementierung von mergeset befindet sich im Ordner <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/VictoriaMetrics\/VictoriaMetrics\/tree\/master\/lib\/mergeset\">\/lib\/mergeset<\/a><\/noindex>. Sie k\u00f6nnen versuchen, herauszufinden, was dort passiert.<\/p>\n<p><\/p>\n<p>Die API-Mergeset ist sehr \u00e4hnlich wie LevelDB und RocksDB. Das bedeutet, dass es erm\u00f6glicht, neue Eintr\u00e4ge schnell zu speichern und schnell Eintr\u00e4ge anhand eines bestimmten Pr\u00e4fixes auszuw\u00e4hlen.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/845704ed36f70beb468907d22701af59.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>\u00dcber die Nachteile von Mergeset werden wir sp\u00e4ter sprechen. Jetzt besprechen wir die Probleme, die bei der Implementierung des invertierten Index in VictoriaMetrics in der Produktion aufgetreten sind.<\/p>\n<p><\/p>\n<p>Warum sind sie aufgetreten?<\/p>\n<p><\/p>\n<p>Der erste Grund ist die hohe Fluktuation. Dies bedeutet, dass es h\u00e4ufige Wechsel zwischen Zeitserien gibt. Wenn eine Zeitreihe endet und eine neue beginnt oder viele neue Zeitserien anfangen. Und das geschieht oft.<\/p>\n<p><\/p>\n<p>Der zweite Grund ist die gro\u00dfe Anzahl an Zeitserien. Zun\u00e4chst, als das Monitoring popul\u00e4r wurde, war die Anzahl der Zeitserien gering. Zum Beispiel muss die Auslastung von CPU, Speicher, Netzwerk und Festplatte f\u00fcr jeden Computer \u00fcberwacht werden. 4 Zeitserien pro Computer. Angenommen, Sie haben 100 Computer und 400 Zeitserien. Das ist sehr wenig. <\/p>\n<p><\/p>\n<p>Im Laufe der Zeit haben die Menschen herausgefunden, dass man detailliertere Informationen messen kann. Zum Beispiel kann man nicht nur die Auslastung des gesamten Prozessors, sondern auch die jedes einzelnen Prozessorkerns messen. Wenn Sie 40 Prozessorkerne haben, stehen Ihnen entsprechend 40-mal mehr Zeitreihen zur Verf\u00fcgung, um die Belastung des Prozessors zu messen. <\/p>\n<p><\/p>\n<p>Aber das ist noch nicht alles. Jeder Prozessorkern kann mehrere Zust\u00e4nde haben, wie zum Beispiel idle, wenn er inaktiv ist. Dazu z\u00e4hlen auch die Nutzung im User-Space, die Arbeit im Kernel-Space und andere Zust\u00e4nde. Und jeder dieser Zust\u00e4nde kann ebenfalls als separate Zeitreihe gemessen werden. Dies erh\u00f6ht die Anzahl der Zeitreihen um das 7- bis 8-fache.<\/p>\n<p><\/p>\n<p>Aus einer einzigen Metrik haben wir 40 x 8 = 320 Metriken nur f\u00fcr einen Computer erhalten. Multiplizieren wir das mit 100, erhalten wir 32.000 statt 400. <\/p>\n<p><\/p>\n<p>Dann kam Kubernetes. Und es wurde sogar komplizierter, da in Kubernetes viele verschiedene Dienste gehostet werden k\u00f6nnen. Jeder Dienst in Kubernetes besteht aus vielen Pods, und all dies muss \u00fcberwacht werden. Dar\u00fcber hinaus erfolgt st\u00e4ndig der Rollout neuer Versionen Ihrer Dienste. F\u00fcr jede neue Version m\u00fcssen neue Zeitreihen erstellt werden. Infolgedessen w\u00e4chst die Anzahl der Zeitreihen exponentiell, und wir stehen vor dem Problem einer gro\u00dfen Anzahl von Zeitreihen, das als High-Cardinality bekannt ist. VictoriaMetrics bew\u00e4ltigt dies im Vergleich zu anderen Zeitreihendatenbanken erfolgreich. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/c11b7ce6d3294ae420243f72636af4b9.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Lassen Sie uns den hohen Churn-Rate genauer betrachten. Was verursacht eine hohe Churn-Rate in der Produktion? Weil einige Werte von Labels und Tags sich st\u00e4ndig \u00e4ndern.<\/p>\n<p><\/p>\n<p>Nehmen wir beispielsweise Kubernetes, wo es das Konzept gibt <code>deployment<\/code>, \u0442. \u0435. \u043a\u043e\u0433\u0434\u0430 \u0432\u044b\u043a\u0430\u0442\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043d\u043e\u0432\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 Kubernetes \u043f\u043e\u0447\u0435\u043c\u0443-\u0442\u043e \u0440\u0435\u0448\u0438\u043b\u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c id\u2019\u0448\u043a\u0443 deployment&#8217;\u0430 \u0432 label.<\/p>\n<p><\/p>\n<p>\u041a \u0447\u0435\u043c\u0443 \u044d\u0442\u043e \u043f\u0440\u0438\u0432\u0435\u043b\u043e? \u041a \u0442\u043e\u043c\u0443, \u0447\u0442\u043e \u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u043d\u043e\u0432\u043e\u043c deployment&#8217;\u0435 \u0443 \u043d\u0430\u0441 \u0432\u0441\u0435 \u0441\u0442\u0430\u0440\u044b\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0440\u044f\u0434\u044b \u043f\u0440\u0435\u0440\u044b\u0432\u0430\u044e\u0442\u0441\u044f, \u0430 \u0432\u043c\u0435\u0441\u0442\u043e \u043d\u0438\u0445 \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0442\u0441\u044f \u043d\u043e\u0432\u044b\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0440\u044f\u0434\u044b \u0441 \u043d\u043e\u0432\u044b\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c \u043b\u0435\u0439\u0431\u043b\u0430 <code>deployment_id<\/code>. Solche Zeitreihen k\u00f6nnen Hunderttausende oder sogar Millionen betragen.<\/p>\n<p><\/p>\n<p>Eine wichtige Eigenschaft dabei ist, dass die Gesamtzahl der Zeitreihen w\u00e4chst, aber die Anzahl der momentan aktiven Zeitreihen, f\u00fcr die Daten empfangen werden, konstant bleibt. Ein solcher Zustand wird als \u2013 hoher Wechselraten bezeichnet.<\/p>\n<p><\/p>\n<p>Das Hauptproblem der hohen Wechselrate besteht darin, eine konstante Suchgeschwindigkeit f\u00fcr alle Zeitreihen anhand eines bestimmten Satzes von Labels \u00fcber einen bestimmten Zeitraum zu gew\u00e4hrleisten. Normalerweise ist dies der Zeitraum der letzten Stunde oder des letzten Tages. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/ac18482b87cc37624d163b30c68cf7be.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Wie kann man dieses Problem l\u00f6sen? Hier ist die erste M\u00f6glichkeit. Man teilt den umgekehrten Index in unabh\u00e4ngige Teile nach Zeit. D. h. ein bestimmter Zeitraum vergeht, wir beenden die Arbeit mit dem aktuellen umgekehrten Index. Und erstellen einen neuen umgekehrten Index. Ein weiterer Zeitraum vergeht, wir erstellen einen weiteren und noch einen. <\/p>\n<p><\/p>\n<p>Und bei der Abfrage dieser umgekehrten Indizes finden wir eine Reihe von umgekehrten Indizes, die in den angegebenen Zeitraum fallen. Und entsprechend w\u00e4hlen wir die IDs der Zeitreihen daraus aus. <\/p>\n<p><\/p>\n<p>Das spart Ressourcen, da wir keine Teile durchsehen m\u00fcssen, die nicht in den angegebenen Zeitraum fallen. Das bedeutet, wenn wir Daten f\u00fcr die letzte Stunde ausw\u00e4hlen, \u00fcberspringen wir normalerweise die Anfragen f\u00fcr die vorherigen Zeitintervalle. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/1afa3a88caa7423941ae3494b9cec706.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Es gibt eine weitere L\u00f6sung f\u00fcr dieses Problem. Diese besteht darin, f\u00fcr jeden Tag eine separate Liste von IDs der Zeitreihen zu speichern, die an diesem Tag aufgetreten sind.<\/p>\n<p><\/p>\n<p>Der Vorteil dieser L\u00f6sung im Vergleich zur vorherigen liegt darin, dass wir keine Informationen \u00fcber Zeitreihen duplizieren, die im Laufe der Zeit nicht verschwinden. Sie sind konstant vorhanden und \u00e4ndern sich nicht. <\/p>\n<p><\/p>\n<p>Der Nachteil dabei ist, dass eine solche L\u00f6sung komplexer umzusetzen und schwieriger zu debuggen ist. Au\u00dferdem hat VictoriaMetrics diese L\u00f6sung gew\u00e4hlt, was historisch bedingt ist. Diese L\u00f6sung schneidet ebenfalls gut ab im Vergleich zur vorherigen. Der Grund daf\u00fcr ist, dass diese L\u00f6sung nicht umgesetzt wurde, weil man die Daten in jeder Partition f\u00fcr Zeitreihen duplizieren muss, die sich nicht \u00e4ndern, d. h. die im Laufe der Zeit nicht verschwinden. VictoriaMetrics wurde in erster Linie auf den Verbrauch von Speicherplatz optimiert, und die vorherige Implementierung verschlechterte diesen. Diese Umsetzung eignet sich besser zur Minimierung des Speicherplatzverbrauchs, weshalb sie gew\u00e4hlt wurde. <\/p>\n<p><\/p>\n<p>Wir mussten uns damit auseinandersetzen. Der Kampf bestand darin, dass in dieser Implementierung immer noch eine viel gr\u00f6\u00dfere Menge <code>timeseries_ids<\/code> an Daten ausgew\u00e4hlt werden muss als wenn der invertierte Index zeitlich getrennt ist.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/5b10e61aaa803428ed3bfe31815f09d3.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Wie haben wir dieses Problem gel\u00f6st? Wir haben es auf originelle Weise gel\u00f6st \u2013 indem wir mehrere Identifikatoren von Zeitreihen in jedem Eintrag des invertierten Index anstelle eines einzigen Identifikators gespeichert haben. D. h. wir haben einen Schl\u00fcssel <code>label=value<\/code>, der in jeder Zeitreihe vorkommt. Und jetzt speichern wir mehrere <code>timeseries_ids<\/code> in einem Datensatz.<\/p>\n<p><\/p>\n<p>Hier ist ein Beispiel. Fr\u00fcher hatten wir N Datens\u00e4tze, und jetzt haben wir einen Datensatz, dessen Pr\u00e4fix mit dem aller anderen \u00fcbereinstimmt. Der vorherige Datensatz enthielt alle IDs der Zeitreihen. <\/p>\n<p><\/p>\n<p>Dadurch konnte die Geschwindigkeit des Scans eines solchen invertierten Indexes um das 10-fache gesteigert werden. Au\u00dferdem wurde der Speicherverbrauch f\u00fcr den Cache reduziert, da wir jetzt die Zeile <code>label=value<\/code> nur einmal im Cache speichern, statt N-mal. Und diese Zeile kann gro\u00df sein, wenn in Ihren Tags und Labels lange Zeilen gespeichert werden, die Kubernetes gerne dort unterbringt.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/ae4f38a440203bad2d03cf2abd1faafe.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Eine weitere M\u00f6glichkeit, die Suche \u00fcber den invertierten Index zu beschleunigen, ist das Sharding. Die Erstellung mehrerer invertierter Indizes anstelle eines einzelnen und das Sharding der Daten zwischen ihnen nach Schl\u00fcssel. Dies ist eine Sammlung <code>key=value<\/code> Das bedeutet, dass wir mehrere unabh\u00e4ngige invertierte Indizes erzeugen, die wir parallel auf mehreren Prozessoren abfragen k\u00f6nnen. Fr\u00fchere Implementierungen erlaubten nur den Betrieb im Einprozessor-Modus, das hei\u00dft, die Daten konnten nur auf einem Kern gescannt werden. Diese L\u00f6sung erm\u00f6glicht es, Daten gleichzeitig auf mehreren Kernen zu scannen, so wie es ClickHouse bevorzugt. Das planen wir umzusetzen.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/5ced6446efbf8ded8d7fb91694a999b0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Kommen wir nun zu unserem Thema \u2013 der Schnittmengenfunktion <code>timeseries_ids<\/code>. Lassen Sie uns anschauen, welche Implementierungen m\u00f6glich sind. Diese Funktion erm\u00f6glicht es, <code>timeseries_ids<\/code> f\u00fcr eine gegebene Menge <code>label=value<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/073a155018a91120c6996c324772f9af.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Die erste Variante ist eine naive Implementierung. Zwei verschachtelte Schleifen. Hier erhalten wir als Eingabe der Funktionen <code>intersectInts<\/code> zwei Slices \u2014 <code>a<\/code> und <code>b<\/code>. Im Ergebnis sollte sie uns die Schnittmenge dieser Slices zur\u00fcckgeben.<\/p>\n<p><\/p>\n<p>Die naive Implementierung sieht so aus. Wir gehen alle Werte aus dem Slice <code>a<\/code>durch, und innerhalb dieser Schleife gehen wir alle Werte aus dem Slice <code>b<\/code>durch. Und vergleichen sie. Wenn sie \u00fcbereinstimmen, haben wir die Schnittmenge gefunden. Und speichern sie in <code>result<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/b89dcbb4f6d73377f9cf4f50169d6dd7.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Welche Nachteile gibt es? Die quadratische Komplexit\u00e4t ist ihr gr\u00f6\u00dfter Nachteil. Beispielsweise, wenn Sie die Gr\u00f6\u00dfen der Slices haben. <code>a<\/code> und <code>b<\/code> Bei einer Million w\u00fcrde diese Funktion Ihnen niemals eine Antwort zur\u00fcckgeben. Denn sie m\u00fcsste eine Billion Iterationen durchf\u00fchren, was selbst f\u00fcr moderne Computer sehr viel ist.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/bad26b0092a2fd7fc813bcb79a9138ce.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Die zweite Implementierung basiert auf einer Map. Wir erstellen eine Map und f\u00fcgen alle Werte aus dem Slice hinzu. <code>a<\/code>Anschlie\u00dfend durchlaufen wir das Slice mit einer separaten Schleife. <code>b<\/code>Und \u00fcberpr\u00fcfen, ob dieser Wert aus dem Slice <code>b<\/code> in der Map vorhanden ist. Wenn ja, f\u00fcgen wir ihn zum Ergebnis hinzu. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/1e610acf641a9cfe4c80aac1fe26044c.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Welche Vorteile bietet das? Der Vorteil liegt darin, dass hier nur eine lineare Komplexit\u00e4t besteht. Das hei\u00dft, die Funktion wird f\u00fcr gro\u00dfe Gr\u00f6\u00dfen des Slices viel schneller ausgef\u00fchrt. F\u00fcr eine Slice-Gr\u00f6\u00dfe von einer Million wird diese Funktion in zwei Millionen Iterationen durchgef\u00fchrt, im Gegensatz zu einer Billion Iterationen wie in der vorherigen Funktion.<\/p>\n<p><\/p>\n<p>Ein Nachteil ist jedoch, dass diese Funktion mehr Speicher ben\u00f6tigt, um diese Map zu erstellen.<\/p>\n<p><\/p>\n<p>Ein weiterer Nachteil ist der hohe Overhead beim Hashing. Dieser Nachteil ist nicht sehr offensichtlich. Und auch f\u00fcr uns war er nicht sehr offensichtlich, weshalb die Implementierung der Schnittmenge (intersection) in VictoriaMetrics zun\u00e4chst \u00fcber eine Map erfolgte. Das Profiling zeigte jedoch sp\u00e4ter, dass die meiste CPU-Zeit f\u00fcr das Schreiben in die Map und die \u00dcberpr\u00fcfung der Existenz eines Wertes in dieser Map aufgewendet wird.<\/p>\n<p><\/p>\n<p>Warum wird in diesen Bereichen CPU-Zeit verbraucht? Weil Go in diesen Zeilen eine Hash-Berechnung durchf\u00fchrt. Das bedeutet, es berechnet den Hash des Schl\u00fcssels, um dann \u00fcber den angegebenen Index im HashMap zuzugreifen. Die Berechnung des Hashs erfolgt in nur wenigen Nanosekunden. Das ist f\u00fcr VictoriaMetrics langsam.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/969f10d875d88998e958894ca28b1181.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Ich habe beschlossen, ein f\u00fcr diesen Fall optimiertes Bitset zu implementieren. So sieht jetzt die Schnittstelle zwischen zwei Slices aus. Hier erstellen wir ein Bitset. Wir f\u00fcgen ihm Elemente aus dem ersten Slice hinzu. Dann \u00fcberpr\u00fcfen wir, ob diese Elemente im zweiten Slice vorhanden sind. Und wir f\u00fcgen sie dem Ergebnis hinzu. Das hei\u00dft, es unterscheidet sich fast nicht von dem vorherigen Beispiel. Lediglich haben wir hier den Zugriff auf die Map durch benutzerdefinierte Funktionen ersetzt. <code>add<\/code> und <code>has<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/b4ec0753d30d9684868a031f605632c0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Auf den ersten Blick scheint es, als ob das langsamer arbeiten sollte, wenn zuvor eine Standard-Map verwendet wurde und hier zus\u00e4tzliche Funktionen aufgerufen werden. Aber das Profiling zeigt, dass dieses System zehnmal schneller ist als die Standard-Map f\u00fcr den Fall von VictoriaMetrics.<\/p>\n<p><\/p>\n<p>Dar\u00fcber hinaus ben\u00f6tigt es viel weniger Speicher im Vergleich zur Implementierung mit Map. Denn hier speichern wir Bits anstelle von acht-Bit-Werten.<\/p>\n<p><\/p>\n<p>Ein Nachteil dieser Implementierung ist, dass sie nicht so offensichtlich und nicht trivial ist. <\/p>\n<p><\/p>\n<p>Ein weiterer Nachteil, der vielen m\u00f6glicherweise nicht auff\u00e4llt, ist, dass diese Implementierung in bestimmten F\u00e4llen schlecht funktionieren kann. Das bedeutet, dass sie f\u00fcr einen spezifischen Anwendungsfall optimiert ist, n\u00e4mlich f\u00fcr den Fall des \u00dcberschneidens von IDs in den Zeitreihen von VictoriaMetrics. Das hei\u00dft nicht, dass sie f\u00fcr alle F\u00e4lle geeignet ist. Wenn sie falsch angewendet wird, kann dies nicht zu einer Leistungssteigerung, sondern zu einem Out-of-Memory-Fehler und einer Verlangsamung der Leistung f\u00fchren. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/94bd174afe43e67b3cf279dc7a1be17c.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Betrachten wir die Implementierung dieser Struktur. Wenn Sie sehen m\u00f6chten, finden Sie sie im Quellcode von VictoriaMetrics, im Ordner <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/VictoriaMetrics\/VictoriaMetrics\/tree\/master\/lib\/uint64set\">lib\/uint64set<\/a><\/noindex>. Sie ist speziell f\u00fcr den Fall von VictoriaMetrics optimiert, bei dem <code>timeseries_id<\/code> ein 64-Bit-Wert darstellt, bei dem die ersten 32 Bit konstant sind und nur die letzten 32 Bit variieren.<\/p>\n<p><\/p>\n<p>Diese Datenstruktur wird nicht auf der Festplatte gespeichert; sie arbeitet nur im Speicher. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/e24ca65f860e2b04037e073f7acae0c1.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Hier ist ihr API. Es ist nicht sehr kompliziert. Das API ist genau auf das spezifische Beispiel der Verwendung in VictoriaMetrics abgestimmt. Das hei\u00dft, hier gibt es keine unn\u00f6tigen Funktionen. Es gibt hier nur die Funktionen, die VictoriaMetrics eindeutig verwendet.<\/p>\n<p><\/p>\n<p>Es gibt Funktionen <code>add<\/code>, die neue Werte hinzuf\u00fcgen. Es gibt eine Funktion <code>has<\/code>, die neue Werte \u00fcberpr\u00fcft. Es gibt auch eine Funktion <code>del<\/code>, die Werte entfernt. Es gibt eine Hilfsfunktion <code>len<\/code>, die die Gr\u00f6\u00dfe der Menge zur\u00fcckgibt. Funktion <code>clone<\/code> klont die Menge. Und Funktion <code>appendto<\/code> konvertiert diese Menge in einen Slice <code>timeseries_ids<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/874b04042d13f342d39f8f393e7fb78c.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>So sieht die Implementierung dieser Datenstruktur aus. In der Menge gibt es zwei Elemente:<\/p>\n<p><\/p>\n<ul>\n<li>\n<p><code>ItemsCount<\/code> \u2013 ein Hilfsfeld, um schnell die Anzahl der Elemente in der Menge zur\u00fcckzugeben. Man k\u00f6nnte auf dieses Hilfsfeld verzichten, aber es wurde hier hinzugef\u00fcgt, weil VictoriaMetrics h\u00e4ufig die L\u00e4nge des Bitsets in seinen Algorithmen abfragt.<\/p>\n<p>\n<\/li>\n<li>\n<p>Das zweite Feld ist <code>buckets<\/code>. Dies ist ein Slice der Struktur <code>bucket32<\/code>. In jeder Struktur wird gespeichert <code>hi<\/code> Feld. Dies sind die oberen 32 Bits. Und zwei Slices \u2014 <code>b16his<\/code> und <code>buckets<\/code> von <code>bucket16<\/code> Strukturen. <\/p>\n<p>\n<\/li>\n<\/ul>\n<p><\/p>\n<p>Hier werden die oberen 16 Bits des zweiten Teils der 64-Bit-Struktur gespeichert. Und hier werden die Bitsets f\u00fcr die unteren 16 Bits jedes Bytes gespeichert. <\/p>\n<p><\/p>\n<p><code>Bucket64<\/code> besteht aus einem Array <code>uint64<\/code>. Die L\u00e4nge wird mit diesen Konstanten berechnet. In einer <code>bucket16<\/code> kann maximal <code>2^16=65536<\/code> Bits gespeichert werden. Teilt man das durch 8, erh\u00e4lt man 8 Kilobyte. Teilt man es nochmal durch 8, ergibt das 1000 <code>uint64<\/code> Wert. Das hei\u00dft, <code>Bucket16<\/code> \u2013 das ist eine 8-Kilobyte-Struktur. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/1cca4e6bfafa3408a3c95e0118e26d80.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Lassen Sie uns betrachten, wie eine der Methoden dieser Struktur zur Hinzuf\u00fcgung eines neuen Wertes implementiert ist. <\/p>\n<p><\/p>\n<p>Alles beginnt mit <code>uint64<\/code> dem Wert. Wir berechnen die oberen 32 Bit, berechnen die unteren 32 Bit. Wir durchlaufen alle <code>buckets<\/code>. Wir vergleichen die oberen 32 Bit in jedem Bucket mit dem hinzuzuf\u00fcgenden Wert. Wenn sie \u00fcbereinstimmen, rufen wir die Funktion <code>add<\/code> in der Struktur b32 <code>buckets<\/code>. Und f\u00fcgen die unteren 32 Bit dort hinzu. Wenn dies <code>true<\/code>zur\u00fcckgibt, bedeutet das, dass wir diesen Wert hinzugef\u00fcgt haben und wir ihn nicht hatten. Wenn es <code>false<\/code>zur\u00fcckgibt, dann war dieser Wert bereits vorhanden. Dann erh\u00f6hen wir die Anzahl der Elemente in der Struktur. <\/p>\n<p><\/p>\n<p>Wenn wir den erforderlichen <code>bucket<\/code> mit dem passenden hi-Wert nicht gefunden haben, rufen wir die Funktion <code>addAlloc<\/code>, die einen neuen <code>bucket<\/code>zuteilt, indem sie ihn zur Bucket-Struktur hinzuf\u00fcgt.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/c5d1e5de767f47c7c40e01e12b6d0617.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Dies ist die Implementierung der Funktion <code>b32.add<\/code>. Sie \u00e4hnelt der vorherigen Implementierung. Wir berechnen die oberen 16 Bit, die unteren 16 Bit.<\/p>\n<p><\/p>\n<p>Dann durchlaufen wir alle oberen 16 Bit. Finden \u00dcbereinstimmungen. Und bei einer \u00dcbereinstimmung rufen wir die Methode add auf, die wir auf der n\u00e4chsten Seite f\u00fcr <code>bucket16<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/e98c4ef316f3a894fd7019c3c85696f7.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>beleuchten werden. Und dies ist die unterste Ebene, die maximal optimiert werden sollte. Wir berechnen f\u00fcr <code>uint64<\/code> die id den Wert im Slice-Bit sowie <code>den Bitmasken.<\/code>. Diese Maske f\u00fcr den 64-Bit-Wert erm\u00f6glicht es, das Vorhandensein eines bestimmten Bits zu \u00fcberpr\u00fcfen oder es zu setzen. Wir pr\u00fcfen, ob dieses Bit gesetzt ist, setzen es gegebenenfalls und geben zur\u00fcck, ob es vorhanden ist. Diese Implementierung hat es uns erm\u00f6glicht, die Schnittstellenoperation von Zeitreihen-IDs im Vergleich zu herk\u00f6mmlichen Maps um das Zehnfache zu beschleunigen.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/4dfa5a6142829a3551be22d6c9c3ab92.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Neben dieser Optimierung bietet VictoriaMetrics noch viele weitere Optimierungen. Die meisten dieser Optimierungen wurden nicht ohne Grund hinzugef\u00fcgt, sondern nach der Profilerstellung des Codes in der Produktion.<\/p>\n<p><\/p>\n<p>Die wichtigste Regel bei der Optimierung lautet: keine Optimierungen hinzuf\u00fcgen, nur weil man annimmt, dass hier ein Engpass besteht, denn es k\u00f6nnte sich herausstellen, dass dort kein Engpass existiert. Optimierungen verschlechtern normalerweise die Codequalit\u00e4t. Daher sollte man nur nach der Profilerstellung optimieren und idealerweise in der Produktion, um reale Daten zu verwenden. Wer interessiert ist, kann den Quellcode von VictoriaMetrics einsehen und die anderen Optimierungen dort studieren.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-Optimierungen in VictoriaMetrics. Alexander Valyalkin\" src=\"\/wp-content\/uploads\/2020\/05\/7585c4dc782627bac5b649e6a55e2ffb.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><em>Ich habe eine Frage zu bitset. Es \u00e4hnelt sehr der Implementierung von C++ vector, einem optimierten bitset. Haben Sie die Implementierung von dort \u00fcbernommen?<\/em><\/p>\n<p><\/p>\n<p>Nein, nicht von dort. Bei der Implementierung dieses Bitsets habe ich mich an das Wissen \u00fcber die Struktur dieser IDs in Time Series orientiert, die in VictoriaMetrics verwendet werden. Ihre Struktur sieht vor, dass die oberen 32 Bits im Wesentlichen konstant sind. Die unteren 32 Bits k\u00f6nnen sich \u00e4ndern. Je geringer das Bit, desto h\u00e4ufiger kann es sich \u00e4ndern. Daher ist diese Implementierung speziell auf diese Datenstruktur optimiert. Soweit ich wei\u00df, ist die C++-Implementierung auf den allgemeinen Fall optimiert. Eine Optimierung f\u00fcr den allgemeinen Fall bedeutet, dass sie in einem spezifischen Fall nicht die optimale L\u00f6sung sein wird.<\/p>\n<p><\/p>\n<p>Ich empfehle Ihnen au\u00dferdem, den Vortrag von Alexey Milovid zu betrachten. Er hat vor etwa einem Monat \u00fcber Optimierungen in ClickHouse f\u00fcr spezifische Spezialisierungen gesprochen. Er erl\u00e4utert gerade, dass die C++-Implementierung oder eine andere Implementierung im Allgemeinen darauf ausgelegt sind, im Durchschnitt gut zu funktionieren. Sie kann schlechter abschneiden als eine spezialisierte Implementierung, die auf spezifisches Wissen zugeschnitten ist, wie bei uns, wenn wir wissen, dass die oberen 32 Bits im Wesentlichen konstant sind.<\/p>\n<p><\/p>\n<p><em>Ich habe eine zweite Frage. Was ist der grundlegende Unterschied zu InfluxDB?<\/em><\/p>\n<p><\/p>\n<p>Es gibt viele grundlegende Unterschiede. In Bezug auf Leistung und Speicherverbrauch zeigt InfluxDB in Tests einen zehnmal h\u00f6heren Speicherverbrauch bei hochgradigen Zeitreihen, insbesondere wenn sie zahlreich sind, zum Beispiel Millionen. Zum Beispiel ben\u00f6tigt VictoriaMetrics 1 GB f\u00fcr eine Million aktiver Reihen, w\u00e4hrend InfluxDB dabei 10 GB ben\u00f6tigt. Das ist ein erheblicher Unterschied. <\/p>\n<p><\/p>\n<p>Ein weiterer wesentlicher Unterschied besteht darin, dass InfluxDB eigenartige Abfragesprachen \u2013 Flux und InfluxQL \u2013 verwendet. Diese sind im Vergleich zu <noindex><a rel=\"nofollow\" href=\"https:\/\/medium.com\/@valyala\/promql-tutorial-for-beginners-9ab455142085\">PromQL<\/a><\/noindex>PromQL, der in VictoriaMetrics unterst\u00fctzt wird, nicht besonders benutzerfreundlich f\u00fcr die Arbeit mit Zeitreihen.<\/p>\n<p><\/p>\n<p>Ein weiteres Unterscheidungsmerkmal ist, dass InfluxDB ein etwas merkw\u00fcrdiges Datenmodell hat, in dem jede Zeile mehrere Felder mit unterschiedlichen Tag-Sets speichern kann. Diese Zeilen werden zudem in verschiedene Tabellen unterteilt. Diese zus\u00e4tzlichen Verkomplizierungen erschweren die sp\u00e4tere Arbeit mit dieser Datenbank. Sie ist schwer zu pflegen und zu verstehen.<\/p>\n<p><\/p>\n<p>In VictoriaMetrics ist alles viel einfacher. Dort stellt jede Zeitreihe ein Schl\u00fcssel-Wert-Paar dar. Der Wert ist eine Sammlung von Punkten \u2013 <code>(Zeitstempel, Wert)<\/code>und der Schl\u00fcssel ist ein Set. <code>label=value<\/code>. Es gibt keine Trennung zwischen Feldern und Messungen. Dies erm\u00f6glicht es Ihnen, beliebige Daten auszuw\u00e4hlen und diese zu kombinieren, zu addieren, zu subtrahieren, zu multiplizieren und zu dividieren, im Gegensatz zu InfluxDB, wo Berechnungen zwischen verschiedenen Zeilen meines Wissens nach immer noch nicht realisiert sind. Selbst wenn sie implementiert wurden, ist es schwierig und erfordert eine Menge Code. <\/p>\n<p><\/p>\n<p><em>Ich habe eine kl\u00e4rende Frage. Habe ich richtig verstanden, dass Sie von einem Problem gesprochen haben, bei dem dieser invertierte Index nicht in den Speicher passt, weshalb die Partitionierung notwendig ist?<\/em><\/p>\n<p><\/p>\n<p>\u0412\u043d\u0430\u0447\u0430\u043b\u0435 \u044f \u043f\u043e\u043a\u0430\u0437\u0430\u043b \u043d\u0430\u0438\u0432\u043d\u0443\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0438\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u0438\u043d\u0434\u0435\u043a\u0441\u0430 \u043d\u0430 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0439 Go&#8217;\u0448\u043d\u043e\u0439 map&#8217;\u0435. \u0422\u0430\u043a\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043d\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u0434\u043b\u044f \u0431\u0430\u0437 \u0434\u0430\u043d\u043d\u044b\u0445, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u044d\u0442\u043e\u0442 \u0438\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0438\u043d\u0434\u0435\u043a\u0441 \u043d\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f \u043d\u0430 \u0434\u0438\u0441\u043a\u0435, \u0430 \u0431\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043e\u043b\u0436\u043d\u0430 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u043d\u0430 \u0434\u0438\u0441\u043a, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438 \u0440\u0435\u0441\u0442\u0430\u0440\u0442\u0435 \u044d\u0442\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u043e\u0441\u0442\u0430\u0432\u0430\u043b\u0438\u0441\u044c \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u043c\u0438. \u0412 \u0434\u0430\u043d\u043d\u043e\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u0438 \u0440\u0435\u0441\u0442\u0430\u0440\u0442\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0443 \u0432\u0430\u0441 \u0438\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0438\u043d\u0434\u0435\u043a\u0441 \u043f\u0440\u043e\u043f\u0430\u0434\u0435\u0442. \u0418 \u0432\u044b \u043f\u043e\u0442\u0435\u0440\u044f\u0435\u0442\u0435 \u0434\u043e\u0441\u0442\u0443\u043f \u043a\u043e \u0432\u0441\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u043c, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043d\u0435 \u0441\u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0430\u0439\u0442\u0438 \u0438\u0445. <\/p>\n<p><\/p>\n<p><em>Hallo! Vielen Dank f\u00fcr den Vortrag! Ich bin Pavel von der Firma Wildberries. Ich habe ein paar Fragen an Sie. Die erste Frage: Glauben Sie, dass es m\u00f6glich gewesen w\u00e4re, wenn Sie einen anderen Ansatz beim Aufbau der Architektur Ihrer Anwendung gew\u00e4hlt und die Daten nach Zeit partitioniert h\u00e4tten? W\u00e4ren Sie dann in der Lage gewesen, Datenintersektionen bei der Suche durchzuf\u00fchren, nur basierend auf den Daten, die in einer Partition f\u00fcr einen bestimmten Zeitraum liegen, das hei\u00dft in einem Zeitintervall, sodass Sie sich keine Gedanken dar\u00fcber machen m\u00fcssten, dass Ihre Datenst\u00fccke unterschiedlich verteilt sind? Die zweite Frage \u2014 da Sie einen solchen Algorithmus mit Bitset und allem anderen implementieren, haben Sie vielleicht auch versucht, Prozessoranweisungen zu verwenden? M\u00f6glicherweise haben Sie solche Optimierungen ausprobiert?<\/em><\/p>\n<p><\/p>\n<p>Ich werde die zweite Frage direkt beantworten. Wir sind dort noch nicht angekommen. Aber wenn es notwendig ist, werden wir das tun. Und was war die erste Frage?<\/p>\n<p><\/p>\n<p><em>Sie haben zwei Szenarien besprochen. Und gesagt, dass Sie die zweite Implementierung mit mehr Komplexit\u00e4t gew\u00e4hlt haben und nicht die erste, bei der die Daten nach Zeit partitioniert sind.<\/em> <\/p>\n<p><\/p>\n<p>Ja. Im ersten Fall w\u00e4re das gesamte Indexvolumen gr\u00f6\u00dfer, da wir in jeder Partition doppelte Daten f\u00fcr die Zeitreihen speichern m\u00fcssten, die sich \u00fcber alle diese Partitionen erstrecken. Und wenn Ihre Churn-Rate bei den Zeitreihen niedrig ist, d. h. st\u00e4ndig die gleichen Reihen verwendet werden, h\u00e4tten wir im ersten Fall im Vergleich zum zweiten Fall wesentlich mehr an Speicherplatz verloren.<\/p>\n<p><\/p>\n<p>So ist es \u2013 ja, die zeitliche Partitionierung ist eine gute Option. Das verwendet Prometheus. Aber Prometheus hat einen anderen Nachteil. Beim Zusammenf\u00fchren dieser Datenst\u00fccke ben\u00f6tigt es, um die Metainformationen zu allen Labels und Zeitreihen im Speicher zu halten. Daher steigt der Speicherverbrauch beim Zusammenf\u00fchren erheblich, wenn die Datenst\u00fccke gro\u00df sind, im Gegensatz zu VictoriaMetrics. Bei der Zusammenf\u00fchrung verbraucht VictoriaMetrics \u00fcberhaupt keinen Speicher; es werden nur einige Kilobyte ben\u00f6tigt, unabh\u00e4ngig von der Gr\u00f6\u00dfe der zusammengef\u00fchrten Datenst\u00fccke.<\/p>\n<p><\/p>\n<p><em>Der verwendete Algorithmus ben\u00f6tigt Speicher. Darin werden Zeitstempel markiert, die Werte enthalten. So pr\u00fcfen Sie das Vorhandensein von Paaren in einem Datensatz und einem anderen. Sie erkennen, ob ein Schnittpunkt stattgefunden hat oder nicht. Normalerweise implementieren Datenbanken Cursors und Iteratoren, die ihren aktuellen Status beibehalten und \u00fcber sortierte Daten iterieren, was eine einfache Komplexit\u00e4t dieser Operationen erm\u00f6glicht.<\/em> <\/p>\n<p><\/p>\n<p>Warum verwenden wir keine Cursors zum \u00dcberschneiden von Daten?<\/p>\n<p><\/p>\n<p><em>Ja.<\/em> <\/p>\n<p><\/p>\n<p>In LevelDB oder im Mergeset werden genau die sortierten Zeilen gespeichert. Wir k\u00f6nnten einen Cursor verwenden, um das \u00dcberschneiden zu finden. Aber warum nutzen wir das nicht? Weil es langsam ist. Cursors setzen voraus, dass f\u00fcr jede Zeile eine Funktion aufgerufen werden muss. Der Funktionsaufruf dauert 5 Nanosekunden. Und wenn Sie 100.000.000 Zeilen haben, verbringen wir also eine halbe Sekunde nur mit dem Funktionsaufruf.<\/p>\n<p><\/p>\n<p><em>Ja, das gibt es. Und meine letzte Frage. Die Frage klingt vielleicht ein wenig seltsam. Warum k\u00f6nnen die notwendigen Aggregationen beim Eintreffen der Daten nicht sofort berechnet und in der ben\u00f6tigten Form gespeichert werden? Warum gro\u00dfe Mengen in Systeme wie VictoriaMetrics, ClickHouse usw. speichern, um dann viel Zeit daf\u00fcr aufzuwenden?<\/em><\/p>\n<p><\/p>\n<p><em>Ich gebe ein Beispiel, damit es verst\u00e4ndlicher wird. Nehmen wir an, wie funktioniert ein kleiner Spielzeug-Tachometer? Er zeichnet die Strecke auf, die Sie zur\u00fcckgelegt haben, indem er diese st\u00e4ndig zu einer Gr\u00f6\u00dfe hinzuf\u00fcgt, zu einer anderen \u2014 die Zeit. Und teilt. Und erh\u00e4lt die Durchschnittsgeschwindigkeit. Man k\u00f6nnte ungef\u00e4hr dasselbe tun. Alle ben\u00f6tigten Fakten im Flug aufsummieren.<\/em><\/p>\n<p><\/p>\n<p>Gut, ich habe die Frage verstanden. Ihr Beispiel hat seine Daseinsberechtigung. Wenn Sie wissen, welche Aggregationen Sie ben\u00f6tigen, ist das die beste Vorgehensweise. Das Problem ist jedoch, dass Menschen diese Metriken und Daten in ClickHouse speichern, ohne zu wissen, wie sie diese in Zukunft aggregieren oder filtern werden. Daher behalten sie alle Rohdaten. Wenn Sie jedoch wissen, dass Sie etwas Durchschnittliches berechnen m\u00fcssen, warum sollten Sie dann nicht gleich das berechnen, anstatt eine Menge Rohwerte zu speichern? Aber das gilt nur, wenn Sie genau wissen, was Sie brauchen.<\/p>\n<p><\/p>\n<p>\u00dcbrigens unterst\u00fctzen Datenbanken f\u00fcr zeitliche Reihen die Aggregationsberechnung. Zum Beispiel unterst\u00fctzt Prometheus <noindex><a rel=\"nofollow\" href=\"https:\/\/prometheus.io\/docs\/prometheus\/latest\/configuration\/recording_rules\/\">recording rules<\/a><\/noindex>. Das hei\u00dft, das ist m\u00f6glich, wenn Sie wissen, welche Aggregationen Sie ben\u00f6tigen. VictoriaMetrics hat das derzeit nicht, aber normalerweise wird Prometheus davor gesetzt, wo Sie dies in den Recoding Rules tun k\u00f6nnen.<\/p>\n<p><\/p>\n<p>Zum Beispiel musste ich in meinem vorherigen Job die Anzahl der Ereignisse in einem gleitenden Fenster der letzten Stunde z\u00e4hlen. Das Problem dabei war, dass ich eine benutzerdefinierte Implementierung in Go erstellen musste, also einen Dienst, um diese Z\u00e4hlung durchzuf\u00fchren. Diese Implementierung war am Ende nicht trivial, da es schwierig ist, dies zu z\u00e4hlen. Die Umsetzung kann einfach sein, wenn Sie einige Aggregaten in festen Zeitintervallen z\u00e4hlen m\u00fcssen. Wenn Sie jedoch Ereignisse in einem gleitenden Fenster z\u00e4hlen wollen, ist es nicht so einfach, wie es scheint. Ich glaube, dass dies bis heute nicht in ClickHouse oder in Zeitreihen-Datenbanken implementiert ist, da es schwierig umzusetzen ist.<\/p>\n<p><\/p>\n<p><em>Und eine weitere Frage. Wir haben jetzt \u00fcber die Mittelung gesprochen und ich erinnere mich, dass es einmal so etwas wie Graphite mit einem Backend namens Carbon gab. Dieses konnte alte Daten reduzieren, d.h. eine Punkt pro Minute, einen Punkt pro Stunde usw. lassen. Grunds\u00e4tzlich ist das ziemlich praktisch, wenn wir rohe Daten, sagen wir mal, f\u00fcr einen Monat ben\u00f6tigen, und alle anderen k\u00f6nnen reduziert werden. Aber Prometheus und VictoriaMetrics unterst\u00fctzen diese Funktionalit\u00e4t nicht. Wird diese Unterst\u00fctzung geplant? Wenn nicht, warum nicht?<\/em><\/p>\n<p><\/p>\n<p>Danke f\u00fcr die Frage. Unsere Nutzer stellen sie gelegentlich. Sie fragen, wann wir die Unterst\u00fctzung f\u00fcr Downsampling hinzuf\u00fcgen. Hier gibt es mehrere Probleme. Erstens hat jeder Benutzer ein eigenes Verst\u00e4ndnis davon, <code>Downsampling<\/code> was Downsampling bedeutet: Manche m\u00f6chten einen beliebigen Punkt in einem bestimmten Intervall, andere w\u00fcnschen sich die maximalen, minimalen oder durchschnittlichen Werte. Wenn viele Systeme Daten in Ihre Datenbank schreiben, kann man diese nicht \u00fcber einen Kamm scheren. Es kann sich herausstellen, dass f\u00fcr jedes System unterschiedliche Downsampling-Methoden notwendig sind. Und das ist in der Umsetzung kompliziert.<\/p>\n<p><\/p>\n<p>Zweitens ist VictoriaMetrics, wie auch ClickHouse, darauf optimiert, mit gro\u00dfen Mengen Rohdaten zu arbeiten. Deshalb kann sie eine Milliarde Zeilen in weniger als einer Sekunde verarbeiten, wenn Sie \u00fcber viele Kerne in Ihrem System verf\u00fcgen. Das Scannen von Zeitreihendaten in VictoriaMetrics erreicht 50.000.000 Punkte pro Sekunde auf einen Kern. Und diese Leistung skaliert mit den vorhandenen Kernen. Das hei\u00dft, wenn Sie beispielsweise 20 Kerne haben, erreicht man eine Scanning-Geschwindigkeit von einer Milliarde Punkten pro Sekunde. Diese Eigenschaft von VictoriaMetrics und ClickHouse verringert den Bedarf an Downsampling.<\/p>\n<p><\/p>\n<p>Ein weiteres Merkmal ist, dass VictoriaMetrics diese Daten effizient komprimiert. Die Kompression liegt im Durchschnitt bei 0,4 bis 0,8 Byte pro Punkt in der Produktion. Jeder Punkt besteht aus einem Zeitstempel + einem Wert. Und er wird im Durchschnitt auf weniger als ein Byte komprimiert. <\/p>\n<p><\/p>\n<p><em>Sergej. Ich habe eine Frage. Was ist der minimale Zeitquantum f\u00fcr die Aufzeichnung?<\/em><\/p>\n<p><\/p>\n<p>Eine Millisekunde. K\u00fcrzlich hatten wir ein Gespr\u00e4ch mit anderen Entwicklern von Zeitreihendatenbanken. Ihr minimales Zeitquantum betr\u00e4gt eine Sekunde. In Graphite beispielsweise auch eine Sekunde. In OpenTSDB ebenfalls eine Sekunde. In InfluxDB \u2013 Nanosekundenpr\u00e4zision. In VictoriaMetrics \u2013 eine Millisekunde, weil in Prometheus eine Millisekunde verwendet wird. Und VictoriaMetrics wurde urspr\u00fcnglich als Remote-Speicher f\u00fcr Prometheus entwickelt. Aber jetzt kann es auch Daten aus anderen Systemen speichern. <\/p>\n<p><\/p>\n<p>Der Mensch, mit dem ich gesprochen habe, sagt, dass sie eine Sekundengenauigkeit haben \u2013 das reicht ihnen, weil es von der Art der Daten abh\u00e4ngt, die in die Zeitreihendatenbank gespeichert werden. Wenn es sich um DevOps-Daten oder Infrastruktur-Daten handelt, die mit einem Intervall von 30 Sekunden oder einer Minute gesammelt werden, dann ist eine Sekundengenauigkeit ausreichend; weniger braucht man nicht. Wenn die Daten jedoch aus Hochfrequenzhandelssystemen gesammelt werden, ist Nanosekundengenauigkeit erforderlich.<\/p>\n<p><\/p>\n<p>Millisekundengenauigkeit in VictoriaMetrics eignet sich sowohl f\u00fcr DevOps-Anwendungen als auch f\u00fcr die meisten Anwendungsf\u00e4lle, die ich zu Beginn der Pr\u00e4sentation erw\u00e4hnt habe. Das einzige, wof\u00fcr es m\u00f6glicherweise nicht geeignet ist, sind Hochfrequenzhandelssysteme.<\/p>\n<p><\/p>\n<p><em>Danke! Und noch eine Frage. Wie sieht es mit der Kompatibilit\u00e4t in PromQL aus?<\/em><\/p>\n<p><\/p>\n<p>Vollst\u00e4ndige Abw\u00e4rtskompatibilit\u00e4t. VictoriaMetrics unterst\u00fctzt vollst\u00e4ndig PromQL. Dar\u00fcber hinaus bietet es zus\u00e4tzliche erweiterte Funktionalit\u00e4ten f\u00fcr PromQL, die genannt werden <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/VictoriaMetrics\/VictoriaMetrics\/wiki\/MetricsQL\">MetricsQL<\/a><\/noindex>. Zu dieser erweiterten Funktionalit\u00e4t gibt es einen Vortrag auf YouTube. Ich habe im Fr\u00fchjahr auf dem Monitoring Meetup in St. Petersburg dar\u00fcber gesprochen.<\/p>\n<p><\/p>\n<p>einen Telegram-Kanal <noindex><a rel=\"nofollow\" href=\"https:\/\/t.me\/VictoriaMetrics_ru1\">VictoriaMetrics<\/a><\/noindex>.<\/p>\n<p class=\"for_users_only_msg\">Nur registrierte Benutzer k\u00f6nnen an der Umfrage teilnehmen. <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/auth\/login\/\">Bitte melden Sie sich an.<\/a><\/noindex>Sind Sie an Contour interessiert?<\/p>\n<h2 class=\"default-block__polling-title\">Was hindert Sie daran, zu VictoriaMetrics als langfristigem Speicher f\u00fcr Prometheus zu wechseln? (Bitte in die Kommentare schreiben, ich werde es zur Umfrage hinzuf\u00fcgen))<\/h2>\n<ul class=\"poll-result\">\n<li class=\"poll-result__item\">\n<p>                <strong class=\"poll-result__data-percent  poll-result__data-percent_winner\">71,4%<\/strong>Ich benutze Prometheus5 nicht<\/p>\n<\/li>\n<li class=\"poll-result__item\">\n<p>                <strong class=\"poll-result__data-percent\">28,6%<\/strong>Ich kannte VictoriaMetrics2 nicht<\/p>\n<\/li>\n<\/ul>\n<p>    7 Benutzer haben abgestimmt. 12 Benutzer haben sich enthalten.<br \/>\n<br \/>Quelle: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/500844\/\">habr.com<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u043a\u043e\u043d\u0446\u0430 2019 \u0433\u043e\u0434\u0430 \u0410\u043b\u0435\u043a\u0441\u0430\u043d\u0434\u0440\u0430 \u0412\u0430\u043b\u044f\u043b\u043a\u0438\u043d\u0430 &quot;Go optimizations in VictoriaMetrics&quot; VictoriaMetrics \u2014 \u0431\u044b\u0441\u0442\u0440\u0430\u044f \u0438 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u0443\u0435\u043c\u0430\u044f \u0421\u0423\u0411\u0414 \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u0444\u043e\u0440\u043c\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0433\u043e \u0440\u044f\u0434\u0430 (\u0437\u0430\u043f\u0438\u0441\u044c \u043e\u0431\u0440\u0430\u0437\u0443\u0435\u0442 \u0432\u0440\u0435\u043c\u044f \u0438 \u043d\u0430\u0431\u043e\u0440 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u044d\u0442\u043e\u043c\u0443 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0445 \u0447\u0435\u0440\u0435\u0437 \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043e\u043f\u0440\u043e\u0441 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0434\u0430\u0442\u0447\u0438\u043a\u043e\u0432 \u0438\u043b\u0438 \u0441\u0431\u043e\u0440 \u043c\u0435\u0442\u0440\u0438\u043a). \u0412\u043e\u0442 \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u0432\u0438\u0434\u0435\u043e \u044d\u0442\u043e\u0433\u043e \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u2014 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":80734,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-80733","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 4.9.10 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u043a\u043e\u043d\u0446\u0430 2019 \u0433\u043e\u0434\u0430 \u0410\u043b\u0435\u043a\u0441\u0430\u043d\u0434\u0440\u0430 \u0412\u0430\u043b\u044f\u043b\u043a\u0438\u043d\u0430 &quot;Go optimizations in VictoriaMetrics&quot; VictoriaMetrics \u2014 \u0431\u044b\u0441\u0442\u0440\u0430\u044f \u0438 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u0443\u0435\u043c\u0430\u044f \u0421\u0423\u0411\u0414 \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u0444\u043e\u0440\u043c\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0433\u043e \u0440\u044f\u0434\u0430 (\u0437\u0430\u043f\u0438\u0441\u044c \u043e\u0431\u0440\u0430\u0437\u0443\u0435\u0442 \u0432\u0440\u0435\u043c\u044f \u0438 \u043d\u0430\u0431\u043e\u0440 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u044d\u0442\u043e\u043c\u0443 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0445 \u0447\u0435\u0440\u0435\u0437 \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043e\u043f\u0440\u043e\u0441 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0434\u0430\u0442\u0447\u0438\u043a\u043e\u0432 \u0438\u043b\u0438 \u0441\u0431\u043e\u0440 \u043c\u0435\u0442\u0440\u0438\u043a). \u0412\u043e\u0442 \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u0432\u0438\u0434\u0435\u043e \u044d\u0442\u043e\u0433\u043e \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u2014\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/go-optimizations-in-victoriametrics-aleksandr-valyalkin\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 4.9.10\" \/>\n\t\t<meta property=\"og:locale\" content=\"de_DE\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47Go optimizations in VictoriaMetrics. \u0410\u043b\u0435\u043a\u0441\u0430\u043d\u0434\u0440 \u0412\u0430\u043b\u044f\u043b\u043a\u0438\u043d | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u043a\u043e\u043d\u0446\u0430 2019 \u0433\u043e\u0434\u0430 \u0410\u043b\u0435\u043a\u0441\u0430\u043d\u0434\u0440\u0430 \u0412\u0430\u043b\u044f\u043b\u043a\u0438\u043d\u0430 &quot;Go optimizations in VictoriaMetrics&quot; VictoriaMetrics \u2014 \u0431\u044b\u0441\u0442\u0440\u0430\u044f \u0438 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u0443\u0435\u043c\u0430\u044f \u0421\u0423\u0411\u0414 \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u0444\u043e\u0440\u043c\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0433\u043e \u0440\u044f\u0434\u0430 (\u0437\u0430\u043f\u0438\u0441\u044c \u043e\u0431\u0440\u0430\u0437\u0443\u0435\u0442 \u0432\u0440\u0435\u043c\u044f \u0438 \u043d\u0430\u0431\u043e\u0440 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u044d\u0442\u043e\u043c\u0443 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0445 \u0447\u0435\u0440\u0435\u0437 \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043e\u043f\u0440\u043e\u0441 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0434\u0430\u0442\u0447\u0438\u043a\u043e\u0432 \u0438\u043b\u0438 \u0441\u0431\u043e\u0440 \u043c\u0435\u0442\u0440\u0438\u043a). \u0412\u043e\u0442 \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u0432\u0438\u0434\u0435\u043e \u044d\u0442\u043e\u0433\u043e \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u2014\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/go-optimizations-in-victoriametrics-aleksandr-valyalkin\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2020-05-08T11:42:31+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-05-08T11:42:31+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Go-Optimierungen in VictoriaMetrics. Alexander Valjalkin | ProHoster","description":"Ich empfehle, sich mit der Auswertung des Vortrags von Alexander Valjalkin aus dem Jahr 2019 \"Go-Optimierungen in VictoriaMetrics\" vertraut zu machen. VictoriaMetrics ist eine schnelle und skalierbare DBMS zur Speicherung und Verarbeitung von Daten in Form von Zeitreihen (ein Eintrag besteht aus der Zeit und einer Menge von Werten, die zu diesem Zeitpunkt geh\u00f6ren, beispielsweise erhalten durch regelm\u00e4\u00dfige Abfragen des Status von Sensoren oder das Sammeln von Metriken). Hier ist der Link zum Video dieses Vortrags \u2014","canonical_url":"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/go-optimizations-in-victoriametrics-aleksandr-valyalkin","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"de_DE","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47Go optimizations in VictoriaMetrics. \u0410\u043b\u0435\u043a\u0441\u0430\u043d\u0434\u0440 \u0412\u0430\u043b\u044f\u043b\u043a\u0438\u043d | ProHoster","og:description":"\u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u043a\u043e\u043d\u0446\u0430 2019 \u0433\u043e\u0434\u0430 \u0410\u043b\u0435\u043a\u0441\u0430\u043d\u0434\u0440\u0430 \u0412\u0430\u043b\u044f\u043b\u043a\u0438\u043d\u0430 &quot;Go optimizations in VictoriaMetrics&quot; VictoriaMetrics \u2014 \u0431\u044b\u0441\u0442\u0440\u0430\u044f \u0438 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u0443\u0435\u043c\u0430\u044f \u0421\u0423\u0411\u0414 \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u0444\u043e\u0440\u043c\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0433\u043e \u0440\u044f\u0434\u0430 (\u0437\u0430\u043f\u0438\u0441\u044c \u043e\u0431\u0440\u0430\u0437\u0443\u0435\u0442 \u0432\u0440\u0435\u043c\u044f \u0438 \u043d\u0430\u0431\u043e\u0440 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u044d\u0442\u043e\u043c\u0443 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0445 \u0447\u0435\u0440\u0435\u0437 \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043e\u043f\u0440\u043e\u0441 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0434\u0430\u0442\u0447\u0438\u043a\u043e\u0432 \u0438\u043b\u0438 \u0441\u0431\u043e\u0440 \u043c\u0435\u0442\u0440\u0438\u043a). \u0412\u043e\u0442 \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u0432\u0438\u0434\u0435\u043e \u044d\u0442\u043e\u0433\u043e \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u2014","og:url":"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/go-optimizations-in-victoriametrics-aleksandr-valyalkin","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2020-05-08T11:42:31+00:00","article:modified_time":"2020-05-08T11:42:31+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"80733","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":null,"breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 16:12:22","updated":"2022-09-29 05:16:51"},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts\/80733","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/comments?post=80733"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts\/80733\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media\/80734"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media?parent=80733"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/categories?post=80733"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/tags?post=80733"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}