Për monitorimin e serverëve dhe shërbimeve kemi përdorur prej kohësh një zgjidhje të kombinuar mbi bazën e Nagios dhe Munin. Megjithatë, kjo lidhje ka disa disavantazhe, prandaj ne, si shumë të tjerë, e shfrytëzojmë aktivisht . Në këtë artikull do të flasim për mënyrat se si mund të zgjidhni problemin e performancës me minimum përpjekjesh, ndërsa numri i metrikave të mbledhura rritet dhe rritet volumi i BD MySQL
Problemet e përdorimit të BD MySQL në përputhje me Zabbix
Derisa BD ishte e vogël dhe numri i metrikave të ruajtura në të ishte i vogël, gjithçka shkonte mrekullisht. Procesi standard housekeeper, i cili aktivizohet nga Zabbix Server, kishte sukses të fshinte të dhënat e vjetra nga BD, duke mos e lejuar atë të rritet. Megjithatë, sa herë që numri i metrikave të mbledhura u rrit dhe volumi i BD arriti një madhësi të caktuar, gjithçka u përkeqësua. Housekeeper nuk arriti të fshinte të dhënat brenda intervalit të caktuar, duke lënë të dhëna të vjetra në BD. Gjatë punës së housekeeper kishte ngarkesë të shtuar në Zabbix Server, e cila mund të zgjaste për një kohë të gjatë. U bë e qartë se duhej të gjeje një zgjidhje për situatën e krijuar.
Kjo është një problem i njohur, pothuajse secili që ka punuar me volume të mëdha monitorimi në Zabbix është përballur me të njëjtën gjë. Zgjidhjet gjithashtu ishin disa: për shembull, zëvendësimi i MySQL me PostgreSQL ose madje Elasticsearch, por zgjidhja më e thjeshtë dhe e provuar ishte kalimi në pjesëzim të tabelave që ruajnë të dhënat e metrikave në BD MySQL. Ne vendosëm të ndjekim këtë rrugë.
Kalimi nga tabelat e zakonshme MySQL në ato të ndara
Zabbix është mirë-dokumentuar dhe tabelat ku ruan metrikat janë të njohura. Këto janë tabelat: history, ku ruhen vlera float, history_str, ku ruhen vlera të shkurtra string, history_text, ku ruhen vlera të gjata tekstuale dhe history_uint, ku ruhen vlera të plota. Ka gjithashtu një tabelë trends, e cila ruan dinamikën e ndryshimeve, por ne vendosëm ta lëmë të paprekur, sepse madhësia e saj është e vogël dhe më vonë do të kthehemi te ajo.
Në përgjithësi, ishte e qartë se cilat tabela duhej të përpunoheshin. Ne vendosëm të krijonim pjesë për çdo javë, përveç javës së fundit, bazuar në numrat e muajit, domethënë, katër pjesë për muaj: nga 1 deri në 7, nga 8 deri në 14, nga 15 deri në 21 dhe nga 22 deri në 1 (të muajit tjetër). Vështirësia ishte se duhej të transformonim tabelat që na duheshin në tabelat e ndara "në flakë", pa ndërprerë punën e Zabbix Server dhe mbledhjen e metrikave.
Siç duket e çuditshme, struktura e të dhënave të tabelave na ndihmoi në këtë. history Për shembull, tabela
`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',ndërkohë që
KEY `history_1` (`itemid`,`clock`) Siç e shohim, çdo metrikë përfundimisht shënohet në tabelë me dy fusha shumë të rëndësishme dhe të përshtatshme për ne itemid dhe clock. Kështu, ne mund të krijojmë një tabelë përkohshme, për shembull, me emrin history_tmp, të konfiguroni ndarjen për të dhe më pas të derdhni të gjithë të dhënat nga tabela history, e pastaj të rinominojmë tabelën history në history_old, dhe tabelën history_tmp në history, pas të cilit do të shtojmë ato të dhëna që nuk i kemi futur nga history_old në history dhe të fshihet history_old. Kjo mund të bëhet krejtësisht në mënyrë të sigurt, ne nuk do të humbim asgjë, pasi fusha të përmendura më sipër itemid dhe clock sigurojnë lidhjen e metrikave të caktuara me një kohë të caktuar, dhe jo me një numër rendor.
Procedura e kalimit
Kujdes! Është shumë e dëshirueshme, para se të filloni ndonjë veprim, të bëni një kopje të plotë rezervë të databazës. Ne jemi njerëz të gjallë dhe mund të bëjmë gabime në komanda, që mund të çojnë në humbje të të dhënash. Po, kopja rezervë nuk do të sigurojë maksimalisht aktualitetin, por është më mirë të kesh një të tillë sesa asnjë.
Pra, asgjë nuk fiket dhe nuk ndalet. E rëndësishme është që në MySQL server të ekzistojë një hapësirë e mjaftueshme e lirë në disk, domethënë, për secilën nga tabelat e përmendura më sipër history, history_text, history_str, history_uint, duhen të paktën sa hapësirë për të krijuar një tabelë me sufixin "_tmp", duke e pasur parasysh se do të ketë të njëjtën peshë si tabela origjinale.
Ne nuk do ta përshkruajmë gjithçka disa herë për secilën nga tabelat e mësipërme dhe do ta shqyrtojmë gjithçka vetëm në shembullin e një prej tyre — tabelës history.
Pra, krijojmë një tabelë të zbrazët history_tmp në bazë të strukturës së tabelës history.
CREATE TABLE `history_tmp` LIKE `history`;Krijojmë partitë që na nevojiten. Për shembull, do ta bëjmë këtë për një muaj. Çdo parti krijohet mbi bazën e një rregulli ndarjeje, i bazuar në vlerën e fushës clock, e cila e krahasojmë me vulën e kohës:
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"))
); Ky operator shton ndarjen për tabelën që krijuam history_tmp. Sqarojmë se të dhënat, të cilat kanë vlerën e fushës clock më pak se "2019-02-01 00:00:00" do të hyjnë në partinë p20190201, pastaj të dhënat të cilat kanë vlerën e fushës clock më shumë se "2019-02-01 00:00:00" por më pak se "2019-02-07 00:00:00" do të hyjnë në partinë p20190207 dhe kështu me radhë.
Një shënim i rëndësishëm: Çfarë do të ndodhë, nëse në tabelën me ndarje duhet të kemi të dhëna për të cilat vlera e fushës clock është më e madhe ose e barabartë me "2019-03-01 00:00:00"? Për shkak se për këto të dhëna nuk ka një parti të përshtatshme, ato nuk do të hyjnë në tabelë dhe do të humbasin. Prandaj, është e nevojshme të mos harroni të krijoni me kohë parti të tjera, për të parandaluar humbjen e tillë të të dhënave (siç do të shpjegohet më poshtë).
Pra, tabela e përkohshme është përgatitur. Po ngarkojmë të dhënat. Procesi mund të zgjasë një kohë të gjatë, por fatmirësisht ai nuk bllokon kërkesa të tjera, kështu që thjesht duhet të jeni të durueshëm:
INSERT IGNORE INTO `history_tmp` SELECT * FROM history;Fjalori kryesor IGNORE gjatë ngarkesës fillestare nuk është i domosdoshëm, pasi të dhënat në tabelë nuk ka, megjithatë ai do t'ju nevojitet gjatë ngarkesës së mëtejshme të të dhënave. Për më tepër, ai mund të jetë i dobishëm, nëse gjatë ngarkesës të dhënash ju duhej ta ndërprisnit këtë proces dhe ta fillonit nga e para.
Pra, pas një kohe (ndoshta edhe disa orë), ngarkesa e parë e të dhënave ka përfunduar. Siç e kuptoni, tani tabela history_tmp përmban të gjitha të dhënat nga tabela history, por vetëm ato që ishin në të në momentin e fillimit të ekzekutimit të kërkesës. Tani keni një zgjedhje: ose bëjmë një kalim tjetër (nëse procesi i ngarkesës zgjati gjatë), ose menjëherë kalojmë në rinovimin e tabelave, për të cilën është folur më lart. Le të flasim fillimisht për kalimin e dytë. Për fillim na nevojitet të kuptojmë kohën e regjistrit të fundit të futur në history_tmp:
SELECT max(clock) FROM history_tmp;Supozoni se keni marrë: 1551045645. Tani do tani, ne do ta vlerësojmë vlerën e marrë në kalimin e dytë të mbushjes së të dhënave:
INSERT IGNORE INTO `history_tmp` SELECT * FROM history WHERE clock>=1551045645;Ky kalim duhet të përfundojë shumë më shpejt. Por nëse kalimi i parë zgjati orë, dhe kalimi i dytë gjithashtu zgjat për një kohë të gjatë, ndoshta do të ishte e arsyeshme të bëni edhe një kalim të tretë, i cili do të ekzekutohet ashtu si kalimi i dytë.
Në fund, ne përsëri kryejmë operacionin për të marrë kohën e fundit të shkrimit të regjistrit në history_tmp, duke ekzekutuar:
SELECT max(clock) FROM history_tmp;Supozoni se keni marrë 1551085645. Ruani këtë vlerë - do na nevojitet për mbushje të mëtejshme.
Tani, pasi që mbushja fillestare e të dhënave në history_tmp ka përfunduar, fillojmë me rinovimin e tabelave:
BEGIN;
RENAME TABLE history TO history_old;
RENAME TABLE history_tmp TO history;
COMMIT; Ne e kemi dizajnuar këtë bllok si një transaksion, për të shmangur momentin e insertimit të të dhënave në një tabelë që nuk ekziston, sepse pas RENAME të parë deri në momentin e ekzekutimit të RENAME të dytë, tabela history nuk do të ekzistojë. Por, edhe nëse gjatë operacioneve RENAME ndodhin disa të dhëna në tabelë, ndërsa tabela vetë nuk do të jetë (për shkak të rinovimit), ne do të marrim një numër të vogël gabimesh insertimi, të cilat mund të injorohen (ne kemi monitorim, jo bankë). history Tani kemi një tabelë të re
me ndarje, por i mungojnë të dhënat që u morën gjatë kalimit të fundit të mbushjes së të dhënave në tabelë. history . Por ne i kemi këto të dhëna në tabelën history_tmpdhe tani do t'i mbushim ato nga aty. Për këtë, do të na nevojitet vlera e ruajtur më parë 1551085645. Pse e ruajmë këtë vlerë dhe jo kohën maksimale të mbushjes nga tabela aktuale? history_old INSERT IGNORE INTO `history` SELECT * FROM history_old WHERE clock>=1551045645; history? Потому что новые данные уже в неё поступают и мы получим неверное время. Итак, дозаливаем данные:
Pas përfundimit të këtij operacioni, në tabelën tonë të re, të ndarë, kemi të gjitha të dhënat që ishin në të vjetrën, plus ato që kanë mbërritur pas rinovimit të tabelës. Tabela history nuk na nevojitet më. Mund ta fshijmë menjëherë, ose para se ta fshijmë mund të bëjmë një kopje rezervë (nëse keni paranojë). history_old E gjithë kjo proces duhet përsëritur për tabelat
Çfarë duhet të rregullohet në cilësimet e Zabbix Server history_str, history_text dhe history_uint.
Что нужно поправить в настройках Zabbix Server
Tani administrimi i bazës së të dhënave mbi historinë e të dhënave tani bie mbi ne. Kjo do të thotë që Zabbix nuk duhet më të fshijë të dhëna të vjetra — do të merremi ne me këtë vetë. Për të parandaluar që Zabbix Server të përpiqet të pastrojë të dhënat vetë, ju duhen të hyni në ndërfaqen web të Zabbix, të zgjidhni në menunë "Administrimi", pastaj nënmenunë "Të përgjithshmet", dhe më pas në listën e rënies nga ana e djathtë të zgjidhni "Pastrimin e historisë". Në faqen që shfaqet, duhet të hiqni të gjitha shenjat për grupin "Historia" dhe të klikoni në butonin "Rivendos". Kjo do të parandalojë pastrimin e panevojshëm të tabelave. historia* nëpërmjet housekeeper.
Vini re se në këtë faqe po ashtu, ndodhet grupi "Dinamikat e ndryshimeve". Kjo është tabela trends, të cilës ne premtuam se do t'i kthehemi. Nëse kjo gjithashtu ka bërë shumë të madhe dhe ka nevojë për ndarje, hiqni shenjat në këtë grup, dhe pastaj trajtoni këtë tabelë ashtu siç është bërë për tabelat historia*.
Mirëmbajtja e mëtejshme e bazës së të dhënave
Ashtu siç u tha më parë, për funksionimin normal të tabelave të ndara, është e nevojshme të krijoni ndarje në kohë. Këtë mund ta bëni kështu:
ALTER TABLE `history` ADD PARTITION (PARTITION p20190307 VALUES LESS THAN (UNIX_TIMESTAMP("2019-03-07 00:00:00")));Për më tepër, që ne krijuam tabela të ndara dhe ndaluam Zabbix Server të pastronte ato, tani fshirja e të dhënave të vjetra është përgjegjësia jonë. Fatmirësisht, këtu nuk ka asnjë problem. Kjo bëhet thjesht duke fshirë atë ndarje, të cilat të dhënat e saj nuk na duhen më.
Për shembull:
ALTER TABLE history DROP PARTITION p20190201;Ndryshe nga operatorët DELETE FROM me një diapazon datash, DROP PARTITION ekzekutohet për disa sekonda, dhe nuk shkakton ndonjë ngarkesë server dhe funksionon njësoj pa probleme në rastin e përdorimit të replikimit në MySQL.
Përfundim
Zgjidhja e përshkruar është provuar me kalimin e kohës. Vëllimi i të dhënave rritet, por nuk ka ushtruar ndonjë ngadalësim të dukshëm të performancës.
Burimi: habr.com
