Utilizzo del partizionamento in MySQL per Zabbix con un numero elevato di oggetti di monitoraggio

Per il monitoraggio dei server e dei servizi utilizziamo da tempo, e con successo, una soluzione combinata basata su Nagios e Munin. Tuttavia, questa combinazione presenta diversi svantaggi, quindi noi, come molti altri, sfruttiamo attivamente Zabbix. In questo articolo parleremo di come, con pochi sforzi, è possibile risolvere il problema delle prestazioni legato all'aumento del numero di metriche raccolte e alla crescita delle dimensioni del database MySQL.

Problemi nell'uso del database MySQL insieme a Zabbix

Fino a quando il database era piccolo e il numero di metriche memorizzate in esso era ridotto, tutto funzionava perfettamente. Il processo standard housekeeper, che viene eseguito dal server Zabbix, eliminava con successo i record obsoleti dal database, impedendone la crescita. Tuttavia, non appena il numero di metriche raccolte è aumentato e il volume del database ha raggiunto una certa dimensione, le cose sono cambiate. L'housekeeper ha smesso di riuscire a eliminare i dati entro il tempo assegnato, rimanendo nel database dati obsoleti. Durante il funzionamento dell'housekeeper si è generato un carico maggiore sul server Zabbix, che poteva persistere a lungo. È diventato chiaro che era necessario risolvere la situazione.

Questo è un problema noto, praticamente chiunque abbia lavorato con grandi volumi di monitoraggio su Zabbix si è trovato di fronte alla stessa situazione. Ci sono state diverse soluzioni: ad esempio, sostituire MySQL con PostgreSQL o persino Elasticsearch, ma la soluzione più semplice e collaudata è stata quella di passare alla partizione delle tabelle che memorizzano i dati delle metriche nel database MySQL. Abbiamo deciso di seguire proprio questo percorso.

Passaggio da tabelle MySQL semplici a tabelle partizionate

Zabbix è ben documentato e le tabelle dove memorizza le metriche sono conosciute. Queste tabelle sono: la cronologia, dove vengono memorizzati i valori float, history_str, dove vengono memorizzati i valori stringa brevi, history_text, dove vengono memorizzati i valori di testo lunghi e history_uint, dove vengono memorizzati i valori interi. C'è anche una tabella trends, che memorizza la dinamica delle variazioni, ma abbiamo deciso di non modificarla, poiché la sua dimensione è contenuta e più avanti torneremo su di essa.

In generale, era chiaro quali tabelle dovevamo elaborare. Abbiamo deciso di creare partizioni per ogni settimana, ad eccezione dell'ultima, basandoci sui numeri del mese, ovvero quattro partizioni al mese: dal 1 al 7, dall'8 al 14, dal 15 al 21 e dal 22 al 1 (del mese successivo). La difficoltà stava nel dover trasformare le tabelle necessarie in tabelle partizionate "al volo", senza interrompere il funzionamento di Zabbix Server e la raccolta delle metriche.

Stranamente, a darci una mano è stata la stessa struttura dei dati delle tabelle. Ad esempio, la tabella la cronologia ha la seguente struttura:

`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',

inoltre

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

Come possiamo vedere, ogni metrica alla fine viene registrata nella tabella con due campi molto importanti e utili per noi itemid e clock. In questo modo, possiamo facilmente creare una tabella temporanea, ad esempio, di nome history_tmp, configurare la partizionamento per essa e quindi trasferire tutti i dati dalla tabella la cronologia, e poi rinominare la tabella la cronologia in history_old, e la tabella history_tmp in la cronologia, dopodiché possiamo inserire i dati che non abbiamo ancora trasferito da history_old in la cronologia e rimuovere history_old. Facendo questo possiamo farlo in modo completamente sicuro, non perderemo nulla, poiché i campi sopra menzionati itemid e clock garantiscono l'associazione di una metrica specifica a un orario specifico, e non a un numero di sequenza.

La procedura stessa del passaggio

Attenzione! È molto consigliato, prima di iniziare qualsiasi azione, effettuare un backup completo del database. Siamo tutti persone e possiamo commettere errori nell'immissione dei comandi, il che potrebbe portare alla perdita di dati. Sì, un backup non garantirà la massima attualità, ma è meglio averne uno piuttosto che nessuno.

Quindi, non spegniamo nulla e non fermiamo nulla. L'importante è che sul server MySQL ci sia una sufficiente quantità di spazio libero su disco, cioè che per ognuna delle tabelle sopra menzionate la cronologia, history_text, history_str, history_uint, ci sia almeno spazio sufficiente per creare una tabella con il suffisso "_tmp", considerando che essa avrà lo stesso volume della tabella originale.

Non descriveremo tutto più volte per ciascuna delle tabelle sopra menzionate e considereremo tutto solo per esempio una di esse — la tabella la cronologia.

Quindi, creiamo una tabella vuota history_tmp basata sulla struttura della tabella la cronologia.

CREATE TABLE `history_tmp` LIKE `history`;

Creiamo le partizioni di cui abbiamo bisogno. Per esempio, faremo questo per un mese. Ogni partizione è creata sulla base di una regola di partizionamento, che si basa sul valore del campo clock, che confrontiamo con il timestamp:

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"))
);

Questo operatore aggiunge la partizione alla tabella che abbiamo creato. history_tmpPrecisiamo che i dati il cui valore del campo clock è inferiore a «2019-02-01 00:00:00» andranno nella partizione p20190201, poi i dati il cui valore del campo clock è maggiore di «2019-02-01 00:00:00» ma inferiore a «2019-02-07 00:00:00» andranno nella partizione p20190207 e così via.

Nota importante: Cosa succede se nella nostra tabella partizionata compaiono dati il cui valore del campo clock è maggiore o uguale a «2019-03-01 00:00:00»? Poiché non esiste una partizione adatta per questi dati, non entreranno nella tabella e verranno persi. Pertanto, è necessario non dimenticare di creare tempestivamente ulteriori partizioni per evitare tali perdite di dati (di cui si parlerà più avanti).

Quindi, la tabella temporanea è pronta. Carichiamo i dati. Il processo può richiedere del tempo, ma fortunatamente non blocca altre richieste, quindi è sufficiente avere un po’ di pazienza:

INSERT IGNORE INTO `history_tmp` SELECT * FROM history;

La parola chiave IGNORE non è obbligatoria durante il caricamento iniziale, poiché non ci sono dati nella tabella, ma sarà necessaria durante il caricamento dei dati successivo. Inoltre, può essere utile se durante il caricamento dei dati bisogna interrompere il processo e ricominciare.

Quindi, dopo un po' di tempo (potrebbe volerci anche qualche ora), il primo caricamento dei dati è andato a buon fine. Come potete capire, ora la tabella history_tmp contiene solo alcuni dei dati dalla tabella la cronologia, e solo quelli che erano presenti al momento dell'inizio dell'esecuzione della query. Qui avete essenzialmente due opzioni: o facciamo un altro passaggio (se il processo di caricamento è durato a lungo) o passiamo direttamente alla rinomina delle tabelle, di cui si è parlato sopra. Iniziamo dal secondo passaggio. Per prima cosa dobbiamo capire l'orario dell'ultima registrazione inserita in history_tmp:

SELECT max(clock) FROM history_tmp;

Supponiamo di aver ottenuto: 1551045645. Ora usiamo il valore ottenuto nella seconda fase di inserimento dei dati:

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

Questa fase dovrebbe finire significativamente più velocemente. Ma se la prima fase ha impiegato ore e la seconda sta richiedendo anch'essa molto tempo, potrebbe essere opportuno eseguire una terza fase, che funzionerà esattamente come la seconda.

Alla fine, eseguiamo di nuovo l'operazione per ottenere il tempo dell'ultima registrazione inserita in history_tmp, eseguendo:

SELECT max(clock) FROM history_tmp;

Supponiamo che tu abbia ottenuto 1551085645. Salva questo valore: ci servirà per l'inserimento aggiuntivo.

Ora, precisamente, quando il caricamento iniziale dei dati in history_tmp è terminato, iniziamo a rinominare le tabelle:

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

Abbiamo configurato questo blocco come una singola transazione per evitare l'inserimento di dati in una tabella non esistente, poiché dopo il primo RENAME e prima dell'esecuzione del secondo RENAME, la tabella la cronologia non esisterà più. Ma anche se tra le operazioni RENAME arrivano alcuni dati e la tabella non esiste ancora (a causa della rinominazione), otterremo un numero limitato di errori di inserimento, che possiamo trascurare (siamo in monitoraggio, non in banca). la cronologia Ora abbiamo una nuova tabella

con partizionamento, ma manca dei dati che sono stati acquisiti durante l'ultimo ciclo di inserimento nella tabella la cronologia . Ma questi dati li abbiamo nella tabella history_tmpe ora li caricheremo da lì. Per fare questo, ci serve il valore precedentemente salvato 1551085645. Perché abbiamo salvato questo valore e non abbiamo utilizzato il tempo massimo di inserimento già dalla tabella attuale history_old INSERT IGNORE INTO `history` SELECT * FROM history_old WHERE clock>=1551045645; la cronologia? Потому что новые данные уже в неё поступают и мы получим неверное время. Итак, дозаливаем данные:

Dopo il termine di questa operazione, abbiamo nella nuova tabella partizionata

tutti i dati che erano nella vecchia, più quelli che sono già arrivati dopo la rinominazione della tabella. La tabella la cronologia non ci serve più. Possiamo eliminarla subito oppure, se sei particolarmente cauto, fare un backup prima di eliminarla. history_old È necessario ripetere l'intero processo descritto sopra per le tabelle

Cosa deve essere corretto nelle impostazioni di Zabbix Server history_str, history_text e history_uint.

Cosa deve essere corretto nelle impostazioni del Zabbix Server

Ora la gestione del database riguardante la storia dei dati ricade sulle nostre spalle. Ciò significa che Zabbix non deve più eliminare i dati obsoleti — ci penseremo noi. Affinché Zabbix Server non tenti di pulire i dati da solo, devi accedere all'interfaccia web di Zabbix, selezionare nel menu 'Amministrazione', poi il sottomenu 'Generale', quindi nel menu a tendina a destra scegliere 'Pulizia della storia'. Nella pagina che appare, dovrai deselezionare tutte le caselle per il gruppo 'Storia' e cliccare sul pulsante 'Aggiorna'. Questo previene la pulizia non necessaria delle tabelle. history* tramite housekeeper.

Nota anche su questa pagina il gruppo 'Dinamica delle modifiche'. Questa è precisamente la tabella trends, alla quale ci eravamo promessi di tornare. Se è diventata anche essa troppo grande e necessita di partizionamento, deseleziona le caselle anche in questo gruppo, poi gestisci questa tabella esattamente come hai fatto per le tabelle history*.

Ulteriore manutenzione del database

Come già accennato, per un funzionamento normale delle tabelle partizionate, è necessario creare le partizioni in tempo. Puoi farlo in questo modo:

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

Inoltre, poiché abbiamo creato tabelle partizionate e vietato a Zabbix Server di pulirle, la rimozione dei dati obsoleti è ora nostra responsabilità. Per fortuna, non ci sono problemi al riguardo. Questo si fa semplicemente eliminando quella partizione, i dati della quale non ci servono più.

Ad esempio:

ALTER TABLE history DROP PARTITION p20190201;

A differenza degli operatori DELETE FROM con specifica dell'intervallo di date, DROP PARTITION viene eseguito in pochi secondi, senza carico eccessivo server e funziona altrettanto bene nel caso venga utilizzato nella replica MySQL.

Conclusione

La soluzione descritta è collaudata nel tempo. Il volume dei dati cresce, ma non si riscontrano rallentamenti significativi delle prestazioni.

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster