TL;DR: Neli aastat tagasi lahkusin Google'ist uue serverite jälgimise tööriista ideega. Idee oli ühendada tavaliselt isoleeritud funktsioonid ühte teenusesse. ja logide analüüsi, mõõtmete kogumise, ja juhtpaneeli. Üks põhimõtteid on, et teenus peab olema tõeliselt kiire, et anda DevOps'idele mugav, interaktiivne ja meeldiv töö. See nõuab gigabaitide andmestike töötlemist pidevalt teatud aja jooksul, jäädes eelarve raamidesse. Olemasolevad logide töötlemise tööriistad on sageli aeglased ja kohmakad, seetõttu seisime silmitsi hea väljakutsega: luua tööriist, mis annaks kasutajatele uue kogemuse.
Selles artiklis kirjeldatakse, kuidas me Scalyris seda probleemi lahendasime, rakendades vana kooli meetodeid, jämedat lähenemist, eemaldades tarbetud kihid ja vältides keerulisi andmestruktuure. Neid õppetunde saate oma inseneritöös rakendada.
Vana kooli jõud
Logide analüüs algab tavaliselt otsingust: leida kõik teatised, mis vastavad teatud mustrile. Scalyris on see kümneid või sadu gigabaite logisid paljusid servereid. Ajakohased lähenemised eeldavad tavaliselt keeruka andmestruktuuri loomist, mis on optimeeritud otsinguks. Olen kindlasti sellist näinud Google'is, kus nad on selles päris head. Aga me valisime palju jämedama lähenemise: logide lineaarne skaneerimine. Ja see toimis — pakume otsingu liidest, mis on järjekordselt kiirem kui konkurentidel (vaata animatsiooni lõpus).
Peamine äratundmine oli see, et kaasaegsed protsessorid on tõeliselt kiireid lihtsates, sirgjoonelistes toimingutes. Seda on lihtne unustada keerulistes, mitmekihilistest süsteemides, mis sõltuvad I/O ja võrgutegevuse kiirusest, mis on tänapäeval väga levinud. Seetõttu töötasime välja disaini, mis minimeerib kihtide ja tarbetu müra arvu. Mitme protsessori ja serveri kasutamisel ulatub otsingukiirus 1 TB sekundis.
Selle artikli peamised järeldused:
- Jäme otsing on täiesti elujõuline lähenemine, et lahendada reaalsed, skaleeritavad probleemid.
- Jõhkrus on disainitehnika, mitte töö vabastamine. Nagu iga tehnika, sobib see paremini mõnede probleemide jaoks kui teiste jaoks ning seda saab rakendada halvasti või hästi.
- Jõhkrus on eriti hea saavutamaks faasi) lahendus tõhusust.
- Tõhus jõhkruse kasutamine nõuab koodi optimeerimist ja piisava hulga ressursside õigeaegset rakendamist. See sobib siis, kui teie serverid on suures koormuses, mis ei ole seotud kasutajatega, samas kui kasutajate toimingud jäävad prioriteediks.
- Tõhusus sõltub kogu süsteemi kavandamisest, mitte ainult sisemise tsükli algoritmist.
(Selles artiklis käsitletakse andmete otsimist mälus. Enamikul juhtudel, kui kasutaja otsib logides, on Scalyr serverid need juba vahemälus. Järgmises artiklis arutame mitte-vahemäluga logides otsimist. Rakendatakse samu põhimõtteid: tõhus kood, jõhkrus meetod suurte arvutusressursside abil).
Jõhkrus meetod
Traditsiooniliselt toimub suurte andmestike otsimine märksõna indeksi kaudu. Serveri logide kontekstis tähendab see iga unikaalse sõna otsimist logis. Iga sõna jaoks tuleb koostada nimekiri kõigist esinemistest. See võimaldab hõlpsalt leida kõik sõnumid selle sõnaga, näiteks 'error', 'firefox' või 'transaction_16851951' - vaadates lihtsalt indeksisse.
Ma kasutasin sellist lähenemist Google'is, ja see toimis hästi. Kuid Scalyr'is otsime logides baiti haaval.
Miks? Abstraktselt algoritmiliselt on märksõna indeksid palju tõhusamad kui jõhker otsing. Kuid me ei müü algoritme, me müüme tõhusust. Ja tõhusus ei seisne mitte ainult algoritmides, vaid ka süsteemi inseneritehnikas. Peame arvestama kõike: andmehulka, otsimise tüüpi, saadaval olevat riistvara ja tarkvarakonteksti. Otsustasime, et meie konkreetse probleemi jaoks sobib midagi nagu 'grep' paremini kui indeks.
Indeksid on suurepärased, kuid neil on piirangud. Ühte sõna on lihtne leida. Siiski, sõnumite otsimine mitme sõna, nagu 'googlebot' ja '404', korral on see juba palju keerulisem. Frase nagu 'uncaught exception' otsimine nõuab mahukamat indeksit, mis registreerib mitte ainult kõik sõnumid selle sõnaga, vaid ka sõna spetsiifilise asukoha.
Tõeline raskus tekkib siis, kui te ei otsi sõnu. Oletame, et soovite näha, kui palju liiklust tuleb botidelt. Esimene mõte on otsida logidest sõna 'bot'. Nii leiate mõned robotid: Googlebot, Bingbot ja paljud teised. Kuid siin on 'bot' mitte sõna, vaid selle osa. Kui otsida 'bot' indeksist, ei leia me teateid sõnaga 'Googlebot'. Kui kontrollida igat sõna indeksis ja seejärel skaneerida indeks leitud märksõnade järgi, aeglustub otsing oluliselt. Seetõttu ei luba mõned logitöötlusprogrammid otsida sõnaosi või (halvimal juhul) võimaldavad kasutada spetsiaalset süntaksit, millel on madalam jõudlus. Me soovime seda vältida.
Veel üks probleem on kirjavahemärgid. Kas soovite leida kõik päringud, mis tulenevad 50.168.29.7? Что насчёт отладки логов, содержащих [error]? Индексы обычно пропускают пунктуацию.
Lõpuks armastavad insenerid võimsaid tööriistu ja mõnikord saab probleemi lahendada ainult regulaaravaldistega. Märksõnade indeks ei sobi selleks väga hästi.
Lisaks on indeksid keerulised. Iga teade tuleb lisada mitmesse märksõnaloendisse. Need loendid tuleb pidevalt hoida otsinguks sobivas vormingus. Päringud, mis sisaldavad fraase, sõnaliike või regulaaravaldisi, tuleb tõlkida mitu loendit, seejärel skaneerida ja koguda kokku lõplik komplekt. Ulatusliku mitme kasutajaga teenuse kontekstis tekitab selline keerukus jõudlusprobleeme, mis ei ole nähtavad algoritmide analüüsimisel.
Märksõnade indeksid võtavad ka palju ruumi ning salvestamine on logihaldussüsteemis peamine kuluartikkel.
Teisest küljest võib iga otsingu jaoks kuluda palju arvutusvõimet. Meie kasutajad hindavad kiiret otsingut ainulaadsete päringute jaoks, kuid selliseid päringuid tehakse suhteliselt harva. Tüüpiliste otsingupäringute, näiteks juhtpaneeli jaoks, rakendame spetsiaalseid meetodeid (selgitame neid järgmises artiklis). Teised päringud on piisavalt haruldased, nii et harva tuleb töödelda rohkem kui ühte korraga. Kuid see ei tähenda, et meie serverid pole hõivatud: need on tegelevad uute sõnumite vastuvõtmise, analüüsi ja tihendamise, teavituste hindamise, vanade andmete tihendamise jne tööd. Seega on meil piisavalt protsessoreid, mida saab kasutada päringute täitmiseks.
Brute force töötab, kui teil on proovivõime (ja palju jõudu)
Brute force töötab kõige paremini lihtsate ülesannete puhul, kus on väikesed sisemised tsüklid. Sageli on võimalik sisemist tsüklit optimeerida väga kõrgete kiiruseni. Kui kood on keeruline, on selle optimeerimine oluliselt raskem.
Alguses oli meie otsingukoodis üsna suur sisemine tsükkel. Salvestame sõnumid 4K lehtedel; iga leht sisaldab teatud sõnumeid (UTF-8 vormingus) ja metainfot iga sõnumi kohta. Metainfos on struktuur, kus on kodeeritud väärtuse pikkus, sõnumi sisemine ID ja muud väljad. Otsingutsükkel nägi välja selline:

