MySQL-i parterite kasutamine Zabbixis, millel on suur hulk jälgitavaid objekte

Serverite ja teenuste jälgimiseks oleme juba pikka aega edukalt kasutanud Nagios ja Munin baasil põhinevat lahendust. Siiski on sellel kombinatsioonil mitmeid puudusi, mistõttu kasutame ka meie aktiivselt. Zabbix. Selles artiklis räägime, kuidas minimaalse vaeva ning pingutusega saab lahendada jõudlusprobleeme, kui jälgitavate mõõdikute arv suureneb ja MySQL andmebaasi maht kasvab.

MySQL andmebaasi probleemid koos Zabbixiga

Kuni andmebaas oli väike ja selles hoitavate mõõdikute arv väike, läks kõik suurepäraselt. Zabbix Serveri käivitav teenus housekeeper suutis edukalt kustutada vanu kirjeid andmebaasist, takistades selle suurenemist. Kuid niipea, kui jälgitavate mõõdikute arv suurenes ja andmebaasi maht saavutas teatud suuruse, halvenes olukord. Housekeeper ei suutnud kümne määratud aja jooksul andmeid kustutada ning andmebaasi jäid vanad andmed. Housekeeperi töö ajal oli Zabbix Serveril suurenenud koormus, mis võis kesta pikka aega. Üks asi oli selge: olukorda tuli kuidagi lahendada.

See on tuntud probleem, millega silmitsi seisavad praktiliselt kõik, kes on töötanud Zabbixiga suurte jälgimismahtudega. Lahendusi on olnud mitmeid: näiteks MySQL asendamine PostgreSQL-iga või isegi Elasticsearchiga, kuid kõige lihtsam ja tõestatud lahendus oli minna üle tabelite partitsioneerimisele, kus hoitakse mõõdikute andmeid MySQL andmebaasis. Me otsustasime valida just selle tee.

Üleminek tavalistelt MySQL tabelitelt partitsioneeritud tabelitele

Zabbix on hästi dokumenteeritud ja tabelid, kus ta hoiab mõõdikuid on teada. Need tabelid on: history, kus hoitakse float väärtusi, history_str, kus hoitakse lühikesi stringi väärtusi, history_text, kus hoitakse pikki tekstiväärtusi ja history_uint, kus hoitakse täisarvulisi väärtusi. On veel tabel trends, mis hoiab muutuste dünaamikat, kuid seda me ei puutu, kuna selle suurus on väike ja hiljem tagasi jõuame selle juurde.

Üldiselt oli selge, millised tabelid tuleb töödelda. Otsustasime luua partitioone iga nädala kohta, välja arvatud viimane, kuu numbrite põhjal, st neli partitiooni kuus: 1.–7., 8.–14., 15.–21. ja 22.–1. (järgmisel kuul). Raskuseks oli see, et meie vajalikud tabelid tuli „lennult” muuta partiitioneeritud tabeliteks, katkestamata Zabbix Serveri tööd ja meetrite kogumist.

Kuidas iganes, aitas meid sellel andmetabelite struktuur. Näiteks tabel history omab järgmist struktuuri:

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

samas

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

Kuidas näeme, iga mõõdik kantakse lõpuks tabelisse, kus on kaks väga olulist ja mugavat meile väljaannet itemid ja clock. Nii et me võime täiesti luua ajutise tabeli, näiteks nimega history_tmp, seadistada selle partitioneerimise ja seejärel kanda kõik andmed tabelist history, seejärel nimetada tabel history ühes history_old, ja tabel history_tmp ühes history, seejärel lisada need andmed, mis meil on lisamata tabelist history_old ühes history ja kustutada. history_old. Seda saab teha täiesti ohutult, me ei kaota midagi, kuna ülaltoodud väljad itemid ja clock tagavad konkreetse mõõdiku sidumise kindlat aega, mitte mingi järjestusnumbriga.

Kogu ülemineku protseduur

