Սերվերների և ծառայությունների մոնիթորինգի համար մենք վաղուց և դեռևս հաջողությամբ օգտագործում ենք Nagios-ի և Munin-ի վրա հիմնված համակցված լուծում: Այնուամենայնիվ, այս համադրությունն ունի մի շարք թերություններ, ուստի մենք, ինչպես շատ ուրիշներ, ակտիվորեն շահագործում ենք այն: Այս հոդվածում մենք կխոսենք այն մասին, թե ինչպես լուծել կատարողականի խնդիրը նվազագույն ջանքերով, երբ վերցվող չափանիշների քանակը մեծանում է, և MySQL տվյալների բազայի ծավալը մեծանում է։
MySQL տվյալների բազայի Zabbix-ի հետ համատեղ օգտագործման խնդիրներ
Թեև տվյալների բազան փոքր էր, և դրանում պահվող չափանիշների քանակը՝ փոքր, ամեն ինչ հիանալի էր։ Zabbix Server-ի կողմից գործարկվող սովորական housekeeper գործընթացը հաջողությամբ ջնջեց տվյալների բազայից հնացած գրառումները՝ կանխելով դրա աճը։ Սակայն, հենց որ հեռացված չափանիշների քանակը մեծացավ, և տվյալների բազայի ծավալը հասավ որոշակի չափի, ամեն ինչ վատացավ։ Housekeeper-ը այլևս չէր կարողանում ջնջել տվյալները իրեն հատկացված ժամանակահատվածում, և հին տվյալները սկսեցին մնալ տվյալների բազայում։ Housekeeper-ի աշխատանքի ընթացքում Zabbix Server-ի վրա առաջացավ ավելի մեծ ծանրաբեռնվածություն, որը կարող էր երկար ժամանակ տևել։ Պարզ դարձավ, որ իրավիճակը պետք է ինչ-որ կերպ լուծվեր։
Սա հայտնի խնդիր է, գրեթե բոլորը, ովքեր աշխատել են Zabbix-ի վրա մեծ ծավալի մոնիթորինգի հետ, բախվել են նույն խնդրին։ Կային նաև մի քանի լուծումներ. օրինակ՝ MySQL-ը փոխարինել PostgreSQL-ով կամ նույնիսկ Elasticsearch-ով, բայց ամենապարզ և ամենափորձված լուծումը MySQL տվյալների բազայում մետրիկ տվյալները պահող աղյուսակների բաժանման անցումն էր։ Մենք որոշեցինք գնալ հենց այս ուղղությամբ։
Սովորական MySQL աղյուսակներից բաժանված աղյուսակների անցում
Zabbix-ը լավ փաստաթղթավորված է, և հայտնի են աղյուսակները, որտեղ այն պահպանում է չափանիշները: Սրանք աղյուսակներն են՝ history, որտեղ պահվում են լողացող արժեքները, history_str, որտեղ պահվում են կարճ տողային արժեքները, history_text, որտեղ պահվում են երկար տեքստային արժեքներ և history_uint, որտեղ պահվում են ամբողջ արժեքներ։ Կա նաև աղյուսակ trends, որը պահպանում է փոփոխությունների դինամիկան, բայց մենք որոշեցինք չանդրադառնալ դրան, քանի որ դրա չափը փոքր է, և մենք դրան կանդրադառնանք մի փոքր ուշ։
Ընդհանուր առմամբ, պարզ էր, թե որ աղյուսակներն էին պետք մշակել: Մենք որոշեցինք բաժանումներ անել յուրաքանչյուր շաբաթվա համար, բացառությամբ վերջինի, ամսվա թվերի հիման վրա, այսինքն՝ ամսական չորս բաժանում՝ 1-ից մինչև 7-ը, 8-ից մինչև 14-ը, 15-ից մինչև 21-ը և 22-ից մինչև 1-ը (հաջորդ ամսվա): Դժվարությունն այն էր, որ մեզ անհրաժեշտ աղյուսակները պետք է «արագ» փոխակերպեինք բաժանվածների՝ առանց ընդհատելու Zabbix Server-ի աշխատանքը և չափանիշների հավաքագրումը:
Տարօրինակ է, բայց աղյուսակների կառուցվածքն ինքնին մեզ օգնության հասավ։ Օրինակ՝ աղյուսակը history ունի հետևյալ կառուցվածքը՝
`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',իսկ
KEY `history_1` (`itemid`,`clock`) Ինչպես տեսնում ենք, յուրաքանչյուր չափանիշ վերջիվերջո մուտքագրվում է աղյուսակի մեջ՝ մեզ համար երկու շատ կարևոր և հարմար դաշտերով։ ապրանքի նույնականացուցիչ и ժամացույցԱյսպիսով, մենք կարող ենք հեշտությամբ ստեղծել ժամանակավոր աղյուսակ, օրինակ՝ անունով history_tmp, կարգավորեք դրա համար բաժանումը, ապա փոխանցեք աղյուսակից բոլոր տվյալները այնտեղ history, ապա վերանվանեք աղյուսակը history в history_old, և սեղանը history_tmp в history, ապա լրացրեք այն տվյալները, որոնք դեռ չենք լրացրել history_old в history և ջնջել history_oldԴուք կարող եք դա անել լիովին անվտանգ, մենք ոչինչ չենք կորցնի, քանի որ վերևում նշված դաշտերը ապրանքի նույնականացուցիչ и ժամացույց կապ են ապահովում որոշակի չափանիշի և որոշակի ժամանակի միջև, այլ ոչ թե որևէ կարգական թվի։
Անցման գործընթացն ինքնին
Ուշադրություն։ Ցանկացած գործողություն սկսելուց առաջ խիստ խորհուրդ է տրվում ստեղծել տվյալների բազայի ամբողջական պահուստային պատճեն։ Մենք բոլորս կենդանի մարդիկ ենք և կարող ենք սխալներ թույլ տալ հրամաններ մուտքագրելիս, ինչը կարող է հանգեցնել տվյալների կորստի։ Այո, պահուստային պատճենը չի ապահովի առավելագույն արդիականություն, բայց ավելի լավ է ունենալ այն, քան ընդհանրապես չունենալ։
Այսպիսով, մենք ոչինչ չենք անջատում կամ կանգնեցնում։ Գլխավորն այն է, որ MySQL սերվերն ինքնին ունենա բավարար ազատ սկավառակի տարածք, այսինքն՝ վերը թվարկված յուրաքանչյուր աղյուսակի համար։ history, history_text, history_str, history_uint, առնվազն, բավարար տարածք կար «_tmp» վերջածանցով աղյուսակ ստեղծելու համար, հաշվի առնելով, որ այն նույն չափի կլինի, ինչ սկզբնական աղյուսակը։
Մենք վերը նշված աղյուսակներից յուրաքանչյուրի համար ամեն ինչ մի քանի անգամ չենք նկարագրի և ամեն ինչ կքննարկենք դրանցից միայն մեկի՝ աղյուսակի օրինակով։ history.
Այսպիսով, եկեք ստեղծենք դատարկ աղյուսակ history_tmp աղյուսակի կառուցվածքի հիման վրա history.
CREATE TABLE `history_tmp` LIKE `history`;Մենք ստեղծում ենք մեզ անհրաժեշտ բաժինները։ Օրինակ, մենք սա կանենք մեկ ամիս։ Յուրաքանչյուր բաժին ստեղծվում է դաշտի արժեքի վրա հիմնված բաժանման կանոնի հիման վրա։ ժամացույց, որը մենք համեմատում ենք ժամանակային նշագրի հետ՝
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"))
); Այս օպերատորը մեր ստեղծած աղյուսակին ավելացնում է բաժանում։ history_tmpԵկեք պարզաբանենք, որ տվյալները, որոնց համար դաշտի արժեքը ժամացույց բաժնում կլինի «2019-02-01 00:00:00»-ից պակաս p20190201, ապա տվյալները, որոնց համար դաշտի արժեքը ժամացույց բաժնում կլինի ավելի քան «2019-02-01 00:00:00»-ը, բայց պակաս քան «2019-02-07 00:00:00»-ը p20190207 եւ այլն:
Կարևոր նշում. Ի՞նչ է պատահում, եթե բաժանված աղյուսակում ունենք տվյալներ, որոնց ժամացույցի դաշտի արժեքը մեծ է կամ հավասար է «2019-03-01 00:00:00»-ին։ Քանի որ այս տվյալների համար համապատասխան բաժին չկա, դրանք չեն մտնի աղյուսակ և կկորչեն։ Հետևաբար, դուք պետք է հիշեք ժամանակին ստեղծել լրացուցիչ բաժիններ՝ նման տվյալների կորուստներից խուսափելու համար (այդ մասին ավելի մանրամասն՝ ստորև)։
Այսպիսով, ժամանակավոր աղյուսակը պատրաստ է։ Եկեք այն լրացնենք տվյալներով։ Գործընթացը կարող է բավականին երկար տևել, բայց, բարեբախտաբար, այն չի արգելափակում որևէ այլ հարցում, այնպես որ պարզապես պետք է համբերատար լինել։
INSERT IGNORE INTO `history_tmp` SELECT * FROM history;IGNORE բանալի բառը պարտադիր չէ սկզբնական լրացման համար, քանի որ աղյուսակում միևնույն է տվյալներ չկան, բայց այն ձեզ անհրաժեշտ կլինի լրացուցիչ տվյալներ ավելացնելիս: Այն կարող է նաև օգտակար լինել, եթե տվյալները լրացնելիս ստիպված լինեք ընդհատել գործընթացը և նորից սկսել:
Այսպիսով, որոշ ժամանակ անց (գուցե նույնիսկ մի քանի ժամ) տեղի է ունեցել տվյալների առաջին վերբեռնումը։ Ինչպես տեսնում եք, աղյուսակն այժմ history_tmp չի պարունակում աղյուսակի բոլոր տվյալները history, բայց միայն նրանք, որոնք դրանում էին հարցման մեկնարկի պահին։ Այստեղ, ըստ էության, դուք ունեք ընտրություն. կամ մենք կատարում ենք ևս մեկ անցում (եթե լրացման գործընթացը երկար է տևել), կամ անմիջապես անցնում ենք աղյուսակների վերանվանմանը, որը քննարկվել է վերևում։ Եկեք նախ խոսենք երկրորդ անցման մասին։ Նախ, մենք պետք է հասկանանք վերջին մուտքագրված գրառման ժամանակը history_tmp:
SELECT max(clock) FROM history_tmp;Ասենք, որ դուք ստացել եք. 1551045645Հիմա ստացված արժեքը օգտագործում ենք տվյալների լրացման երկրորդ փուլում՝
INSERT IGNORE INTO `history_tmp` SELECT * FROM history WHERE clock>=1551045645;Այս անցումը պետք է շատ ավելի արագ ավարտվի։ Բայց եթե առաջին անցումը ժամեր է տևել, իսկ երկրորդը՝ երկար, գուցե ճիշտ լինի երրորդ անցումը կատարել, որը կատարվում է ճիշտ նույն կերպ, ինչ երկրորդը։
Վերջում մենք կրկին կատարում ենք գրառման վերջին տեղադրման ժամանակը ստանալու գործողությունը history_tmp, անելով.
SELECT max(clock) FROM history_tmp;Ասենք, որ դուք ունեք 1551085645Պահպանեք այս արժեքը՝ մեզ այն անհրաժեշտ կլինի լիցքավորման համար։
Եվ հիմա, իրականում, երբ տվյալների սկզբնական բեռնումն ավարտված է history_tmp Ավարտված է, եկեք սկսենք վերանվանել աղյուսակները՝
BEGIN;
RENAME TABLE history TO history_old;
RENAME TABLE history_tmp TO history;
COMMIT; Մենք այս բլոկը ձևաչափեցինք որպես մեկ գործարք՝ գոյություն չունեցող աղյուսակում տվյալները տեղադրելու պահից խուսափելու համար, քանի որ առաջին RENAME-ից հետո մինչև երկրորդ RENAME-ի կատարումը, աղյուսակը history գոյություն չի ունենա։ Բայց նույնիսկ եթե աղյուսակում RENAME գործողությունների միջև history որոշ տվյալներ կհասնեն, բայց աղյուսակն ինքնին դեռ գոյություն չի ունենա (անվանափոխության պատճառով), մենք կստանանք փոքր թվով մուտքագրման սխալներ, որոնք կարող են անտեսվել (մենք ունենք մոնիթորինգ, ոչ թե բանկ):
Հիմա մենք ունենք նոր սեղան history բաժանման միջոցով, բայց բացակայում են աղյուսակում տվյալների ներմուծման վերջին փուլի ընթացքում ստացված տվյալները history_tmpԲայց մենք այս տվյալները ունենք աղյուսակում։ history_old և մենք հիմա կլրացնենք դրանք այնտեղից։ Դրա համար մեզ անհրաժեշտ կլինի նախկինում պահպանված 1551085645 արժեքը։ Ինչո՞ւ պահպանեցինք այս արժեքը և չօգտագործեցինք ընթացիկ աղյուսակից առավելագույն լրացման ժամանակը։ historyՔանի որ նոր տվյալներ արդեն մուտքագրվում են, և մենք սխալ ժամանակ կստանանք։ Այսպիսով, մենք ավելացնում ենք տվյալներ՝
INSERT IGNORE INTO `history` SELECT * FROM history_old WHERE clock>=1551045645; Այս գործողությունն ավարտելուց հետո մեր նոր, բաժանված աղյուսակում մենք ունենք history Այստեղ կան բոլոր տվյալները, որոնք կային հին աղյուսակում, գումարած աղյուսակի վերանվանումից հետո ստացվածները։ Աղյուսակ history_old Մեզ այն այլևս պետք չէ։ Կարող եք այն անմիջապես ջնջել, կամ կարող եք դրա պահուստային պատճենը ստեղծել ջնջելուց առաջ (եթե դուք պարանոյիկ եք)։
Վերը նկարագրված ամբողջ գործընթացը պետք է կրկնվի աղյուսակների համար։ history_str, history_text и history_uint.
Ինչը պետք է շտկվի Zabbix Server-ի կարգավորումներում
Այժմ տվյալների բազայի պահպանումը՝ տվյալների պատմության առումով, ընկնում է մեր ուսերին։ Սա նշանակում է, որ Zabbix-ը այլևս չպետք է ջնջի հին տվյալները՝ մենք դա կանենք ինքներս։ Որպեսզի Zabbix սերվերը չփորձի ինքնուրույն մաքրել տվյալները, անհրաժեշտ է մտնել Zabbix վեբ ինտերֆեյս, ցանկում ընտրել «Ադմինիստրացիա», ապա «Ընդհանուր» ենթացանկ, ապա աջ կողմում գտնվող ցանկում ընտրել «Մաքրել պատմությունը»։ Բացվող էջում անհրաժեշտ է հանել «Պատմություն» խմբի բոլոր վանդակների նշումը և սեղմել «Թարմացնել» կոճակը։ Սա կկանխի աղյուսակների ավելորդ մաքրումը։ history* տնային տնտեսուհու միջոցով։
Ուշադրություն դարձրեք նույն էջում գտնվող «Փոփոխությունների դինամիկա» խմբին։ Սա ընդամենը աղյուսակ է։ trends, որին մենք խոստացել էինք վերադառնալ։ Եթե այն նույնպես չափազանց մեծ է դարձել ձեզ համար և կարիք ունի բաժանման, հանեք նշումները նաև այս խմբում, ապա մշակեք այս աղյուսակը նույն կերպ, ինչպես արվել է աղյուսակների համար։ history*.
Լրացուցիչ տվյալների բազայի պահպանում
Ինչպես ավելի վաղ գրվել էր, բաժանված աղյուսակների վրա նորմալ աշխատանքի համար անհրաժեշտ է ժամանակին ստեղծել բաժանումներ։ Դա կարելի է անել հետևյալ կերպ.
ALTER TABLE `history` ADD PARTITION (PARTITION p20190307 VALUES LESS THAN (UNIX_TIMESTAMP("2019-03-07 00:00:00")));Բացի այդ, քանի որ մենք ստեղծեցինք բաժանված աղյուսակներ և արգելեցինք Zabbix Server-ին մաքրել դրանք, հին տվյալների ջնջումն այժմ մեր պարտականությունն է։ Բարեբախտաբար, այստեղ ընդհանրապես խնդիրներ չկան։ Դա արվում է պարզապես այն բաժանումը ջնջելով, որի տվյալները մեզ այլևս պետք չեն։
Օրինակ `
ALTER TABLE history DROP PARTITION p20190201;Ի տարբերություն ամսաթվի միջակայքով DELETE FROM հրամանների, DROP PARTITION-ի գործողությունը տևում է մի քանի վայրկյան և ամբողջությամբ բեռնաթափվում է։ սերվեր և նույնքան անխափան աշխատում է MySQL կրկնօրինակման օգտագործման ժամանակ։
Ամփոփում
Նկարագրված լուծումը դիմացել է ժամանակի փորձությանը։ Տվյալների ծավալը մեծանում է, բայց աշխատանքի նկատելի դանդաղում չի նկատվում։
Source: www.habr.com
