TL;DR: Katër vjet më parë, unë e braktisa Google me idenë e një instrumenti të ri për monitorimin e serverëve. Ideja ishte të bashkonte në një shërbim funksione që zakonisht janë të izoluar. dhe analizës së logjeve, mbledhjes së metrikeve, dhe panelit të monitorimit. Një nga parimet është që shërbimi të jetë me të vërtetë i shpejtë, duke i ofruar devOps një punë të lehtë, interaktive dhe të këndshme. Kjo kërkon përpunimin e grupeve të dhënash disa gigabajt në pjesë sekonde, pa kaluar buxhetin. Veglat ekzistuese për punën me logjet shpesh janë të ngadalta dhe të paperceptueshme, prandaj ne u përballëm me një detyrë të mirë: të dizajnonim një vegël që t'u jepte përdoruesve një përvojë të re në punë.
Në këtë artikull, do të përshkruajmë se si ne në Scalyr e zgjidhem këtë problem, duke aplikuar metoda të vjetra, një qasje me forcë të papërpunuar, duke eliminuar shtresat e panevojshme dhe duke shmangur struktura komplekse të të dhënave. Këto mësime mund t'i aplikoni në detyrat tuaja inxhinierike.
Forca e stilit të vjetër
Analiza e logjeve zakonisht fillon me kërkimin: të gjejmë të gjitha mesazhet që përputhen me një model të caktuar. Në Scalyr, kjo janë dhjetëra ose qindra gigabajt logjesh nga shumë serverë. Qasjet moderne zakonisht parashikojnë ndërtimin e një strukture të komplikuar të dhënash, të optimizuar për kërkim. Sigurisht, kam parë diçka të tillë në Google, ku ata janë mjaft të mirë në këto gjëra. Por ne zgjodhëm një qasje shumë më të thjeshtë: skanimin lineare të logjeve. Dhe kjo funksionoi - ne ofrojmë një ndërfaqe me një kërkim që është shumë më e shpejtë se ajo e konkurrentëve (shihni animacionin në fund).
Rastësia kryesore ishte se procesorët modernë janë me të vërtetë shumë të shpejtë në operacione të thjeshta dhe të drejtpërdrejta. Kjo është e lehtë të humbasësh në sisteme të ndërlikuara me shumë shtresa, të cilat varen nga shpejtësia e I/O-së dhe operacioneve rrjet, dhe të tilla sisteme sot janë shumë të zakonshme. Për këtë arsye, ne zhvilluam një dizajn që minimizon numrin e shtresave dhe mbeturinave të panevojshme. Me disa procesorë dhe serverë paralel, shpejtësia e kërkimit arrin deri në 1 TB në sekondë.
Këto janë konkluzionet kryesore nga ky artikull:
- Kërkimi i thjeshtë është një qasje krejt e realizueshme për të zgjidhur probleme reale dhe në shkallë të madhe.
- Forca bruto është një teknikë projektimi, dhe jo një çlirim nga puna. Si çdo teknikë tjetër, ajo është më e përshtatshme për disa probleme se për të tjerat, dhe mund të realizohet keq ose mirë.
- Forca bruto është veçanërisht e mirë për arritjen e stabilitetit në performancë.
- Përdorimi efikas i forcës bruto kërkon optimizimin e kodit dhe përdorimin e mjaftueshëm të burimeve në kohë. Ajo përshtatet nëse serverët tuaj janë nën një ngarkesë të madhe, e cila nuk lidhet me përdoruesit, dhe operacionet e përdoruesve mbeten në prioritet.
- Performanca varet nga dizajni i tërë sistemit, dhe jo vetëm nga algoritmi i ciklit të brendshëm.
(Ky artikull përshkruan kërkimin e të dhënave në memorie. Në shumicën e rasteve, kur përdoruesi kryen një kërkim në loge, serverat Scalyr tashmë i kanë ruajtur ato. Në artikullin e ardhshëm do të diskutojmë për kërkimin në loge që nuk janë në cache. Të njëjtat principe zbatohen: kod efikas, metoda forcë bruto me burime të mëdha llogaritëse).
Metoda e forcës bruto
Tradicionalisht, kërkimi në një grup të madh të dhënash kryhet përmes indekseve të fjalëve kyçe. Në lidhje me loget e serverëve, kjo do të thotë kërkimi i çdo fjale unike në log. Për çdo fjalë duhet ngjitur një listë e të gjitha përfshirjeve. Kjo lejon që të gjenden lehtësisht të gjitha mesazhet me këtë fjalë, si 'error', 'firefox' ose 'transaction_16851951' - thjesht shikoni në indeks.
Kam përdorur një qasje të tillë në Google, dhe ajo ka funksionuar mirë. Por në Scalyr ne i kërkojmë loget byte për byte.
Pse? Nga një këndvështrim algoritmik abstrakt, indekset e fjalëve kyçe janë shumë më efektive se kërkimi i forcës bruto. Megjithatë ne nuk shesim algoritme, ne shesim performancë. Dhe performanca nuk është vetëm algoritme, por edhe inxhinieri sistemore. Duhet të marrim parasysh gjithçka: volumin e të dhënave, llojin e kërkimit, pajisjet e disponueshme dhe kontekstin programor. Ne vendosëm se për problemin tonë specifik, një variant si 'grep' është më i përshtatshëm sesa indeksi.
Indekset janë të shkëlqyera, por ato kanë kufizime. Të gjesh një fjalë është e lehtë. Por kërkimi i mesazheve me disa fjalë, si 'googlebot' dhe '404' është shumë më i ndërlikuar. Kërkimi i një fraze si 'uncaught exception' kërkon një indeks më të rëndë, i cili regjistron jo vetëm të gjitha mesazhet me këtë fjalë, por edhe vendndodhjen e saktë të fjalës.
Vështirësia e vërtetë shfaqet kur po kërkoni jo fjalët. Supozoni se dëshironi të shihni sa trafik po vjen nga robotët. Mendimi i parë është të kërkoni në logët me fjalën ‘bot’. Kështu do të gjeni disa robotë: Googlebot, Bingbot dhe shumë të tjerë. Por këtu ‘bot’ është pjesë e fjalës, jo e gjithë fjala. Nëse kërkoni ‘bot’ në indekset, nuk do të gjeni mesazhe me fjalën ‘Googlebot’. Nëse kontrolloni secilën fjalë në indeks dhe më pas skanoni indeksin për fjalët kyç të gjetura, kërkimi do të vonohet shumë. Si rezultat, disa programe për punën me logët nuk e lejojnë kërkimin për pjesë fjalësh ose (në rastin më të mirë) lejojnë të përdoren sintaksat speciale me performancë më të ulët. Ne duam ta evitojmë këtë.
Një problem tjetër është pikësimi. Dëshironi të gjeni të gjitha kërkesat nga 50.168.29.7? Что насчёт отладки логов, содержащих [error]? Индексы обычно пропускают пунктуацию.
Në fund, inxhinierët duan mjete të fuqishme, dhe ndonjëherë një problem mund të zgjidhet vetëm me një shprehje të rregullt. Indeksi i fjalëve kyçe nuk është shumë i përshtatshëm për këtë.
Për më tepër, indekset janë të ndërlikuara. Çdo mesazh duhet të shtohet në disa lista fjalësh kyçe. Këto lista duhet të mbahen vazhdimisht në një format të lehtë për t'u kërkuar. Kërkesat me fraza, fragmente fjalësh ose shprehje të rregullta duhet të konvertohen në operacione me disa lista, dhe rezultatet duhet të skanohet dhe të bashkohen për të marrë setin përfundimtar. Në kontekstin e një shërbimi me shumë përdorues në shkallë të gjerë, një kompleksitet i tillë krijon probleme performance që nuk duken gjatë analizimit të algoritmeve.
Indekset e fjalëve kyçe gjithashtu zënë shumë hapësirë, dhe ruajtja është një shpenzim kryesor në sistemin e menaxhimit të logëve.
Nga ana tjetër, mund të harxhohet shumë kapacitet llogaritës për çdo kërkim. Përdoruesit tanë vlerësojnë kërkimin me shpejtësi të lartë për kërkesat unike, por këto kërkesa bëhen relativisht shpesh. Për kërkesat tipike të kërkimit, siç është panela e monitorimit, ne aplikojmë teknika speciale (do t'i përshkruajmë ato në artikullin e ardhshëm). Kërkesa të tjera janë mjaft të rralla, kështu që rrallëherë duhet të përpunojmë më shumë se një përsëri. Por kjo nuk do të thotë se serverët tanë nuk janë të angazhuar: ata janë të ngarkuar me punën e pranimit, analizës dhe kompresimit të mesazheve të reja, vlerësimin e njoftimeve, kompresimin e të dhënave të vjetra dhe kështu me radhë. Kështu, ne kemi një rezervë të konsiderueshme procesorësh që mund të angazhojmë për përpunimin e kërkesave.
Forca e egër funksionon nëse keni një problem të madh (dhe shumë forcë)
Forca e egër funksionon më së miri në detyra të thjeshta me cikle të brendshme të vogla. Shpesh mund të optimizoni ciklin e brendshëm për të punuar me shpejtësi shumë të lartë. Nëse kodi është i komplikuar, atëherë është shumë më e vështirë të optimizohet.
Fillimisht, kodi ynë për kërkimin kishte një cikël të brendshëm mjaft të madh. Ne ruajmë mesazhet në faqe prej 4K; çdo faqe përmban disa mesazhe (në UTF-8) dhe metadatat për çdo mesazh. Metadatatat janë një strukturë ku janë të koduara gjatësia e vlerave, ID e brendshme e mesazhit dhe fushat e tjera. Cikli i kërkimit dukej kështu:

Ky është një variant i thjeshtuar në krahasim me kodin real. Por edhe këtu duken disa vendosje objekti, kopje të dhënash dhe thirrje funksionesh. JVM e optimizon mjaft mirë thirrjet e funksioneve dhe alokon objekte efemer, kështu që ky kod funksionoi më mirë se sa e meritonim. Gjatë testimit, klientët e përdorën mjaft me sukses. Por në fund, kaluam në një nivel të ri.
(Mund të pyesni se pse ne ruajmë mesazhet në një format të tillë me faqe prej 4K, tekst dhe metadata, dhe jo punojmë me log-et direkt. Ka shumë arsye, të cilat i reduktojmë në faktin që motori Scalyr është më shumë si një bazë të dhënash të shpërndara sesa si një sistem skedarësh. Kërkimi tekstual shpesh kombinohet me filtra si në një DB në fushat pas analizimit të log-eve. Ne mund të kërkojmë njëkohësisht në mijëra log-e, dhe skedarët e thjeshtë tekstualë nuk janë të përshtatshëm për menaxhimin tonë tranzaksional, replikues dhe të shpërndara të të dhënave).
Fillimisht dukeshin se ky kod nuk ishte shumë i përshtatshëm për optimizimin sipas metodës së forcës së madhe. “Puna e vërtetë” në String.indexOf() as nuk dominonte në profilin e CPU-së. Kështu që optimizimi i vetëm këtij metode nuk do të sillte një efekt të rëndësishëm.
Kështu ndodhi që ne ruajmë metadata në fillim të çdo faqeje, dhe teksti i të gjithë mesazheve në UTF-8 është i paketuar në fund të kundërt. Duke shfrytëzuar këtë, ne rishkruam ciklin për të kërkuar në të gjithë faqen menjëherë:

Kjo version funksionon drejtpërdrejt mbi pamjen raw byte[] dhe kryen kërkimin e të gjithë mesazheve menjëherë në të gjithë faqen 4K.
Kjo është shumë më e lehtë për t'u optimizuar për metodën e forcës së madhe. Cikli brendshëm i kërkimit thirret njëkohësisht për të gjithë faqen 4K, e jo veçmas për çdo mesazh. Nuk ka as kopjimi të të dhënave, as alokimi të objekteve. Dhe operacionet më komplekse me metadata thirren vetëm me një rezultat pozitiv, dhe jo për çdo mesazh. Kështu, ne eliminuam një ton shpenzimesh shtesë, dhe ngarkesa e mbetur përqendrohet në një cikël të vogël brendshëm kërkimi, i cili është i përshtatshëm për optimizimin e mëtejshëm.
Algoritmi ynë aktual i kërkimit bazohet në . Ai është i ngjashëm me algoritmin e Boyer–Moore me kalimin e gjatësi së vargut kërkues në çdo hap. Dallimi kryesor është se ai kontrollon dy byte në një herë për të minimizuar ndeshjet e rreme.
Implementimi ynë kërkon krijimin e një tabele kërkimi 64K për çdo kërkim, por kjo është e padukshme krahas gigabajtëve të të dhënave në të cilat kërkojmë. Cikli i brendshëm përpunon disa gigabajtë në sekondë në një bërthamë. Në praktikë, performanca stabile është rreth 1,25 GB në sekondë për çdo bërthamë, dhe ka potencial për përmirësim. Disa shpenzime jashtë ciklit të brendshëm mund të eliminohen dhe ne planifikojmë të eksperimentojmë me ciklin e brendshëm në C në vend të Java-s.
Apliko forcën
Diskutuam se si kërkimi në logje mund të realizohej "me forcë", por sa "forcë" kemi? Mjaft.
1 bërthamë: nëse përdoret siç duhet, një bërthamë moderne procesori është mjaft e fuqishme vetë.
8 bërthama: aktualisht po punojmë në serverat Amazon hi1.4xlarge dhe i2.4xlarge SSD, çdo njëri prej të cilëve ka 8 bërthama (16 procese). Siç u përmend më lart, zakonisht këto bërthama janë të angazhuara në operacione prapaskenë. Kur një përdorues bën kërkim, ato operacione prapaskenë pezullohen, duke liruar të gjitha 8 bërthamat për kërkimin. Kërkimi përfundon zakonisht brenda një çasti, pas së cilës puna prapaskenë rinis (programi regulues garanton që fluksi i kërkesave të kërkimit nuk do të pengojë punën e rëndësishme prapaskenë).
16 bërthama: për qëndrueshmëri, ne organizojmë serverat në grupe master/slave. Çdo master ka në varësi një server SSD dhe një EBS. Nëse serveri kryesor dëmtohet, serveri SSD menjëherë e zëvendëson atë. Gati gjithmonë master dhe slave punojnë normalisht, kështu që çdo bllok të dhënash është i aksesueshëm për kërkim në dy serverë të ndryshëm (serveri i varur EBS ka një procesor të dobët, kështu që nuk e marrim parasysh). E ndajmë detyrën midis tyre, për aq sa kemi një total prej 16 bërthamash të aksesueshme.
Shumë bërthama: në të ardhmen e afërt do të ndajmë të dhënat mbi serverat në një mënyrë që të gjithë të kontribuoni në përpunimin e çdo kërkese jo triviale. Do të punojë çdo bërthamë. [Shënim: ne implementuam planin dhe rritëm shpejtësinë e kërkimit deri në 1 TB/s, shih shënimin në fund të artikullit].
Thjeshtësia siguron qëndrueshmëri
Një tjetër avantazh i metodës me forcë është performanca e saj mjaft e qëndrueshme. Zakonisht, kërkimi nuk është shumë i ndjeshëm ndaj detajeve të detyrës dhe grupit të të dhënave (mendoj se pikërisht për këtë e quajnë "me forcë").
Indeksi i fjalëve kyçe ndonjëherë jep rezultate tepër të shpejta, ndërsa në raste të tjera jo. Supozoni se keni 50 GB log-esh, ku termi ‘customer_5987235982’ shfaqet saktësisht tre herë. Kërkimi për këtë term llogarit direkt nga indeksi tri lokacione dhe përfundon menjëherë. Por kërkimi i komplikuar me wildcard mund të skanojë mijëra fjalë kyçe dhe të zgjasë shumë.
Nga ana tjetër, kërkimi përmes metodës brute force për çdo kërkesë kryhet me një shpejtësi më shumë ose më pak të njëjtë. Kërkimi i fjalëve të gjata është më i mirë, por madje edhe kërkimi i një simboli ndodh mjaft shpejt.
Thjeshtësia e metodës brute force do të thotë se performanca e saj është afër maksimumit teorik. Këtu ka më pak mundësi për mbingarkesë të papritur të disqeve, konflikte gjatë bllokimeve, ndjekjen e treguesve dhe mijëra arsye të tjera për dështime. Unë sapo shqyrtova kërkesat e bërë nga përdoruesit e Scalyr javën e kaluar në serverin tonë më të ngarkuar. Kishim 14,000 kërkesa. Saktësisht tetë prej tyre zgjatën më shumë se një sekondë; 99% u përfundua brenda 111 milisekondave (nëse nuk keni përdorur mjete analize log-esh, besoni: është shpejt).
Performanca e qëndrueshme dhe e besueshme është e rëndësishme për përvojën e përdoruesit të shërbimit. Nëse ai ngadalsohet herë pas here, përdoruesit do ta perceptojnë atë si të pasigurt dhe do të hezitojnë ta përdorin.
Kërkimi i log-ëve në veprim
Këtu është një animacion i vogël që tregon kërkimin Scalyr në veprim. Kemi një llogari demo, ku importojmë çdo ngjarje në çdo depozitë publike në Github. Në këtë demonstratë, unë shqyrtoj të dhënat për një javë: rreth 600 MB log-esh të papërpunuara.
Videoja është regjistruar në kohë reale, pa përgatitje të veçantë, në desktopin tim (rreth 5000 kilometra nga serveri). Performanca që do të shihni, në masë të madhe iu dedikohet , si dhe një backend-i të shpejtë dhe të besueshëm. Sa herë që ndodh një pauzë pa indikatorin ‘loading’, unë bëj një pauzë për t'ju lejuar të lexoni atë që do të klikoj.

Në përfundim
Kur përpunoni sasi të mëdha të të dhënave, është e rëndësishme të zgjidhni një algoritëm të mirë, por "i mirë" nuk do të thotë "i çuditshëm". Mendoni për mënyrën se si do të funksionojë kodi juaj në praktikë. Disa faktorë që mund të jenë të rëndësishëm në botën reale janë përjashtuar nga analiza teorike e algoritmeve. Algoritmet më të thjeshta janë më të lehta për t'u optimizuar dhe janë më të qëndrueshme në situata të kufizuara.
Po ashtu, mendoni për kontekstin në të cilin do të ekzekutohet kodi. Në rastin tonë, nevojiten serverë mjaft të fuqishëm për të menaxhuar detyrat në sfond. Përdoruesit nisnin kërkimin relativisht rrallë, kështu që mund të huazojmë një grup të tërë serverësh për një periudhë të shkurtër, të nevojshme për përfundimin e çdo kërkese.
Me metodën e forcës së vrazhdtë, ne zbatuam një kërkim të shpejtë, të besueshëm dhe fleksibël mbi një grup logesh. Shpresojmë që këto ide do të jenë të dobishme për projektet tuaja.
Redaktimi: titulli dhe teksti u ndryshuan nga "Kërkimi me shpejtësi 20 GB në sekondë" në "Kërkimi me shpejtësi 1 TB në sekondë", për të reflektuar rritjen e performancës gjatë viteve të fundit. Ky rritje shpejtësie është kryesisht e lidhur me ndryshimin e llojit dhe numrit të serverëve EC2 që ne po ngremë sot për të shërbyer bazës së klientëve në rritje. Në të ardhmen e afërt, priten ndryshime që do të ofrojnë një përmirësim të madh të efikasitetit, dhe ne presim me padurim mundësinë për të folur për të.
Burimi: habr.com
