Verwenden der Partitionierung in MySQL für Zabbix mit einer großen Anzahl von Überwachungsobjekten

Für die Überwachung von Servern und Diensten setzen wir seit langem erfolgreich eine kombinierte Lösung ein, die auf Nagios und Munin basiert. Dieser Haufen hat jedoch eine Reihe von Nachteilen, so dass wir, wie viele, Zabbix aktiv ausnutzen. In diesem Artikel werden wir darüber sprechen, wie Sie das Leistungsproblem mit minimalem Aufwand lösen können, wenn Sie die Anzahl der entfernten Metriken erhöhen und das Volumen der MySQL-Datenbank erhöhen

Probleme bei der Verwendung einer MySQL-Datenbank mit Zabbix


Obwohl die Datenbank klein und die Anzahl der darin gespeicherten Metriken gering war, war alles wunderbar. Der reguläre Housekeeper-Prozess, mit dem Zabbix Server selbst gestartet wird, hat veraltete Datensätze erfolgreich aus der Datenbank gelöscht und verhindert, dass sie größer werden. Sobald jedoch die Anzahl der erfassten Metriken anstieg und die Datenbankgröße eine bestimmte Größe erreichte, wurde alles schlechter. Die Haushälterin schaffte es nicht mehr, Daten für das zugewiesene Zeitintervall zu löschen, und die alten Daten blieben in der Datenbank. Während des Betriebs der Haushälterin war der Zabbix-Server einer erhöhten Belastung ausgesetzt, die lange anhalten konnte. Es wurde klar, dass es notwendig war, die aktuelle Situation irgendwie zu lösen.

Dies ist ein bekanntes Problem. Fast alle, die mit großen Überwachungsmengen auf Zabbix arbeiteten, sahen sich mit dem gleichen Problem konfrontiert. Es gab auch mehrere Lösungen: Zum Beispiel das Ersetzen von MySQL durch PostgreSQL oder sogar Elasticsearch. Die einfachste und bewährte Lösung bestand jedoch darin, auf Partitionstabellen zu wechseln, in denen Metrikdaten in der MySQL-Datenbank gespeichert sind. Wir haben uns für diesen Weg entschieden.

Migration von regulären MySQL-Tabellen auf partitionierte


Zabbix ist gut dokumentiert und die Tabellen, in denen Metriken gespeichert werden, sind bekannt. Dies sind Tabellen: history , in denen float-Werte gespeichert werden, history_str , in denen kurze Zeichenfolgenwerte gespeichert werden, history_text , in denen lange history_uint gespeichert werden, und history_uint , in denen ganzzahlige Werte gespeichert werden. Es gibt auch eine trends , in der die Dynamik von Änderungen gespeichert ist. Wir haben jedoch beschlossen, sie nicht zu berühren, da sie klein ist und wir etwas später darauf zurückkommen werden.

Im Allgemeinen war klar, welche Tabellen verarbeitet werden mussten. Wir haben beschlossen, Partitionen für jede Woche, mit Ausnahme der letzten, basierend auf den Zahlen des Monats, d. H. vier Partitionen pro Monat: vom 1. bis zum 7., vom 8. bis zum 14., vom 15. bis zum 21. und vom 22. bis zum 1. (nächster Monat). Die Schwierigkeit bestand darin, dass wir die benötigten Tabellen sofort in partitionierte Tabellen umwandeln mussten, ohne Zabbix Server zu unterbrechen und Messdaten zu erfassen.

Seltsamerweise hat uns dabei die Struktur dieser Tabellen geholfen. Die history hat beispielsweise die folgende Struktur:

 `itemid` bigint(20) unsigned NOT NULL, `clock` int(11) NOT NULL DEFAULT '0', `value` double(16,4) NOT NULL DEFAULT '0.0000', `ns` int(11) NOT NULL DEFAULT '0', 

dabei

 KEY `history_1` (`itemid`,`clock`) 

Wie Sie sehen, wird jede Metrik schließlich in eine Tabelle mit zwei sehr wichtigen und für uns praktischen Feldern itemid und clock eingegeben. So können wir beispielsweise eine temporäre Tabelle mit dem Namen history_tmp , eine history_tmp dafür einrichten und dann alle Daten aus der history dorthin übertragen und dann die history in history_old und die history_tmp Tabelle in history umbenennen und dann die Daten hinzufügen die wir von history_old zu history history_old history und history_old löschen. Sie können dies völlig sicher tun, wir verlieren nichts, da die oben angegebenen Felder itemid und clock eine Verbindungsmetrik zu einer bestimmten Zeit und nicht zu einer Art Seriennummer enthalten.

Der Übergangsprozess selbst


