TL;DR: Neli aastat tagasi lahkusin Google'ist uue serverite jälgimisriista ideega. Idee oli ühendada ühe teenuse alla tavaliselt isoleeritud funktsioonid ja logide analüüsi, mõõdikute kogumise, ja juhtpaneeli. Üks põhimõtteid on see, et teenus peab olema tõeliselt kiire, pakkudes DevOps-inseneridele lihtsat, interaktiivset ja meeldivat töökogemust. See nõuab andmehulga töötlemist mitme gigabaidi ulatuses sekundite jooksul, jäädes eelarve piiridesse. Olemasolevad logitöötlustooted on sageli aeglased ja kohmakad, seetõttu seisime silmitsi hea väljakutsega: arendada välja tööriist, mis annaks kasutajatele uusi töövõimalusi.
Selles artiklis kirjeldatakse, kuidas meie Scalyr'is selle probleemi lahendasime, rakendades vana kooli meetodeid, jõhkrat lähenemist, eemaldades liigseid kihte ja vältides keerulisi andmestruktuure. Need õppetunnid saate rakendada oma inseneriprojektides.
Vana kooli jõud
Logide analüüs algab tavaliselt mustrite leidmisest: leida kõik sõnumid, mis vastavad mingile mustrile. Scalyris on see kümneid või sadu gigabaites logisid, mis pärinevad paljusid serveritest. Kaasaegsed lähenemisviisid eeldavad tavaliselt keeruka andmestruktuuri loomist, mis on optimeeritud otsimiseks. Olen muidugi näinud selliseid lahendusi Google’is, kus nad on selles üsna head. Kuid me valisime tunduvalt primitiivse lähenemise: logide lineaarne skaneerimine. Ja see toimib — pakume kiiremat otsinguliidest, kui meie konkurendid (vt animatsiooni lõpus).
Oluline avastus oli see, et kaasaegsed protsessorid on tõeliselt kiired lihtsates, sirgjoonelistes operatsioonides. Seda on lihtne unustada keerukates, mitmekihilistes süsteemides, mis sõltuvad I/O kiirusest ja võrgutoimingutest, ning sellised süsteemid on tänapäeval väga levinud. Seega töötasime välja disaini, mis minimaliseerib kihtide ja liigse müra arvu. Mitme protsessori ja serveri samaaegse kasutamise korral saavutab otsimise kiirus 1 TB sekundis.
Selle artikli põhijäreldused:
- Karmine otsing on täiesti toimiv lähenemine reaalses, suurte mastaabiga probleemide lahendamiseks.
- Karm jõud on projekteerimistehnika, mitte töö ära võtmine. Nagu iga tehnika, sobib see paremini teatud probleemide jaoks kui teiste jaoks, ja seda saab rakendada nii halvasti kui hästi.
- Karm jõud on eriti efektiivne stabiilne tulemuste saavutamiseks.
- Karma jõu tõhus kasutamine nõuab koodi optimeerimist ja piisavate ressursside õigeaegset rakendamist. See sobib, kui teie serverid on suure koormuse all, mis ei ole seotud kasutajatega, samas kui kasutajaoperatsioonid jäävad prioriteetseks.
- Tõhusus sõltub kogu süsteemi projekteerimisest, mitte ainult sise-tsükli algoritmist.
(Selles artiklis käsitletakse andmete otsimist mälus. Enamasti, kui kasutaja otsib logisid, on Scalyr serverid need juba vahemällu salvestanud. Järgmises artiklis arutame vahemällu salvestamata logide otsimist. Rakenduvad samad põhimõtted: efektiivne kood, karm jõu meetod suurte arvutusressurssidega).
Karma jõu meetod
Traditsiooniliselt toimub suure andmehulgaga otsing märksõnade indeksi kaudu. Serveripäevikute kontekstis tähendab see iga unikaalse sõna otsimist logis. Iga sõna jaoks tuleb koostada loetelu kõikidest esinemistest. See võimaldab hõlpsasti leida kõiki sõnaga seotud teateid, näiteks ‘error’, ‘firefox’ või «transaction_16851951» — lihtsalt vaatame indeksisse.
Kasutasin sellist lähenemist Google'is ja see töötas hästi. Kuid Scalyr'is otsime me logides baitihaaval.
Miks? Abstraktsest algoritmilisest vaatenurgast on märksõnade indeksid palju efektiivsemad kui jäik otsing. Kuid me ei müü algoritme, vaid jõudlust. Ja jõudlus ei sõltu ainult algoritmidest, vaid ka süsteemi inseneritööst. Me peame võtma arvesse kõike: andmehulka, otsingu tüüpi, saadaval olevaid seadmeid ja tarkvara konteksti. Otsustasime, et meie konkreetse probleemi jaoks sobib variant nagu ‘grep’ paremini kui indeks.
Indeksid on suurepärased, kuid neil on piirangud. Üks sõna on lihtne leida. Kuid mitme sõna, näiteks 'googlebot' ja '404', otsimine on juba palju keerulisem. Frase otsimine nagu 'uncaught exception' nõuab mahukamat indeksit, mis registreerib mitte ainult kõik selle sõnaga seonduvad sõnumid, vaid ka sõna täpse asukoha.
Tõeline väljakutse tekib siis, kui otsite mitte sõnu. Oletame, et soovite vaadata, kui palju liiklust tuleb botidelt. Esimene mõte on otsida logidest sõna 'bot'. Nii leiate mõned botid: Googlebot, Bingbot ja paljusid teisi. Kuid siin on 'bot' mitte sõna, vaid selle osa. Kui otsite 'bot' indeksis, ei leia me sõnumite seast sõna 'Googlebot'. Kui kontrollime iga sõna indeksis ja seejärel skaneerime indeksit leitud märksõnade põhjal, aeglustub otsing oluliselt. Tulemuseks on see, et mõned logide töötlemise programmid ei luba otsida osade sõnade kaudu või (parimal juhul) lubavad nad kasutada spetsiaalset süntaksit madalama jõudlusega. Soovime seda vältida.
Veel üks probleem on punktuatsioon. Kas soovite leida kõik päringud, mis on saadetud 50.168.29.7? Что насчёт отладки логов, содержащих [error]? Индексы обычно пропускают пунктуацию.
Lõpuks armastavad insenerid võimsaid tööriistu, ja mõnikord on probleem võimalik lahendada ainult regulaaravalduse kaudu. Märksõnade indeks ei sobi selleks hästi.
Lisaks on indeksid keerulised. Iga sõnum tuleb lisada mitmesse märksõnalisti. Need nimistud tuleks pidevalt hoida otsinguks mugavas formaadis. Päringud koos fraaside, sõnapurkide või regulaaravaldusega tuleb tõlkida mitme loendi operatsioonideks, ning tulemusi tuleb skaneerida ja kombineerida, et saada lõppkomplekt. Sellise keerukuse tõttu, konteksti suurte mitme kasutaja teenuse puhul tekivad jõudlusprobleemid, mis ei paista algoritmide analüüsimisel.
Märksõnade indeksid võtavad samuti palju ruumi, ja salvestus on peamine kuluartikli logihaldussüsteemis.
Teiselt poolt võib iga otsingute teostamine nõuda palju arvutusvõimet. Meie kasutajad hindavad kiiret otsimist unikaalsete päringute järgi, kuid selliseid päringuid tehakse suhteliselt harva. Tüüpiliste päringute, näiteks juhtpaneeli jaoks, rakendame spetsiifilisi meetodeid (millest kirjutame järgmises artiklis). Teised päringud on piisavalt haruldased, et tavaliselt teeme ühe korraga. Kuid see ei tähenda, et meie serverid ei oleks koormatud: need töötavad uute sõnumite vastuvõtmise, analüüsimise ja tihendamise, häirete hindamise, vanade andmete tihendamise jt kallal. Seega on meil piisavalt protsessoreid, mida saab kasutada päringute täitmiseks.
Jõhkralt jõud toimib, kui sul on jõhker probleem (ja palju jõudu)
Jõhkralt jõud töötab kõige paremini lihtsate ülesannete puhul, kus on väiksed sisemised tsüklid. Tihti saad sisemise tsükli optimeerida väga kõrgete kiiruseni. Kui kood on keeruline, siis on seda palju raskem optimeerida.
Alguses oli meie otsingukoodis üsna suur sise tsükkel. Salvestame lehtedele 4K sõnumid; iga leht sisaldab teatud sõnumeid (UTF-8 formaadis) ja iga sõnumi metainfot. Metainfos on struktuur, kus on kodeeritud väärtuse pikkus, sõnumi sisemine ID ja teised väljad. Otsingutsükkel nägi välja selline:

See on lihtsustatud variant võrreldes tegeliku koodiga. Kuid isegi siin on näha mitmeid objekti paigutusi, andmekopeerimisi ja funktsioonikutsesid. JVM optimeerib funktsioonikutsed üsna hästi ja eraldab üleminekuobjekte, seega töötas see kood paremini, kui me seda väärisime. Testimise ajal kasutasid kliendid seda üsna edukalt. Kuid lõpuks jõudsime uuele tasemele.
(Sa võite küsida, miks me salvestame sõnumeid sellises formaadis 4K lehtedes, tekstide ja metainfoga, mitte ei tööta logide kaudu otse. Siin on palju põhjuseid, mis tulenevad sellest, et Scalyr'i sisemootor sarnaneb rohkem jaotatud andmebaasidele kui failisüsteemile. Tekstipõhine otsing ühendatakse sageli andmebaasi stiilis filtritega pärast logide töötlemist. Saame samal ajal otsida paljude tuhande logi seast ning lihtsad tekstifailid ei sobi meie tehingupõhise, replikeeritud ja jaotatud andmete halduse jaoks).
Alguses tundus, et selline kood ei sobi väga hästi jõhkrate meetodite optimeerimiseks. "Tõeline töö" String.indexOf() ei domineerinud isegi CPU profiilis. Seega ei tooks ainult selle meetodi optimeerimine olulist efekti.
Nii on juhtunud, et salvestame metainfot iga lehe alguses ja kõikide sõnumite tekst UTF-8 formaadis on pakendatud teises otsas. Kasutades seda, kirjutasime ümber tsükli, et otsida kohe kogu lehe ulatuses:

Selline versioon töötab otse raw byte[] ja otsib kõiki sõnumeid korraga kogu 4K lehel.
Seda on palju lihtsam optimeerida bruteforce meetodile. Siseotsingutsükkel käivitatakse korraga kogu 4K lehe jaoks, mitte eraldi igas sõnumis. Ei ole andmete kopeerimist ega objektide valimist. Ja keerulisemaid metaandmete operatsioone kutsutakse esile vaid positiivsete tulemuste korral, mitte igas sõnumis. Nii oleme elimineerinud palju ülemäärast koormust ning ülejäänud koormus keskendub väikesele siseotsingutsüklile, mis sobib hästi edasiseks optimeerimiseks.
Meie tegelik otsingu algoritm põhineb . See sarnaneb Boyer—Moore algoritmiga, mis jätab igal sammul umbes otsingurea pikkuse võrra vahele. Peamine erinevus on see, et see kontrollib kahte baiti korraga, et minimeerida vale vasteid.
Meie rakendus nõuab iga otsingu jaoks 64K otsingutabeli loomist, kuid see on naljakas võrreldes gigabaidiga andmetega, mille hulgast me otsime. Sisemine tsükkel töötleb ühe südamiku kohta mitut gigabaidi sekundis. Praktikas on stabiilne jõudlus umbes 1,25 GB sekundis igal südamikul, ja parandamiseks on potentsiaali. Mõningaid ülevaateid, mis ületavad sisemist tsüklit, on võimalik kõrvaldada, ja me plaanime katsetada sisemist tsüklit C keeles Java asemel.
Kasutame jõudu
Arutasime, et logide otsingut võib teostada 'karmilt', kuid kui palju 'jõudu' meil on? Pole vähe.
1 südamik: korraliku kasutamise korral on üks tänapäeva protsessori südamik iseenesest üsna võimas.
8 südamikku: hetkel töötame Amazon hi1.4xlarge ja i2.4xlarge SSD serverites, milles igas on 8 tuuma (16 niiti). Nagu eelnevalt mainitud, on need tuumad tavaliselt hõivatud taustategevustega. Kui kasutaja otsingut teeb, siis taustategevused peatatakse, vabastades kõik 8 tuuma otsingu jaoks. Otsing toimub tavaliselt murdosa sekundiga, pärast mida taustatööd jätkuvad (regulatsiooniprogramm garanteerib, et otsingupäringute tulv ei segaks olulisi taustategevusi).
16 tuuma: usaldusväärsuse tagamiseks korraldame serverid master/slave gruppidesse. Igal master serveril on alamserver SSD ja üks EBS. Kui peamine server jookseb kokku, asub SSD server kohe tema kohale. Enamiku ajast töötavad master ja slave korralikult, mistõttu on iga andmeplokk otsinguks kergesti kättesaadav kahel erineval serveril (alamserveri EBS-l on nõrgem protsessor, seetõttu ei arvestata seda). Jagame ülesande nende vahel, et meil oleks kokku 16 tuuma.
Palju tuumi: lähitulevikus saame andmed jaotada serverite vahel nii, et kõik osaleksid iga mittetäielikupärase päringu töötlemises. Iga tuum hakkab tööle. [Märkus: oleme rakendanud plaani ning suurendanud otsingu kiiruseni 1 TB/s, vt. artikli lõpus olevat märkust].
Lihtsus tagab usaldusväärsuse
Veel üks jõhkrate meetodite eelis on üsna stabiilne jõudlus. Üldiselt ei ole otsing liiga tundlik ülesande ja andmekogumi detailidele (arvan, et seetõttu nimetatakse seda 'jõhkraks').
Märksõnade indekseerimine annab mõnikord uskumatult kiireid tulemusi, kuid teistel juhtudel mitte. Oletame, et teil on 50 GB logisid, kus termin 'customer_5987235982' esineb täpselt kolm korda. Otsing selle termini järgi leiab otseselt indeksist kolm asukohta ja lõppeb koheselt. Kuid keeruline otsing, mis sisaldab jokerteemade kasutamist, võib skanneerida tuhandeid märksõnu ja võtta palju aega.
Teiselt poolt toimub jõhkrate otsingute tegemine mistahes päringu puhul enam-vähem sama kiiresti. Pikemate sõnade otsimine on efektiivsem, kuid isegi ühe sümboli otsing toimub piisavalt kiiresti.
Jõhkrate meetodi lihtsus tähendab, et selle jõudlus on lähedal teoreetilisele maksimumile. Siin on vähem võimalusi ettenägematuks kettasüsteemi ülekoormuseks, lukustuste konfliktiks, näidiku jälgimise probleemideks ja tuhandeks muuks tõrketekitamiseks. Vaatasin just Scalyr kasutajate päringute arvu möödunud nädalal meie kõige rohkem koormatud serveris. Kokku oli 14 000 päringut. Täpselt kaheksa neist kestis rohkem kui ühe sekundi; 99% täideti 111 millisekundi jooksul (kui te ei kasutanud logianalüüsi tööriistu, uskuge mind: see on kiire).
Stabiilne ja usaldusväärne jõudlus on teenuse kasutusmugavuse seisukohalt oluline. Kui see aeg-ajalt hangub, tunnevad kasutajad seda kui ebausaldusväärset ja ei kasuta seda meelsasti.
Logide otsing teostamisel
Siin on väike animatsioon, mis näitab Scalyr'i otsingut tegutsemas. Meil on demo konto, kuhu impordime iga sündmuse igas avalikus Githubi hoones. Selles demonstratsioonis uurin nädalate andmeid: umbes 600 MB töötlemata logisid.
Video on salvestatud reaalajas, ilma erilise ettevalmistuseta, minu töölaual (umbes 5000 kilomeetrit serverist). Tulemus, mida te näete, on suuresti tänu , samuti kiirele ja usaldusväärsele tagaplaanile. Iga kord, kui tekib paus ilma ‘loading’ indikaatorita, teen ma pausi, et teil oleks aega lugeda, mida ma valmistun vajutama.

Kokkuvõtteks
Suure andmehulkade töötlemisel on oluline valida hea algoritm, kuid "hea" ei tähenda "veider". Mõelge sellele, kuidas teie kood praktikas töötab. Teoreetilises algoritmide analüüsis jääb tähelepanuta mõned tegurid, mis võivad reaalses maailmas suurt tähtsust omada. Lihtsamad algoritmid on kergemini optimeeritavad ja need on piirjuhtumites stabiilsemad.
Mõelge ka kontekstile, milles kood töötab. Meie puhul on vajalikud piisavalt võimsad serverid taustaprotsesside haldamiseks. Kasutajad algatavad otsingu suhteliselt harva, seetõttu saame laenata terve rida servereid lühikeseks ajavahemikuks, mis on vajalik iga otsingu teostamiseks.
Jõhkrate meetoditega oleme rakendanud kiire ja usaldusväärse paindliku otsingu logide kogumi kaudu. Loodame, et need ideed on teie projektide jaoks kasulikud.
Redigeerimine: pealkiri ja tekst on muudetud «Otsing kiirusel 20 GB/s» asemel «Otsing kiirusel 1 TB/s», et kajastada viimastel aastatel toimunud jõudluse tõusu. See kiirusetõus on peamiselt tingitud EC2 serverite tüübi ja arvu muutumisest, mida me täna kliendibaasi teenindamiseks üles tõstame. Tulevikus ootame muudatusi, mis tagavad veel ühe järsu tõusu efektiivsuses, ja ootame põnevusega, et saaksime sellest rääkida.
Allikas: habr.com