Tähelepanu! Enne meetmete alustamist on soovitatav teha täielik varukoopia andmebaasist. Me kõik oleme inimesed ja võime teha käsuviibaga vigu, mis võivad viia andmete kadumiseni. Jah, varukoopia ei paku maksimaalset ajakohasust, kuid parem on omada sellist kui mitte ühtegi.

Seega ei lülita me midagi välja ega peata. Peamine on see, et MySQL-serveris oleks piisavalt vabakohta kettal, t. e. et iga ülalnimetatud tabeli jaoks history, history_text, history_str, history_uint, oleks vähemalt piisavalt ruumi loomise jaoks tabeli, millel on sufiks „_tmp”, arvestades, et see on sama suur nagu algne tabel.

Me ei hakka kirjeldama kõike mitu korda iga ülaltoodud tabeli jaoks ja vaatame kõike ainult ühe nende näitel — tabeli history.

Nii et loome tühja tabeli history_tmp tabeli struktuuri põhjal history.

CREATE TABLE `history_tmp` LIKE `history`;

Loome vajalikud jaotised. Näiteks teeme seda üheks kuuks. Iga jaotis luuakse partitsioneerimisreegli alusel, mis põhineb väli väärtusel clock, mida me võrreldame ajatempli:

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

See käsk lisab partitsioneerimise meie loodud tabelile history_tmp. Täpsustame, et andmed, mille väärtus on clock vähem kui "2019-02-01 00:00:00", lähevad jaotisesse p20190201, seejärel andmed, mille väärtus on clock rohkem kui "2019-02-01 00:00:00", kuid vähem kui "2019-02-07 00:00:00", lähevad jaotisesse p20190207 ja nii edasi.

Oluline märkus: Entak kui meil on partitsioneeritud tabelis andmeid, mille kellaaeg on suurem või võrdne "2019-03-01 00:00:00"? Kuna nendele andmetele ei ole sobivat partitsiooni, ei pääse need tabelisse ja kaovad. Seetõttu peate meeles pidama, et luua õigeaegselt täiendavaid partitsioone, et vältida selliseid andmekao olukordi (millest räägime allpool).

Nii et ajutine tabel on ette valmistatud. Laeme andmeid. Protsess võib võtta üsna kaua aega, kuid õnneks see ei blokeeri mingeid teisi päringuid, nii et lihtsalt peame olema kannatlikud:

INSERT IGNORE INTO `history_tmp` SELECT * FROM history;

Sõna IGNORE algses laadimises ei ole vajalik, kuna tabelis andmeid ei ole, kuid see on vajalik andmete järkjärguliseks lisamiseks. Lisaks võib see osutuda kasulikuks, kui laadimise ajal tuleb protsess peatada ja uuesti alustada.

Nii et mingi aja pärast (võib-olla isegi mitu tundi) on esimene andmete laadimine toimunud. Nagu te aru saate, sisaldab tabel nüüd history_tmp kõiki andmeid tabelist history, vaid need, mis olid selles hetkeks, kui päring hakkas toimuma. Siin on teil tegelikult valik: kas me teeme veel ühe käigu (kui laadimisprotsess kestis kaua) või liigume kohe tabelite nimetamise juurde, millest eespool räägiti. Alustame esimese rohkema käigu määramisega. Esiteks peame mõistma viimase sisestatud kirje kella aega history_tmp:

SELECT max(clock) FROM history_tmp;

Eeldame, et olete saanud: 1551045645. Nüüd kasutame saadud väärtust teise käigu andmete laadimiseks:

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

See käik peaks lõppema märgatavalt kiiremini. Kuid kui esimene käik kestis tunde ja teine samuti kaua, võib osutuda õigeks teha ka kolmas käik, mis toimub täpselt nii nagu teine.

Lõpus teeme taas operatsiooni, et saada viimase sisestuse kellaaega history_tmp, tehes:

SELECT max(clock) FROM history_tmp;

Eeldame, et olete saanud 1551085645. Salvestage see väärtus — see on meile vajalik laadimise ajal.