Achtung! Es ist sehr wünschenswert, vor dem Starten einer Aktion eine vollständige Sicherung von der Datenbank durchzuführen. Wir sind alle lebende Menschen und können Fehler in den Befehlen machen, die zu Datenverlust führen können. Ja Eine Sicherungskopie bietet keine maximale Relevanz, es ist jedoch besser, eine als keine zu haben.
Schalten Sie also nichts aus oder hören Sie nicht auf. Die Hauptsache ist, dass auf dem MySQL-Server selbst genügend freier Speicherplatz vorhanden sein sollte, d. H. Damit für jede der obigen Tabellen history , history_text , history_str , history_uint mindestens genügend Speicherplatz vorhanden ist, um eine Tabelle mit dem Suffix "_tmp" zu erstellen, history_str , sie history_uint der ursprünglichen Tabelle.

Wir werden nicht mehrmals alles für jede der obigen Tabellen beschreiben und alles am Beispiel von nur einer davon betrachten - der history .

Erstellen Sie daher eine leere history_tmp Tabelle basierend auf der Struktur der history Tabelle.

 CREATE TABLE `history_tmp` LIKE `history`; 

Wir erstellen die Partitionen, die wir brauchen. Lassen Sie es uns zum Beispiel einen Monat lang tun. Jede Partition wird basierend auf der Partitionsregel erstellt, basierend auf dem Wert des Uhrfelds , das wir mit dem Zeitstempel vergleichen:

 ALTER TABLE `history_tmp` PARTITION BY RANGE( clock ) ( PARTITION p20190201 VALUES LESS THAN (UNIX_TIMESTAMP("2019-02-01 00:00:00")), PARTITION p20190207 VALUES LESS THAN (UNIX_TIMESTAMP("2019-02-07 00:00:00")), PARTITION p20190214 VALUES LESS THAN (UNIX_TIMESTAMP("2019-02-14 00:00:00")), PARTITION p20190221 VALUES LESS THAN (UNIX_TIMESTAMP("2019-02-21 00:00:00")), PARTITION p20190301 VALUES LESS THAN (UNIX_TIMESTAMP("2019-03-01 00:00:00")) ); 

Dieser Operator fügt der history_tmp uns erstellten Tabelle history_tmp eine Partitionierung hinzu. Lassen Sie uns klarstellen, dass die Daten, für die der Wert des Uhrfelds kleiner als "2019-02-01 00:00:00" ist, in die Partition p20190201 fallen , dann die Daten, für die der Wert des Uhrfelds größer als "2019-02-01 00:00:00" ist, aber kleiner "2019-02-07 00:00:00" fällt in die Gruppe p20190207 und so weiter.
Wichtiger Hinweis: Und was passiert, wenn die partitionierte Tabelle Daten enthält, deren Wert im Uhrfeld größer oder gleich "2019-03-01 00:00:00" ist? Da es für diese Daten keine geeignete Partition gibt, fallen sie nicht in die Tabelle und gehen verloren. Daher dürfen Sie nicht vergessen, rechtzeitig zusätzliche Partitionen zu erstellen, um einen solchen Datenverlust (über den weiter unten berichtet wird) zu vermeiden.
Damit ist der temporäre Tisch vorbereitet. Füllen Sie die Daten. Der Vorgang kann ziemlich lange dauern, blockiert aber glücklicherweise keine weiteren Anforderungen, sodass Sie nur Geduld haben müssen:

 INSERT IGNORE INTO `history_tmp` SELECT * FROM history; 

Das Schlüsselwort IGNORE wird beim erstmaligen Ausfüllen nicht benötigt, da die Tabelle noch keine Daten enthält. Sie benötigen diese jedoch, wenn Sie Daten hinzufügen. Außerdem kann es hilfreich sein, wenn Sie diesen Vorgang unterbrechen und beim Ausfüllen der Daten erneut starten müssen.

Nach einiger Zeit (vielleicht sogar ein paar Stunden) ist der erste Datenupload abgeschlossen. Wie Sie verstehen, enthält die history_tmp Tabelle jetzt nicht alle Daten aus der history Tabelle, sondern nur die Daten, die sich zum Zeitpunkt des Abfragestarts darin befanden. Tatsächlich haben Sie hier die Wahl: Entweder machen wir einen weiteren Durchgang (wenn der Füllvorgang lange gedauert hat), oder wir benennen die oben genannten Tabellen sofort um. Nehmen wir zuerst den zweiten Durchgang. Zuerst müssen wir die Zeit des zuletzt eingefügten Datensatzes in history_tmp :

 SELECT max(clock) FROM history_tmp; 

Angenommen, Sie erhalten: 1551045645 . Jetzt verwenden wir den erhaltenen Wert im zweiten Durchgang der Datenfüllung:

 INSERT IGNORE INTO `history_tmp` SELECT * FROM history WHERE clock>=1551045645; 

Diese Passage sollte viel schneller enden. Wenn der erste Durchgang jedoch stundenlang und der zweite auch über einen längeren Zeitraum durchgeführt wurde, ist es möglicherweise richtig, den dritten Durchgang durchzuführen, der dem zweiten Durchgang vollständig ähnlich ist.

Am Ende führen wir den Vorgang zum history_tmp des Zeitpunkts der letzten Einfügung des Datensatzes in history_tmp indem wir history_tmp :

 SELECT max(clock) FROM history_tmp; 

Angenommen , Sie haben 1551085645 . Behalten Sie diesen Wert bei - wir werden ihn zum Nachfüllen benötigen.

Und jetzt, wenn die primären Daten in history_tmp , benennen wir die Tabellen um:

 BEGIN; RENAME TABLE history TO history_old; RENAME TABLE history_tmp TO history; COMMIT; 

Wir haben diesen Block als eine Transaktion konzipiert, um zu vermeiden, dass Daten in eine nicht vorhandene Tabelle eingefügt werden, da die Verlaufstabelle nach dem ersten RENAME bis zum zweiten RENAME nicht vorhanden ist. Aber selbst wenn einige Daten zwischen den RENAME-Vorgängen in der history eingehen und die Tabelle selbst noch nicht existiert (aufgrund von Umbenennungen), wird eine kleine Anzahl von Einfügefehlern angezeigt, die vernachlässigt werden können (wir haben Überwachung, nicht die Bank).

Jetzt haben wir eine neue history mit Partitionierung, aber sie enthält nicht genügend Daten, die beim letzten Durchlauf des Einfügens von Daten in die history_tmp Tabelle history_tmp . Aber wir haben diese Daten in der Tabelle history_old und teilen sie jetzt von dort aus. Dafür benötigen wir den zuvor gespeicherten Wert 1551085645. Warum haben wir diesen Wert gespeichert und die maximale Füllzeit nicht bereits aus der aktuellen history ? Weil neue Daten bereits eingehen und wir die falsche Zeit bekommen. Also messen wir die Daten:

 INSERT IGNORE INTO `history` SELECT * FROM history_old WHERE clock>=1551045645; 

Nach dem Ende dieses Vorgangs befinden sich in der neuen, partitionierten history alle Daten, die in der alten Tabelle enthalten waren, sowie die Daten, die nach dem Umbenennen der Tabelle eingegangen sind. Die Tabelle history_old wird nicht mehr benötigt. Sie können es sofort löschen oder eine Sicherungskopie davon erstellen (falls Sie Paranoia haben), bevor Sie es löschen.

Der gesamte oben beschriebene Prozess muss für die history_uint history_str , history_text und history_uint .

Was muss in den Zabbix Server-Einstellungen behoben werden?


Jetzt liegt die Pflege der Datenbank zur Datenhistorie auf unseren Schultern. Dies bedeutet, dass Zabbix keine alten Daten mehr löschen sollte - wir werden es selbst tun. Damit Zabbix Server nicht versucht, die Daten selbst zu bereinigen, müssen Sie zur Zabbix-Weboberfläche gehen, im Menü "Administration", dann im Untermenü "Allgemein" und rechts in der Dropdown-Liste "Verlauf löschen" auswählen. Deaktivieren Sie auf der angezeigten Seite alle Kontrollkästchen für die Gruppe "Verlauf" und klicken Sie auf die Schaltfläche "Aktualisieren". Auf diese Weise wird verhindert, dass history* -Tabellen von uns durch die Haushälterin bereinigt werden.

Beachten Sie auf derselben Seite die Gruppe „Dynamik der Veränderungen“. Dies ist nur die trends , zu der wir versprochen haben, zurückzukehren. Wenn es auch für Sie zu groß geworden ist und partitioniert werden muss, deaktivieren Sie auch diese Gruppe und verarbeiten Sie diese Tabelle genau wie für history* -Tabellen.

Weitere Datenbankpflege


Wie bereits erwähnt, ist es für den normalen Betrieb in partitionierten Tabellen erforderlich, Partitionen rechtzeitig zu erstellen. Du kannst das so machen:

 ALTER TABLE `history` ADD PARTITION (PARTITION p20190307 VALUES LESS THAN (UNIX_TIMESTAMP("2019-03-07 00:00:00"))); 

Da wir partitionierte Tabellen erstellt und Zabbix Server verboten haben, diese zu bereinigen, ist das Löschen alter Daten nun unser Anliegen. Zum Glück gibt es überhaupt keine Probleme. Dies geschieht einfach durch Löschen der Partition, deren Daten wir nicht mehr benötigen.

Zum Beispiel:

 ALTER TABLE history DROP PARTITION p20190201; 

Im Gegensatz zu DELETE FROM-Anweisungen mit einem Datumsbereich wird DROP PARTITION in wenigen Sekunden ausgeführt, lädt den Server überhaupt nicht und funktioniert bei Verwendung der Replikation in MySQL genauso reibungslos.

Fazit


Die beschriebene Lösung hat sich bewährt. Das Datenvolumen wächst, es ist jedoch kein Leistungsabfall festzustellen.

Source: https://habr.com/ru/post/de480082/


All Articles