See on lihtsustatud versioon tegelikust koodist. Kuid isegi siin on näha mitmeid objekti paigutusi, andmekopeerimisi ja funktsioonikõnesid. JVM optimeerib funktsioonikõnesid ja eraldab efemeerseid objekte üsna hästi, seega töötas see kood paremini, kui me seda väärisime. Testimise ajal kasutasid kliendid seda üsna edukalt. Kuid lõpuks liikusime uuele tasemele.
(Te võid küsida, miks me salvestame sõnumeid sellises formaadis, mille lehe suurus on 4K, koos teksti ja metaandmetega, mitte ei tööta logidega otse. On palju põhjuseid, mis tulenevad sellest, et Scalyr'i sisemine mootor sarnaneb rohkem jaotatud andmebaasile kui failisüsteemile. Tekstiline otsing kombineeritakse sageli andmebaasi stiilis filtritega pärast logide parsimist. Saame samal ajal otsida paljude tuhandete logide seast, ja tavalised tekstifailid ei sobi meie tehingulise, replikeeritud, jaotatud andmehaldusüksuse jaoks).
Algul tundus, et selline kood ei sobi hästi jõhkrale jõu meetodile optimeerimiseks. "Tõeline töö" on String.indexOf() isegi ei domineerinud CPU profiilis. See tähendab, et ainult selle meetodi optimeerimine ei tooks olulisi tulemusi.
Nii juhtus, et salvestame metaandmed iga lehe alguses ja kõigi sõnumite tekst UTF-8 pakituna teises otsas. Kasutades seda, ümber kirjutasime tsükli, et otsida kohe kogu lehte:

See versioon töötab otse esitluses raw byte[] ja otsib kõiki sõnumeid kohe kogu 4K lehe ulatuses.
Seda on palju lihtsam optimeerida jõhkrale jõu meetodi jaoks. Sisemine otsingutsükkel kutsutakse esile samal ajal kogu 4K lehes, mitte eraldi iga sõnumi jaoks. Ei ole ei andmete kopeerimist ega objektide eraldamist. Ja keerulisemad operatsioonid metaandmetega kutsub esile ainult positiivse tulemuse korral, mitte iga sõnumi jaoks. Nii et oleme välistanud tonni üleliigset koormust ja ülejäänud koormus on kontsentreeritud väikesesse sisemisse otsingutsüklisse, mis sobib hästi edasisteks optimeerimisteks.
Meie tegelik otsingu algoritm põhineb . See sarnaneb Boyer-Moore algoritmile, kus iga sammu ajal vaadatakse kaks bait'i, et vähendada vale vasteid.
Meie lahendus nõuab iga otsingu jaoks 64K otsingutabeli loomist, kuid see on midagi võrreldes andmegigantidega, milles me otsime. Sisemine tsükkel töötleb mitu gigabaiti sekundis ühes südamikus. Praktikas on stabiilne jõudlus umbes 1,25 GB sekundis igas südamikus ja paranemise potentsiaal on olemas. Mõningaid kulusid sisemise tsükli väliselt saab kõrvaldada ja plaanime katsetada sisemist tsüklit C-s Java asemel.
Kasutame jõudu
Arutasime, et logide otsingut saab teostada „brutaalselt“, kuid kui palju „jõudu“ meil on? Üsna palju.
1 tuum: õigesti kasutades on üks kaasaegse protsessori südamik üsna võimas iseenesest.
8 tuuma: hetkel töötame Amazon hi1.4xlarge ja i2.4xlarge SSD serveritel, millest igal on 8 südamikku (16 lõime). Nagu eelnevalt mainitud, on tavaliselt need südamikud hõivatud taustategevustega. Kui kasutaja teeb otsingu, peatatakse taustategevused, vabastades kõik 8 südamikku otsimiseks. Otsing lõpeb tavaliselt murdosa sekundi jooksul, pärast mida jätkub taustatöö (programmi regulaator tagab, et otsingupäringute äkiline langus ei segaks olulisi taustategevusi).
16 südamikku: usaldusväärsuse tagamiseks korraldame serverid master/slave gruppidesse. Iga masteri all tegutseb üks SSD server ja üks EBS. Kui peamine server langeb, võtab SSD server koheselt tema koha. Peaaegu kogu aeg töötavad master ja slave normaalselt, seega on iga andmeplokk otsinguks saadaval kahes erinevas serveris (alamsüsteemi EBS serveril on nõrk protsessor, nii et me ei arvestata seda). Jagame ülesande nende vahel, nii et meil on kokku 16 südamikku saadaval.
Palju südamikke: lähitulevikus jaotame andmed serverite vahel nii, et kõik osalevad iga mittetriviaalse päringu töötlemisel. Igat südamikku kasutatakse. [Märkus: me oleme rakendanud plaani ja suurendanud otsingu kiiruseni 1 TB/s, vt märget artikli lõpus.].
Lihtsus tagab usaldusväärsuse
Veel üks brutaalse meetodi eelis on üsna stabiilne jõudlus. Üldiselt ei ole otsing liiga tundlik ülesande ja andmekogumi detailide osas (arvan, et just sellepärast nimetatakse seda „brutaalseks“).
Märksõnade indekseerimine annab mõnikord uskumatult kiireid tulemusi, kuid teistes olukordades mitte. Oletame, et teil on 50 GB logisid, kus termin 'customer_5987235982' esineb täpselt kolm korda. Selle termini otsimine arvutab otse indeksist välja kolm asukohta ja lõppeb koheselt. Kuid keeruline otsing, mis kasutab asendajaid, võib skaneerida tuhandeid märksõnu ja võtta palju aega.
Teiselt poolt, jõhkrate meetodite abil teostatav otsimine toimub enam-vähem ühtlaselt. Pika sõna otsimine on parem, kuid isegi ühe märgi otsimine toimub piisavalt kiiresti.
Jõhkrate meetodite lihtsus tähendab, et nende jõudlus on lähedane teoreetilisele maksimumile. Siin on vähem võimalusi ettenägematuks ketaste ülekoormamiseks, lukustumise konfliktideks, pointeri jahtimiseks ning tuhandeks muuks ebaõnnestumise põhjuseks. Vaatasin just Scalyr kasutajate poolt meie kõige hõivatumal serveril eelmise nädala jooksul tehtud päringuid. Kokku oli 14 000 päringut. Täpselt kaheksa neist võtsid kauem kui üks sekund; 99% tehti 111 millisekundi piires (kui te ei ole logide analüüsi tööriistu kasutanud, uskuge mind: see on kiire).
Stabiilne ja usaldusväärne jõudlus on teenuse kasutusmugavuse jaoks oluline. Kui see aeg-ajalt jääb seisma, tajuvad kasutajad seda kui usaldusväärset ja ei taha seda kasutada.
Logide otsimine tegevuses
Siin on väike animatsioon, mis näitab Scalyr otsimist tegevuses. Meil on demo-konto, kuhu impordime iga sündmuse igas avalikus Githubi hoidlas. Selles demonstreeringus uurin ma andmeid nädala jooksul: umbes 600 MB töötlemata logisid.
Video on salvestatud reaalajas, ilma eriliste ettevalmistusteta, minu lauaarvutis (umbes 5000 kilomeetrit serverist). Teie nähtav jõudlus on suuresti tänu , samuti kiirele ja usaldusväärsele tagaplaanile. Iga kord, kui tekib paus ilma 'loading' indikaatorita, siis teen mina pausi, et saaksite lugeda, mida ma plaanin vajutada.

Kokkuvõtteks
Suure andmemahtude töötlemisel on oluline valida hea algoritm, kuid "hea" ei tähenda tingimata "kompleksne". Mõelge, kuidas teie kood reaalses elus toimib. Teoreetiline analüüs jätab välja mõned tegurid, mis võivad reaalses maailmas suurt tähtsust omada. Lihtsamad algoritmid on kergemini optimeeritavad ja nad on stabiilsemad äärmuslikes olukordades.
Mõelge ka konteksti, kus kood töötab. Meie puhul on vaja piisavalt võimsaid servereid, et hallata taustülesandeid. Kasutajad käivitavad otsingu suhteliselt harva, mistõttu saame laenata terve grupi servereid lühiõige perioodi jaoks, mis on vajalik iga otsingu täitmiseks.
Jõhkrate meetodite abil oleme rakendanud kiire, usaldusväärse ja paindliku logide otsingu. Loodame, et need ideed osutuvad teie projektides kasulikeks.
Redigeerimine: pealkiri ja tekst on muutunud "Otsing kiirusel 20 GB sekundis" pealkirjaks "Otsing kiirusel 1 TB sekundis", et peegeldada viimase paari aasta jooksul toimunud jõudluse kasvu. See kiirusetõus on peamiselt seotud EC2 serverite tüübi ja arvu muutumisega, mida täna kasutame suurenenud kliendibaasi teenindamiseks. Varsti on oodata muudatusi, mis toovad kaasa veel ühe terava efektiivsuse tõusu, ja ootame põnevusega võimalust sellest rääkida.
Allikas: habr.com