Ja nüüd, kui esialgne andmete laadimine history_tmp on lõppenud, liigume tabelite nimetamise juurde:

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

Me oleme selle ploki korraldanud kui ühe tehingu, et vältida andmete sisestamise momenti mitteeksisteerivasse tabelisse, sest pärast esimest RENAME'i kuni teise RENAME'i täitmiseni tabel history ei saa eksisteerida. Aga isegi kui RENAME’i operatsioonide vahel tabelisse history tulevad mingid andmed, ja tabelit endal veel ei ole (tänu ümbernimetamisele), saame väikese hulga sisestusvigu, mille võib ignoreerida (meil on jälgimine, mitte pank).

Nüüd on meil uus tabel history partitsioneerimisega, kuid sellest puuduvad andmed, mis saadi viimase andmete sisestamise käigus tabelisse history_tmp. Kuid need andmed on meil tabelis history_old ja me täiendame neid sealt praegu. Selleks vajame varasemat salvestatud väärtust 1551085645. Miks me selle väärtuse salvestasime, mitte ei kasutanud suurimat sisestamise aega juba olemasolevast tabelist history? Потому что новые данные уже в неё поступают и мы получим неверное время. Итак, дозаливаем данные:

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

Pärast selle operatsiooni lõppu on meil uues, partitsioneeritud tabelis history kõik andmed, mis olid vanas, pluss need, mis on juba tulnud pärast tabeli ümbernimetamist. Tabel history_old me ei vajata enam. Saate selle kohe kustutada, või saate enne kustutamist teha sellest varukoopia (kui olete mures).

Ülaltoodud protsess tuleb korduda tabelite jaoks. history_str, history_text ja history_uint.

Mida tuleks Zabbix Serveri seadistustes muuta?

Nüüd lasub andmebaasi hooldus meie õlgadel seoses andmete ajalooga. See tähendab, et Zabbix ei pea enam vanu andmeid kustutama — sellega tegeleme nüüd ise. Et Zabbix Server ei prooviks andmeid ise puhastada, peate sisenema Zabbix veebiliidesesse, valima menüüst 'Administratsioon', seejärel alammenüüst 'Üldine', ja seejärel paremas rippmenüüs valima 'Ajaloo puhastamine'. Ilmuvanel lehel tuleb tühistada kõik ruudud rühmas 'Ajalugu' ja vajutada nuppu 'Uuenda'. See takistab meil mittevajalikku tabelite puhastamist. history* housekeeperi kaudu.

Pange tähele, et samal lehel rühmas 'Muutuste dünaamika'. See on see tabel trends, mille juurde me lubasime tagasi tulla. Kui see on samuti liiga suur ja vajab jagamist, tühjendage ka selles grupis ruudud ja seejärel töötlege seda tabelit täpselt samamoodi nagu tehti tabelitega. history*.

Andmete baasi edasine hooldus

Nagu varem mainitud, on partitseeritud tabelite normaalseks toimimiseks vajalik partitsioonide õigeaegne loomine. Seda saab teha järgmiselt:

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

Lisaks on meie ülesanne eemaldada vanad andmed, kuna oleme loonud partitseeritud tabelid ja keelanud Zabbix Server'il neid puhastada. Õnneks ei ole siin mingeid probleeme. See toimub lihtsalt partitsiooni kustutamisega, mille andmed pole enam vajalikud.

Näiteks:

ALTER TABLE history DROP PARTITION p20190201;

Erinevalt DATE RANGE määratlemisega DELETE FROM käskudest, töötab DROP PARTITION paari sekundi jooksul, koormates täiesti minimaalselt. server See töötab sama probleemivabalt ka MySQL replikatsiooni kasutamisel.

Kokkuvõte

Kirjeldatud lahendus on aja jooksul tõestatud. Andmemaht kasvab, kuid mingit märkimisväärset jõudluse aeglustumist ei täheldata.

Allikas: habr.com

Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster