Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Ju ofroj të njihem me interpretimin e raportit të fundit të vitit 2019 nga Aleksandër Valjalkin "Optimizimet e Go në VictoriaMetrics"

VictoriaMetrics — njĂ« DBMS e shpejtĂ« dhe e shkallĂ«zueshme pĂ«r ruajtjen dhe pĂ«rpunimin e tĂ« dhĂ«nave nĂ« formĂ«n e serive temporale (ncifuli pĂ«rbĂ«het nga koha dhe njĂ« grup vlerash pĂ«rkatĂ«se tĂ« kĂ«saj kohe, pĂ«r shembull, tĂ« marra pĂ«rmes njĂ« sondazhi periodik tĂ« gjendjes sĂ« sensorĂ«ve ose mbledhjes sĂ« metrikeve).

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Ja lidhja pĂ«r videon e kĂ«tij raporti — https://youtu.be/MZ5P21j_HLE

Slida

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Do të flas pak për veten time. Unë jam Aleksandër Valjalkin. Këtu është akuntemi im në GitHub. Pasioni im janë Go dhe optimizimi i performancës. Kam shkruar shumë biblioteka të ndryshme, disa të dobishme e disa jo. Ato fillojnë ose me fast, ose me quick prefiksin.

Aktualisht po punoj nĂ« VictoriaMetrics. ÇfarĂ« Ă«shtĂ« kjo dhe çfarĂ« bĂ«j atje? KĂ«tĂ« do ta shpjegoj nĂ« kĂ«tĂ« prezantim.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Plani i raportit është si më poshtë:

  • Fillimisht do t'ju tregoj se çfarĂ« Ă«shtĂ« VictoriaMetrics.
  • MĂ« pas do tĂ« shpjegoj se çfarĂ« janĂ« seritĂ« temporale.
  • MĂ« pas do tĂ« flas pĂ«r se si funksionon njĂ« bazĂ« tĂ« dhĂ«nash pĂ«r seritĂ« temporale.
  • MĂ« tej do tĂ« flas pĂ«r arkitekturĂ«n e bazĂ«s sĂ« tĂ« dhĂ«nave: nga çfarĂ« pĂ«rbĂ«het ajo.
  • Dhe mĂ« pas do tĂ« kalojmĂ« te optimizimet qĂ« janĂ« nĂ« VictoriaMetrics. KĂ«tu pĂ«rfshihet optimizimi i indeksit tĂ« invertuar dhe optimizimi pĂ«r implementimin e bitset nĂ« Go.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Dikush nĂ« audiencĂ« di se çfarĂ« Ă«shtĂ« VictoriaMetrics? Uau, shumĂ« njerĂ«z e dinĂ« kĂ«tĂ«. Kjo Ă«shtĂ« njĂ« lajm i mirĂ«. PĂ«r ata qĂ« nuk e dinĂ« – Ă«shtĂ« njĂ« bazĂ« tĂ« dhĂ«nash pĂ«r seritĂ« temporale. Ajo bazohet nĂ« arkitekturĂ«n ClickHouse, mbi disa detaje tĂ« implementimit tĂ« ClickHouse. PĂ«r shembull, mbi aspekte si: MergeTree, llogaritje tĂ« paralelĂ« nĂ« tĂ« gjitha bĂ«rthamat e disponueshme tĂ« procesorit dhe optimizimi i performancĂ«s pĂ«rmes punĂ«s mbi blloqet e tĂ« dhĂ«nave qĂ« vendosen nĂ« cache-in e procesorit.

VictoriaMetrics ofron kompresimin më të mirë të të dhënave krahasuar me bazat e tjera të të dhënave për seritë temporale.

Ajo shkallĂ«zohet vertikalisht — dmth. mund tĂ« shtoni njĂ« numĂ«r mĂ« tĂ« madh procesorĂ«sh, njĂ« sasi mĂ« tĂ« madhe tĂ« memories operativĂ« nĂ« njĂ« kompjuter. VictoriaMetrics do tĂ« jetĂ« nĂ« gjendje tĂ« shfrytĂ«zojĂ« kĂ«to burime tĂ« disponueshme dhe do tĂ« rrisĂ« performancĂ«n lineare.

Po ashtu, VictoriaMetrics shkallĂ«zohet horizontalisht — dmth. mund tĂ« shtoni node tĂ« tjera nĂ« klasterin e VictoriaMetrics, dhe performanca e saj do tĂ« rritet pothuajse linearisht.

Siç e keni marrë me mend, VictoriaMetrics është një bazë të dhënash e shpejtë, sepse nuk mund të shkruaj për të tjera. Dhe ajo është e shkruar në Go, prandaj po flas për të në këtë takim.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Kush e di se çfarĂ« Ă«shtĂ« njĂ« seri e pĂ«rkohshme? Edhe shumĂ« njerĂ«z e dinĂ« kĂ«tĂ«. NjĂ« seri e pĂ«rkohshme Ă«shtĂ« njĂ« seri çiftesh (timestamp, vlera), ku kĂ«to çifte janĂ« renditur sipas kohĂ«s. Vlera pĂ«rfaqĂ«son njĂ« numĂ«r fluturues – float64.

Çdo seri e pĂ«rkohshme identifikohet unikisht me njĂ« çelĂ«s. ÇfarĂ« pĂ«rbĂ«n ky çelĂ«s? Ai pĂ«rbĂ«het nga njĂ« grup i pandashĂ«m çiftĂ«sh çelĂ«s-vlerĂ«.

Ja njĂ« shembull i njĂ« serie tĂ« pĂ«rkohshme. ÇelĂ«si i kĂ«saj serie Ă«shtĂ« njĂ« listĂ« çiftĂ«sh: __name__="cpu_usage" – ky Ă«shtĂ« emri i metrikĂ«s, instance="my-server" – ky Ă«shtĂ« kompjuteri ku Ă«shtĂ« mbledhur kjo metrikĂ«, datacenter="us-east" – ky Ă«shtĂ« qendra e tĂ« dhĂ«nave ku ndodhet ky kompjuter.

Kemi marrĂ« emrin e njĂ« serie tĂ« pĂ«rkohshme, e cila pĂ«rbĂ«het nga tri çiftĂ« çelĂ«s-vlerĂ«. Ky çelĂ«s i korrespondon njĂ« liste çiftĂ«sh (timestamp, value). t1, t3, t3, ..., tN – kĂ«to janĂ« timestamps, 10, 20, 12, ..., 15 – vlerat pĂ«rkatĂ«se. Kjo Ă«shtĂ« pĂ«rdorimi i CPU nĂ« momentin e caktuar pĂ«r kĂ«tĂ« seri.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Ku mund të përdoren seritë e përkohshme? A ka ndokush ide?

  • NĂ« DevOps, mund tĂ« masim ngarkesĂ«n e CPU, RAM, rrjetit, rps, numrin e gabimeve etj.
  • IoT – mund tĂ« masim temperaturĂ«n, presionin, koordinatat gjeografike dhe ndonjĂ« gjĂ« tjetĂ«r.
  • Po ashtu financat – mund tĂ« monitorojmĂ« çmimet e aksioneve dhe valutave.
  • PĂ«rveç kĂ«saj, seritĂ« e pĂ«rkohshme mund tĂ« pĂ«rdoren pĂ«r monitorimin e proceseve prodhuese nĂ« fabrika. Kemi pĂ«rdorues qĂ« pĂ«rdorin VictoriaMetrics pĂ«r monitorimin e turbinave tĂ« erĂ«s, pĂ«r robotĂ«t.
  • Gjithashtu, seritĂ« e pĂ«rkohshme janĂ« tĂ« dobishme pĂ«r grumbullimin e informacionit nga sensorĂ«t e pajisjeve tĂ« ndryshme. PĂ«r shembull, pĂ«r motorin; pĂ«r matjen e presionit nĂ« pneun; pĂ«r matjen e shpejtĂ«sisĂ«, distancĂ«s; pĂ«r matjen e konsumit tĂ« benzinĂ«s etj.
  • Po ashtu, seritĂ« e pĂ«rkohshme mund tĂ« pĂ«rdoren pĂ«r monitorimin e aeroplanĂ«ve. NĂ« çdo aeroplan ka njĂ« kuti tĂ« zezĂ« qĂ« mbledh seritĂ« e pĂ«rkohshme pĂ«r parametrat e ndryshĂ«m tĂ« shĂ«ndetit tĂ« aeroplanit. SeritĂ« e pĂ«rkohshme pĂ«rdoren gjithashtu nĂ« industrinĂ« ajrore.
  • ShĂ«ndetĂ«sia – kjo pĂ«rfshin presionin e gjakut, pulsin etj.

Ndoshta ka edhe aplikacione të tjera që kam harruar, por shpresoj se kuptuat që seritë e përkohshme përdoren aktivisht në botën moderne. Dhe volumi i përdorimit të tyre po rritet cdo vit.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Për çfarë është e nevojshme një bazë të dhënash për seritë e përkohshme? Pse nuk mund të përdorim një bazë të dhënash racionale për ruajtjen e serive të përkohshme?

Sepse në seritë e kohës zakonisht ka një sasi të madhe informacioni, e cila është e vështirë të ruhet dhe të përpunohet në databaza të zakonshme. Prandaj, janë shfaqur databaza speciale për seritë e kohës. Këto baza ruajnë në mënyrë efektive pikat (timestamp, value) me një çelës të caktuar. Ato ofrojnë API për të lexuar të dhënat e ruajtura sipas çelësit, për një çift çelës-vlerë, ose për disa çifte të tilla, ose për regexp. Për shembull, nëse dëshironi të gjeni ngarkesën e procesorit të të gjitha shërbimeve tuaja në qendrën e të dhënave në Amerikë, duhet të përdorni një kërkesë të tillë.

Zakonisht, databazat për seritë e kohës paraqesin gjuhë kërkese të specializuara, sepse SQL për seritë e kohës nuk është shumë i përshtatshëm. Edhe pse ka databaza që mbështesin SQL, ai nuk është shumë i përshtatshëm. Gjuha më të përshtatshme janë PromQL, InfluxQL, Flux, Q. Shpresoj që dikush të ketë dëgjuar për së paku një nga këto gjuhë. Për PromQL, ndoshta shumë kanë dëgjuar. Kjo është gjuha e kërkesës për Prometheus.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Këtu është si duket arkitektura e një databaze moderne për seritë e kohës, me shembullin e VictoriaMetrics.

Ajo pĂ«rbĂ«het nga dy pjesĂ«. ËshtĂ« njĂ« depo pĂ«r indeksin e invertuar dhe njĂ« depo pĂ«r vlerat e serive tĂ« kohĂ«s. KĂ«to depo janĂ« tĂ« ndara.

Kur vjen një shënim i ri në databazë, ne së pari kthehemi në indeksin e invertuar për të gjetur identifikuesin e serisë së kohës sipas një grupi të caktuar label=value për këtë metrikë. Gjejmë këtë identifikues dhe ruajmë vlerën në depo të dhënash.

Kur vjen një kërkesë për tërheqjen e të dhënave nga TSDB, ne së pari shohim në indeksin e invertuar. Nxjerrim të gjitha timeseries_ids shënimet që përputhen me këtë grup label=value. Dhe pastaj nxjerrim të gjitha të dhënat e nevojshme nga depoja e të dhënave, e indeksuar sipas timeseries_ids.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Le të shohim një shembull se si databaza për seritë e kohës përpunon një kërkesë të ardhur select.

  • SĂ« pari, ajo nxjerr tĂ« gjitha timeseries_ids nga indeksi i invertuar, qĂ« pĂ«rmbajnĂ« çiftet e caktuara label=value, ose pĂ«rmbushin njĂ« shprehje tĂ« dhĂ«nĂ« tĂ« rregullt.
  • Pastaj ajo nxjerr tĂ« gjitha pikat e tĂ« dhĂ«nave nga depoja e dhĂ«nash nĂ« njĂ« interval tĂ« caktuar kohor pĂ«r tĂ« gjeturat timeseries_ids.
  • Pas kĂ«saj, databaza kryen disa llogaritje mbi kĂ«to pika tĂ« tĂ« dhĂ«nave, sipas kĂ«rkesĂ«s sĂ« pĂ«rdoruesit. Dhe mĂ« pas kthen pĂ«rgjigjen.

NĂ« kĂ«tĂ« prezantim, do t'ju tregoj pĂ«r pjesĂ«n e parĂ«. Kjo Ă«shtĂ« kĂ«rkimi timeseries_ids pĂ«r indeksin e invertuar. Mund tĂ« shihni pjesĂ«n e dytĂ« dhe tĂ« tretĂ« mĂ« vonĂ«. kodet burimore tĂ« VictoriaMetrics, ose mund tĂ« prisni derisa tĂ« pĂ«rgatis dokumente tĂ« tjera 🙂

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Le të fillojmë me indeksin e invertuar. Disa njerëz mund të mendojnë se është e thjeshtë. Kush e njeh indeksin e invertuar dhe si funksionon? O, tashmë nuk ka shumë njerëz. Le të përpiqemi të kuptojmë se çfarë është.

NĂ« tĂ« vĂ«rtetĂ«, gjithçka Ă«shtĂ« e thjeshtĂ«. ËshtĂ« njĂ« fjalor qĂ« lidh çelĂ«sin me vlerĂ«n. ÇfarĂ« Ă«shtĂ« çelĂ«si? Kjo çift label=valueindex label dhe vlera — janĂ« fjalĂ«. NĂ«se vlerat janĂ« njĂ« grup timeseries_ids, i cili pĂ«rmban çiftin e caktuar label=value.

Indeksi i invertuar lejon të gjenden shpejt të gjitha timeseries_ids, që kanë label=value.

Gjithashtu, ai lejon të gjeni shpejt timeseries_ids seritë e kohës për disa çifte label=value, ose për çifte label=regexp. Si ndodh kjo? Përmes gjetjes së prerjes së një grumbulli timeseries_ids për çdo çift label=value.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Le të shqyrtojmë realizimet e ndryshme të indeksit të invertuar. Le të fillojmë me realizimin më të thjeshtë naiv. Ai duket kështu.

Funksioni getMetricIDs merr njĂ« listĂ« fjalish. Çdo fjalĂ« pĂ«rmban label=value. Kjo funksion kthen njĂ« listĂ« metricIDs.

Si funksionon? Këtu kemi një variabël globale, e quajtur invertedIndex. Ky është një fjalor i zakonshëm (map), që lidh fjalinë me slice int-i. Fjalia përmban label=value.

Realizimi i funksionit: marrim metricIDs për të parën label=value, pastaj kalojmë në të tjerat label=value, marrim metricIDs për to. Dhe thërrasim funksionin intersectInts, për të cilin do të flitet më vonë. Dhe ky funksion kthen prerjen e këtyre listave.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Siç e shihni, realizimi i indeksit të invertuar nuk është shumë i komplikuar. Por kjo është një realizim naiv. Cilat janë disavantazhet e saj? Disavantazhi kryesor i realizimit naiv është se indeksi i tillë i invertuar ruhet në memorien operative. Pas rinisjes së aplikacionit, ne humbasim këtë indeks. Nuk ka ruajtje të këtij indeksi në disk. Për një bazë të dhënash, një indeks i tillë i invertuar nuk është i përshtatshëm.

Disavantazhi i dytĂ« gjithashtu lidhet me memorien. Indeksi i invertuar duhet tĂ« vendoset nĂ« memorien operative. NĂ«se ai kalon madhĂ«sinĂ« e memories operative, Ă«shtĂ« e qartĂ« se do tĂ« marrim – gabim 'out of memory'. Dhe programi nuk do tĂ« funksionojĂ«.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Kjo problem mund të zgjidhet përmes zgjidhjeve të gatshme si LevelDB, ose RocksDB.

Në përmbledhje, na nevojitet një bazë të dhënash që lejon të bëjmë shpejt tre operacione.

  • Operacioni i parĂ« Ă«shtĂ« shĂ«nimi çelĂ«s-vlerĂ« nĂ« kĂ«tĂ« bazĂ«. Ajo e bĂ«n kĂ«tĂ« shumĂ« shpejt, ku çelĂ«s-vlerĂ« janĂ« vargje tĂ« rastĂ«sishme.
  • Operacioni i dytĂ« Ă«shtĂ« kĂ«rkimi i shpejtĂ« tĂ« vlerĂ«s sipas çelĂ«sit tĂ« caktuar.
  • Dhe operacioni i tretĂ« Ă«shtĂ« kĂ«rkimi i shpejtĂ« tĂ« tĂ« gjitha vlerave sipas prefixit tĂ« caktuar.

LevelDB dhe RocksDB – kĂ«to baza janĂ« zhvilluar nĂ« Google dhe Facebook. NĂ« fillim doli LevelDB. Pastaj ekipi nga Facebook mori LevelDB dhe filloi ta pĂ«rmirĂ«sojĂ«, duke bĂ«rĂ« RocksDB. Tani, nĂ« RocksDB brenda Facebook-ut punojnĂ« pothuajse tĂ« gjitha databazat e brendshme, pĂ«rfshirĂ« se ata kanĂ« transferuar MySQL nĂ« RocksDB. Ata e quajtĂ«n MyRocks.

Indeksi i invertuar mund tĂ« realizohet me ndihmĂ«n e LevelDB. Si mund ta bĂ«jmĂ« kĂ«tĂ«? RuajmĂ« si çelĂ«s label=value. NdĂ«rsa si vlerĂ« – identifikuesin e serisĂ« kohore, ku ndodhet çifti label=value.

Nëse kemi shumë seri kohore me këtë çift label=value, do të ketë shumë rreshta në këtë bazë të dhënash me çelësa të njëjtë dhe me vlera të ndryshme. timeseries_idsPër të marrë listën e të gjitha timeseries_ids, që fillojnë me këtë label=prefix, ne bëjmë një skanimin e gamës, për të cilën kjo bazë është optimizuar. Kjo do të thotë, ne zgjedhim të gjithë rreshtat, që fillojnë me label=prefix dhe marrim vlerat e nevojshme. timeseries_ids.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Ja një implementim i përafërt, si do të dukej në Go. Ne kemi një indeks të invertuar. Kjo është LevelDB.

Funksioni është i njëjtë, si për implementimin naive. Ajo pothuajse përsërit rresht për rresht implementimin naive. Momentin e vetëm është që në vend të qasjes në map ne qasemi në indeksin e invertuar. Ne nxjerrim të gjitha vlerat për çiftin e parë. label=valuePastaj kalojmë përmes të gjithë çifteve të mbetura label=value dhe nxjerrim grupe të përkatshme të metricIDs për to. Më pas gjejmë kryqëzimin.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Duket gjithçka mirë, por në këtë zgjidhje ka disavantazhe. VictoriaMetrics fillimisht implementoi indeksin e invertuar mbi bazën e LevelDB. Por në fund, u detyrua të heq dorë nga ai.

Pse? Sepse LevelDB Ă«shtĂ« mĂ« i ngadaltĂ« se implementimi naive. NĂ« implementimin naive sipas çelĂ«sit tĂ« caktuar ne nĂ« menjĂ« nxjerrim tĂ« gjithĂ« slice-in metricIDs. Kjo Ă«shtĂ« njĂ« operacion shumĂ« i shpejtĂ« – tĂ« gjithĂ« slice-in Ă«shtĂ« gati pĂ«r t'u pĂ«rdorur.

Në LevelDB, për çdo thirrje të funksionit GetValues duhet të kalojmë përmes të gjithë rreshtave, që fillojnë me label=value. Dhe për çdo rresht të nxjerrim vlerën timeseries_ids. Nga këta timeseries_ids të mbledhim slice-in e këtyre timeseries_ids. Èshte e qartë se kjo është shumë më e ngadaltë se sa thjesht të qasemi në një map të zakonshëm sipas çelësit.

Disavantazhi i dytĂ« Ă«shtĂ« se LevelDB Ă«shtĂ« shkruar nĂ« C. Thirrja e funksioneve C nga Go nuk Ă«shtĂ« shumĂ« e shpejtĂ«. Merr qindra nanosekonda. Kjo nuk Ă«shtĂ« shumĂ« e shpejtĂ«, sepse krahasuar me thirrjen e zakonshme tĂ« funksionit tĂ« shkruar nĂ« Go, e cila merr 1-5 nanosekonda, diferenca nĂ« performancĂ« Ă«shtĂ« disa herĂ« mĂ« e lartĂ«. PĂ«r VictoriaMetrics, ky ishte njĂ« disavantazh fatal 🙂

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Prandaj, unë shkrova një implementim të vetin të indeksit të kthyer. Dhe e quajta mergeset.

Mergeset është i bazuar në strukturën e të dhënave MergeTree. Kjo strukturë e të dhënave është huazuar nga ClickHouse. Qartë, mergeset duhet të jetë optimizuar për kërkimin e shpejtë timeseries_ids për një çelës të caktuar. Mergeset është shkruar plotësisht në Go. Ju mund të shihni kodin burimor të VictoriaMetrics në GitHub. Implementimi i mergeset ndodhet në dosjen /lib/mergeset. Ju mund të provoni të kuptoni se çfarë ndodh atje.

API i mergeset është shumë i ngjashëm me LevelDB dhe RocksDB. Pra, ai lejon ruajtjen e shpejtë të regjistrimeve të reja dhe përzgjedhjen e shpejtë të regjistrimeve sipas një prefiksi të caktuar.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Për disavantazhet e mergeset do të flasim më vonë. Tani, do të diskutojmë se cilat probleme u shfaqën me VictoriaMetrics në prodhim gjatë implementimit të indeksit të kthyer.

Pse ndodhi kjo?

Arsyeja e parë është shkalla e lartë e ndryshimit. Në përkthim në shqip - kjo është ndërrimi i shpeshtë i serive të kohës. Kjo ndodh kur një seri e kohës përfundon dhe nis një seri e re, ose fillojnë shumë seri të reja të kohës. Dhe kjo ndodh shpesh.

Arsyeja e dytë është numri i madh i serive të kohës. Fillimisht, kur monitorimi po fitonte popullaritet, numri i serive të kohës ishte i vogël. Për shembull, për çdo computer duhet të monitorohet ngarkesa e procesorit, memories, rrjetit dhe diskut. 4 seri të kohës për çdo computer. Keni, për shembull, 100 kompjuterë dhe 400 seri të kohës. Kjo është shumë pak.

Me kalimin e kohës, njerëzit e shpikën se mund të masin informacione më të detajuara. Për shembull, të maten ngarkesat e çdo bërthame të veçantë të procesorit, jo thjesht të gjithë procesorit. Nëse keni 40 bërthama procesori, atje keni gjithashtu 40 herë më shumë seri të kohës për të matur ngarkesën e procesorit.

Por megjithatĂ«, kjo nuk Ă«shtĂ« gjithçka. Çdo bĂ«rthamĂ« procesori mund tĂ« ketĂ« disa gjendje si idle, kur Ă«shtĂ« nĂ« pritje. Gjithashtu ka punĂ« nĂ« user space, punĂ« nĂ« kernel space dhe gjendje tĂ« tjera. Çdo gjendje e tillĂ« gjithashtu mund tĂ« matet si njĂ« seri e veçantĂ« e kohĂ«s. Kjo rrit nĂ« mĂ«nyrĂ« tĂ« konsiderueshme numrin e serive nĂ« 7-8 herĂ«.

Nga një metrikë kemi marrë 40 x 8 = 320 metrika vetëm për një kompjuter. Duke e shumëfishuar me 100, marim 32,000 në vend të 400.

Pastaj u shfaq Kubernetes. Dhe akoma pĂ«rkeqĂ«soi situatĂ«n, sepse nĂ« Kubernetes mund tĂ« hostohen shumĂ« shĂ«rbime tĂ« ndryshme. Çdo shĂ«rbim nĂ« Kubernetes pĂ«rbĂ«het nga shumĂ« pod, dhe tĂ« gjitha kĂ«to duhet tĂ« monitorohen. PĂ«rveç kĂ«saj, kemi njĂ« deployment tĂ« vazhdueshĂ«m tĂ« versioneve tĂ« reja tĂ« shĂ«rbimeve tuaja. PĂ«r çdo version tĂ« ri, duhet tĂ« krijohen seria tĂ« reja tĂ« kohĂ«s. Si pĂ«rfundim, numri i serive tĂ« kohĂ«s rritet nĂ« mĂ«nyrĂ« eksponenciale dhe ne pĂ«rballemi me problemin e numrit tĂ« madh tĂ« serive tĂ« kohĂ«s, qĂ« quhet high-cardinality. VictoriaMetrics pĂ«rballet me tĂ« nĂ« mĂ«nyrĂ« efektive krahasuar me bazat e tjera tĂ« tĂ« dhĂ«nave pĂ«r seritĂ« e kohĂ«s.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Le të shqyrtojmë më hollësisht high churn rate. Pse ekziston high churn rate në prodhim? Sepse disa vlera të etiketave dhe etiketave ndryshojnë vazhdimisht.

Për shembull, le të marrim Kubernetes, ku ekziston koncepti deployment, dmth. kur ndodh një version i ri i aplikacionit tuaj. Zhvilluesit e Kubernetes për një arsye të panjohur vendosën të shtojnë id e deployment në etiketë.

ÇfarĂ« ndodhi si rezultat? TĂ« gjithĂ« seritĂ« e vjetra tĂ« kohĂ«s ndĂ«rpriten me çdo deployment tĂ« ri, dhe nĂ« vend tĂ« tyre fillojnĂ« seritĂ« e reja tĂ« kohĂ«s me vlerĂ«n e re tĂ« etiketĂ«s deployment_id. Ka mundĂ«si qĂ« tĂ« ketĂ« qindra mijĂ«ra dhe madje miliona tĂ« tilla serish.

NjĂ« karakteristikĂ« e rĂ«ndĂ«sishme e gjithĂ« kĂ«saj Ă«shtĂ« se numri total i serive tĂ« kohĂ«s rritet, por numri i serive tĂ« kohĂ«s qĂ« janĂ« aktualisht aktive, tĂ« cilat marrin tĂ« dhĂ«na, mbetet konstant. Ky gjendje quhet – high churn rate.

Problemi kryesor i high churn rate është të sigurojë një shpejtësi konstante kërkimi për të gjitha seritë e kohës në një grup të caktuar etiketash për një interval të caktuar kohor. Zakonisht, ky është një interval kohor për orët e fundit ose për ditët e fundit.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Si e zgjidhim këtë problem? Këtu është një variant i parë. Kjo është ndarja e indeksit të përmbysur në pjesë të pavarura sipas kohës. Do thotë, kalon një interval kohe, përfundojmë punën me indeksin e përmbysur aktual. Dhe krijojmë një indeks të ri të përmbysur. Kalon një tjetër interval kohe, krijojmë një tjetër dhe një tjetër.

Dhe gjatë zgjedhjes nga këto indekse të përmbysura ne gjejmë një grup indekse të përmbysura që bien brenda intervalit të caktuar. Dhe, përkatësisht, zgjedhim aty id-të e serive temporale.

Kjo lejon kursimin e burimeve, sepse nuk na nevojitet të shqyrtojmë pjesët që nuk bien brenda intervalit të caktuar. Do thotë, zakonisht, nëse po zgjedhim të dhëna për orën e fundit, atëherë për intervalet e mëparshme të kohës ne anashkalojmë kërkesat.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Ka edhe një variant tjetër për zgjidhjen e këtij problemi. Kjo është ruajtja e një liste të veçantë të id-ve të serive temporale për çdo ditë, të cilat u gjendën gjatë atij ditë.

Avantazhi i kësaj zgjidhjeje në krahasim me zgjidhjen e mëparshme është se ne nuk e ripërsërisim informacionin mbi seritë temporale që nuk zhduken me kalimin e kohës. Ato janë gjithmonë aty dhe nuk ndryshojnë.

Disavantazhi është se një zgjidhje e tillë është më e komplikuar në realizim dhe më e vështirë për debug. Dhe VictoriaMetrics e zgjodhi këtë zgjidhje. Kjo ndodhi historikisht. Kjo zgjidhje gjithashtu tregon rezultate të kënaqshme, në krahasim me të parën. Sepse kjo zgjidhje nuk u realizua për shkak se duhej të ripërsëritnim të dhënat në çdo pjesë për seritë temporale që nuk ndryshojnë, domethënë ato që nuk zhduken me kalimin e kohës. VictoriaMetrics është optimizuar fillimisht për konsumimin e hapësirës në disk, dhe realizimi i mëparshëm e përkeqësoi këtë konsum. Ndërsa kjo realizim është më i përshtatshëm për minimizimin e konsumit të hapësirës në disk, prandaj u zgjodh.

Duhej të përballem me të. Lufta qëndronte në atë se në këtë realizim duhet të përzgjedhësh gjithmonë një numër shumë më të madh timeseries_ids për të dhënat, sesa kur indeksi i përmbysur është i ndarë sipas kohës.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Si e zgjidhĂ«m kĂ«tĂ« problem? E zgjodhĂ«m nĂ« njĂ« mĂ«nyrĂ« origjinale – duke ruajtur disa identifikues tĂ« serive temporale nĂ« secilĂ«n tĂ« dhĂ«nĂ« tĂ« indeksit tĂ« pĂ«rmbysur nĂ« vend tĂ« njĂ« identifikuesi tĂ« vetĂ«m. Do thotĂ«, ne kemi njĂ« çelĂ«s label=value, qĂ« ndodh nĂ« çdo rresht temporal. Dhe tani ne ruajmĂ« disa timeseries_ids nĂ« njĂ« regjistĂ«r.

Ja një shembull. Më parë kishim N regjistra, ndërsa tani kemi një regjistër, me një prefiks të njëjtë si të tjerët. Regjistri i mëparshëm kishte një vlerë që përmbante të gjitha id-të e rreshtave temporale.

Kjo ka lejuar rritjen e shpejtësisë së skanimit të këtij indeksi të përmbysur deri në 10 herë. Dhe ka lejuar reduktimin e konsumit të memories për cache, sepse tani ne ruajmë vargun label=value vetëm një herë në cache së bashku me N herë. Dhe ky varg mund të jetë i madh, nëse keni rreshta të gjatë në etiketa dhe labels që i pëlqen t'i fusë atje Kubernetes.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

NjĂ« variant tjetĂ«r pĂ«rshpejtimi i kĂ«rkimit nĂ« indeksin e pĂ«rmbysur Ă«shtĂ« sharding. Krijimi i indekseve tĂ« shumta tĂ« pĂ«rmbysura nĂ« vend tĂ« njĂ« dhe sharding tĂ« tĂ« dhĂ«nave midis tyre sipas njĂ« çelĂ«si. Ky Ă«shtĂ« njĂ« grup key=value çiftash. Pra, ne kemi disa indekse tĂ« pavarura tĂ« pĂ«rmbysura, tĂ« cilat mund t’i marrim nĂ« pyetje paralelisht nĂ« disa procesorĂ«. Realizimet e mĂ«parshme lejonin punĂ«n vetĂ«m nĂ« modin njĂ«-procesor, dmth. skanimin e tĂ« dhĂ«nave vetĂ«m nĂ« njĂ« bĂ«rthamĂ«. Ç solution i jep mundĂ«sinĂ« pĂ«r tĂ« skanuar tĂ« dhĂ«nat menjĂ«herĂ« nĂ« disa bĂ«rthama, siç i pĂ«lqen tĂ« bĂ«jĂ« ClickHouse. KĂ«tĂ« planifikojmĂ« ta implementojmĂ«.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Dhe tani le tĂ« kthehemi te dhitĂ« tona – tek funksioni i ndĂ«rprerjes timeseries_ids. Le tĂ« shohim se cilat mund tĂ« jenĂ« realizimet. Ky funksion lejon tĂ« gjejmĂ« timeseries_ids pĂ«r njĂ« grup tĂ« caktuar label=value.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Varianti i parĂ« – Ă«shtĂ« realizimi ingenuoz. Dy cikle tĂ« ngjitur. Ja ne marrim nĂ« input tĂ« funksionit intersectInts dy slices — a dhe b. NĂ« daljen e saj ajo duhet tĂ« na kthejĂ« ndĂ«rprerjen e kĂ«tyre slices.

Realizimi ingenuoz duket kështu. Ne kalojmë përmes të gjitha vlerave nga slice a, brenda këtij cikli kalojmë përmes të gjitha vlerave të slice b. Dhe i krahasojmë ato. Nëse ata përputhen, atëherë kemi gjetur ndërprerjen. Dhe e ruajmë në rezultati.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Cilat janĂ« disavantazhet? Kompleksiteti katror – Ă«shtĂ« disavantazhi kryesor i saj. PĂ«r shembull, nĂ«se keni dimensionet e slice a dhe b me njĂ« milion, atĂ«herĂ« ky funksion kurrĂ« nuk do t'ju kthejĂ« njĂ« pĂ«rgjigje. Sepse i nevojitet njĂ« trilion iteracionesh, qĂ« Ă«shtĂ« shumĂ« edhe pĂ«r kompjuterĂ«t modernĂ«.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Realizimi i dytĂ« bazohet nĂ« hartĂ«. Ne krijojmĂ« njĂ« hartĂ«. Vendosim nĂ« kĂ«tĂ« hartĂ« tĂ« gjitha vlerat nga slice a. Pastaj kalojmĂ« me njĂ« cikĂ«l tĂ« veçantĂ« nĂ« slice b. Dhe kontrollojmĂ« – a ekziston kjo vlerĂ« nga slice b nĂ« map. NĂ«se Ă«shtĂ« atje, atĂ«herĂ« e shtojmĂ« nĂ« rezultat.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Cilat janë përparësitë? Përparësia është se këtu ka vetëm kompleksitet lineare. Kjo do të thotë se funksioni do të ekzekutohet shumë më shpejt për madhësi të mëdha slices. Për një slice me madhësi një milion, ky funksion do të ekzekutohet në 2 milion iteracione, ndryshe nga një trilion iteracione, si në funksionin e mëparshëm.

Ndërsa një disavantazh është se ky funksion kërkon më shumë memorie për të krijuar këtë map.

Disavantazhi i dytĂ« – Ă«shtĂ« overhead i madh nĂ« hashim. Ky disavantazh nuk Ă«shtĂ« shumĂ« i dukshĂ«m. Dhe pĂ«r ne nuk ishte as shumĂ« i dukshĂ«m, prandaj nĂ« fillim, nĂ« VictoriaMetrics, realizimi i bashkĂ«ngjitjes ishte pĂ«rmes map. Por mĂ« pas profilizimi tregoi se koha kryesore e procesorit shpenzohej pĂ«r tĂ« shkruar nĂ« map dhe pĂ«r tĂ« kontrolluar nĂ«se vlera Ă«shtĂ« nĂ« kĂ«tĂ« map.

Pse në këto vende shpenzohet kohë procesori? Sepse në këto rreshta Go kryen operacionin e hashimit. Kjo do të thotë se ai llogarit hash-in nga çelësi, për t'u referuar më pas në indeksin e caktuar në HashMap. Operacioni i llogaritjes së hash-it kryhet në dhjetra nanosekonda. Kjo është e ngadalshme për VictoriaMetrics.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Unë vendosa të implementoj bitset, optimizuar veçanërisht për këtë rast. Ja si duket tani bashkëngjitja e dy slices. Këtu krijojmë një bitset. Shtojmë elementet nga slice i parë. Më pas kontrollojmë praninë e këtyre elementeve në slice-in e dytë. Dhe i shtojmë në rezultat. Kjo do të thotë se pothuajse nuk ndryshon nga shembulli i mëparshëm. E vetmja gjë që ne këtu zëvendësuam duhet të jetë referimi në map me funksione të personalizuara. add dhe ka.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Nga një shikim të parë duket se kjo duhet të funksionojë më ngadalë, nëse më parë ishte përdorur një map standard, dhe tani thirren ndonjë funksion, por profilizimi tregon se kjo gjë punon 10 herë më shpejt se map standard për rastin me VictoriaMetrics.

Përveç kësaj, ajo përdor shumë më pak memorie në krahasim me realizimin në map. Sepse ne ruajmë këtu bitet në vend të vlerave tetëbyte.

Disavantazhi i kësaj realizimi është se nuk është aq e dukshme, jo triviale.

Një tjetër disavantazh që shumë njerëz mund të mos e vënë re është se kjo zbatim mund të funksionojë keq në disa raste. Domethënë, është optimizuar për një rast të caktuar, për rastin e përputhjes së identiteteve të serive temporale të VictoriaMetrics. Kjo nuk do të thotë se ajo do të jetë e përshtatshme për të gjitha rastet. Nëse përdoret gabimisht, do të kemi jo një përmirësim të performancës, por një gabim out of memory dhe ngadalësim të performancës.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Le të shqyrtojmë zbatimin e kësaj strukture. Nëse doni ta shihni, është në burimet e VictoriaMetrics, në dosjen lib/uint64set. Ajo është optimizuar pikërisht për rastin e VictoriaMetrics, ku timeseries_id është një vlerë 64-bit, ku 32 bitët e parë janë kryesisht konstantë dhe ndryshojnë vetëm 32 bitët e fundit.

Kjo strukturë të dhënash nuk ruhet në disk, ajo funksionon vetëm në memorie.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Këtu është API-i i saj. Ai nuk është shumë i komplikuar. API-u është adaptuar pikërisht për shembullin specifik të përdorimit të VictoriaMetrics. Domethënë, këtu nuk ka funksione të tepërta. Këtu janë funksionet që përdoren qartë nga VictoriaMetrics.

Ka funksione add, që shton vlera të reja. Ka një funksion ka, që kontrollon vlerat e reja. Dhe ka një funksion del, që heq vlera. Ka një funksion ndihmës len, që kthen madhësinë e grupit. Funksioni clone klonon grupin. Dhe funksioni appendto e ndryshon këtë grup në slice timeseries_ids.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Ja si duket implementimi i kësaj strukture të dhënash. Në set ka dy elemente:

  • ItemsCount – ky Ă«shtĂ« njĂ« fushĂ« ndihmĂ«se, pĂ«r tĂ« kthyer shpejt numrin e elementeve nĂ« set. Mund tĂ« ishte hequr ky fushĂ« ndihmĂ«se, por duhej ta shtonim kĂ«tu, sepse VictoriaMetrics shpesh pyet nĂ« algoritmet e saj pĂ«r gjatĂ«si bitset.

  • Fusha e dytĂ« Ă«shtĂ« buckets. Ky Ă«shtĂ« njĂ« slice i strukturĂ«s bucket32. NĂ« çdo strukturĂ« ruhet hi fusha. KĂ«to janĂ« 32 bitĂ«t e sipĂ«rm. Dhe dy slice - b16his dhe buckets nga bucket16 struktura.

Këtu ruhen 16 bitët e sipërm të pjesës së dytë të strukturës 64-bit. Dhe këtu ruhen bitsets për 16 bitët më të rinj të çdo byte.

Bucket64 është i përbërë nga një array uint64. Gjatesia llogaritet duke përdorur këto konstanta. Në një bucket16 maksimumi mund të ruajë 2^16=65536 bït. Nëse e ndajmë atë me 8, kjo është 8 kilobajt. Nëse e ndajmë përsëri me 8, kjo është 1000 uint64 në vlerë. Domethënë, Bucket16 është një strukturë 8-kilobajtëshe.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Le të shohim se si është implementuar një nga metodat e kësaj strukture për shtimin e një vlerë të re.

Të gjitha fillon me uint64 vlerave. Llogarisim 32 bitët e sipërm, llogarisim 32 bitët e poshtëm. Kalojmë përmes të gjithë buckets. Krahasojmë 32 bitët e sipërm në secilën bucket me vlerën që po shtohet. Dhe nëse ato përputhen, atëherë thërrasim funksionin add në strukturën b32 buckets. Dhe shtojmë aty 32 bitët e poshtëm. Dhe nëse kjo ktheu true, atëherë do të thotë se e kemi shtuar atë vlerë aty dhe nuk e kishim një të tillë. Nëse ajo kthen false, atëherë një vlerë e tillë tashmë ekzistonte. Më pas rrisim numrin e elementëve në strukturë.

Nëse nuk gjetëm të nevojshmen bucket me vlerën hi-përkatëse, atëherë thërrasim funksionin addAlloc, i cili alokon një të ri bucket, duke e shtuar atë në strukturën bucket.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Kjo është implementimi i funksionit b32.add. Ajo është e ngjashme me implementimin e mëparshëm. Ne llogarisim 16 bitët e vjetër, 16 bitët e rinj.

Më pas kalojmë përmes të gjithë 16 bitëve të sipërm. Gjejmë përputhje. Dhe në rast përputhjeje thërrasim metodën add, të cilën do ta shqyrtojmë në faqen e ardhshme për bucket16.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Dhe këtu është niveli më i ulët, i cili duhet të jetë maksimalisht i optimizuar. Ne llogarisim për uint64 id vlerën në slice bit, si dhe bitmask. Kjo është një maskë për këtë vlerë 64-bit, me të cilën mund të kontrollojmë pranishmërinë e këtij biti, ose ta vendosim atë. Ne kontrollojmë nëse ky bit është i vendosur dhe e vendosim, dhe kthejmë pranishmërinë. Kjo është implementimi ynë, i cili ka lejuar të shpejtojë operacionin e ndërprerjes së ids të serive kohore 10 herë krahasuar me maps standarde.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Në VictoriaMetrics përveç kësaj optimizimi ka shumë optimizime të tjera. Shumica e këtyre optimizimeve janë shtuar jo thjesht kështu, por pas profilizimit të kodit në produksion.

Ky Ă«shtĂ« rregulli kryesor i optimizimit – mos shtoni optimizimin, duke supozuar se kĂ«tu do tĂ« ketĂ« njĂ« pikĂ« tĂ« ngushtĂ«, sepse mund tĂ« rezultojĂ« se nuk do tĂ« ketĂ« njĂ« pikĂ« tĂ« ngushtĂ« atje. Optimizimi zakonisht pĂ«rkeqĂ«son cilĂ«sinĂ« e kodit. Prandaj, Ă«shtĂ« e nevojshme tĂ« optimizohet vetĂ«m pas profilizimit dhe preferohet nĂ« produksion, nĂ« mĂ«nyrĂ« qĂ« tĂ« jenĂ« tĂ« dhĂ«na reale. AtĂ«herĂ«, ata qĂ« janĂ« tĂ« interesuar, mund tĂ« shikojnĂ« burimet e VictoriaMetrics dhe tĂ« studiojnĂ« optimizimet e tjera qĂ« ka.

Optimizations Go në VictoriaMetrics. Aleksandër Valjalkin

Kam një pyetje në lidhje me bitset. Ka shumë ngjashmëri me implementimin e C++ vector bool, bitset i optimizuar. E keni marrë implementimin nga atje?

Jo, nuk është prej aty. Kur po realizoja këtë bitset, unë u orientova në njohuritë e strukturës së këtyre ids timeseries që përdoren në VictoriaMetrics. Strukturën e tyre është e tillë që 32 bitët e sipërm janë kryesisht të qëndrueshëm. 32 bitët e poshtëm mund të ndryshojnë. Sa më poshtë të jetë bita, aq më shpesh mund të ndodhi ndryshimi. Prandaj, kjo realizim është pikërisht optimizuar për këtë strukturë të dhënash. Realizimi në C++, aq sa di unë, është optimizuar për rastin e përgjithshëm. Nëse bëhet optimizimi për rastin e përgjithshëm, atëherë kjo do të thotë se nuk do të jetë më optimal për rastin specifik.

Ju rekomandoj gjithashtu të shikoni ligjëratën e Aleksej Milovidit. Ai rreth një muaj më parë fliste për optimizimet në ClickHouse për specializime specifike. Ai vërtet tregon se në rastin e përgjithshëm implementimi në C++ ose ndonjë implementim tjetër janë të punuar për të funksionuar mirë në mesatare. Ajo mund të funksionojë më keq se një implementim i specializuar për njohuri specifike, siç jemi ne, kur e dimë se 32 bitët e sipërm janë kryesisht të qëndrueshëm.

Kam një pyetje tjetër. Cila është dallimi i theksuar nga InfluxDB?

Janë shumë dallime të theksuara. Nëse flasim për performancën dhe konsumimin e memories, InfluxDB në testime tregon dhjetë herë më shumë konsumim të memories për seritë temporale me kardinalitet të lartë kur keni shumë, për shembull, miliona. Për shembull, VictoriaMetrics konsumon 1 GB për një milion seri aktive, ndërsa InfluxDB në këtë rast konsumon 10 Gb. Dhe kjo është një ndryshim i madh.

Dallimi i dytĂ« i theksuar Ă«shtĂ« se nĂ« InfluxDB ka gjuhĂ« tĂ« çuditshme pyetjesh – Flux dhe InfluxQL. Ato nuk janĂ« shumĂ« tĂ« pĂ«rshtatshme pĂ«r punĂ« me seritĂ« temporale nĂ« krahasim me PromQL, qĂ« mbĂ«shtetet nĂ« VictoriaMetrics. PromQL Ă«shtĂ« gjuha e pyetjeve nga Prometheus.

Dhe njĂ« dallim tjetĂ«r Ă«shtĂ« se InfluxDB ka njĂ« model tĂ« çuditshĂ«m tĂ« tĂ« dhĂ«nave, ku nĂ« çdo rresht mund tĂ« mbahen disa fushĂ« me grupe tĂ« ndryshme tag-esh. KĂ«to rreshta ndahen edhe nĂ« tabela tĂ« ndryshme. KĂ«to komplikime shtesĂ« e bĂ«jnĂ« mĂ« tĂ« vĂ«shtirĂ« punĂ«n e mĂ«vonshme me kĂ«tĂ« bazĂ«. ËshtĂ« e vĂ«shtirĂ« pĂ«r t'u mbajtur dhe kuptuar.

NĂ« VictoriaMetrics gjithçka Ă«shtĂ« shumĂ« mĂ« e thjeshtĂ«. Atje çdo seri temporale pĂ«rfaqĂ«son njĂ« çelĂ«s-vlerĂ«. Vlera Ă«shtĂ« njĂ« set pikash – (timestamp, value), ndĂ«rsa çelĂ«si Ă«shtĂ« njĂ« set label=value. Nuk ka ndarje mes fushave dhe matjeve. Kjo ju lejon tĂ« zgjidhni tĂ« dhĂ«na tĂ« çfarĂ«do lloji dhe mĂ« pas t'i kombinoni, tĂ« shtoni, tĂ« zbritni, tĂ« shumĂ«zoni, tĂ« ndani, ndryshe nga InfluxDB, ku llogaritjet midis rreshtave tĂ« ndryshĂ«m nuk janĂ« ende realizuar, sa kam dijeni. NĂ«se janĂ« realizuar, ato janĂ« tĂ« komplikuara, duhet tĂ« shkruani shumĂ« kode.

Kam një pyetje sqaruese. A e kuptova saktë që kishte ndonjë problem, për të cilin flisnit, se indeksi i invers nuk i ndalon në memorie, prandaj aty bëhet particionimi?

Fillimisht, tregova një implementim naive të indeksit të invers në standardin Go map. Një implementim i tillë nuk është i përshtatshëm për bazat e të dhënave, sepse ky indeks i invers nuk ruhet në disk, dhe një bazë të dhënash duhet të ruajë në disk, për të siguruar që këto të dhëna të mbeten të aksesueshme pas rinisjes. Në këtë implementim, pasi aplikacioni riniset, indeksi i invers do të humbasë. Dhe do të humbni qasjen në të gjitha të dhënat, sepse nuk do të jeni në gjendje t'i gjeni ato.

Përshëndetje! Faleminderit për prezantimin! Unë quhem Pavel. Jam nga kompania Wildberries. Kam disa pyetje për ju. Pyetja e parë. Si mendoni, nëse do të kishit zgjedhur një parim tjetër në ndërtimin e arkitekturës së aplikacionit tuaj dhe do të ishit partiçionuar të dhënat sipas kohës, ndoshta do të kishit mundur të realizonit ndërhapësira të të dhënave gjatë kërkimit, duke u bazuar vetëm në faktin se në një partiçion ndodhen të dhëna për një periudhë kohore, dmth. për një interval kohor dhe nuk do t'ju duhej të shqetësoheshit për faktin se keni pjesë të shpërndara në mënyrë të ndryshme? Pyetja numër 2 - nëse realizoni një algoritëm të tillë me bitset dhe gjithçka tjetër, ndoshta keni provuar të përdorni instruksionet e procesorit? Mund të keni provuar optimizime të tilla?

Për pyetjen e dytë do t'ju përgjigjem menjëherë. Ne nuk kemi arritur ende në këtë. Por nëse do të jetë e nevojshme, do të arrijmë. A ishte e para, çfarë ishte pyetja?

Diskutuat dy skenarë. Dhe thatë se zgjodhët të dytin me një implementim më të komplikuar. Dhe nuk e përzgjodhët të parin, ku të dhënat janë partiçionuar sipas kohës.

Po, në rastin e parë, volumi total i indeksit do të ishte më i madh, sepse në çdo parti ne do të duhej të ruanim kopje të dhënash për ato seritë kohore që vazhdojnë përmes këtyre të gjitha partive. Dhe nëse ka një shkallë të ulët të ndryshimit në seritë kohore, pra, vazhdimisht përdoren të njëjtat seri, atëherë në rastin e parë do të humbnim ndjeshëm më shumë hapësirë diskore krahasuar me rastin e dytë.

Po, ndarja sipas kohës është një opsion i mirë. Kjo e përdor Prometheus. Por në Prometheus ka një mangësi tjetër. Kur bashkohen këto copa të dhënash, kërkohet të ruajë në memorje metainformacione për të gjitha etiketat dhe seritë kohore. Prandaj, nëse copat e dhënash janë të mëdha që ai i bashkon, përdorimi i memorjes rritet shumë gjatë bashkimit, ndryshe nga VictoriaMetrics. VictoriaMetrics gjatë bashkimit nuk konsumon fare memorje, përdoren disa kilobajtë, pavarësisht nga përmasat e copave të bashkuara të dhënash.

Algoritmi që përdorni konsumon memorje. Aty regjistrohen etiketat e serive kohore që kanë vlera. Dhe kështu kontrolloni praninë e dyfisht në një grup të dhënash dhe në tjetrin. Dhe kuptoni - ndodhi përputhja apo jo. Zakonisht në bazat e të dhënave implemtohen kursore, iterators, që ruajnë gjendjen e tyre aktuale dhe kalojnë nëpër të dhënat e renditura, për shkak të kësaj, keni një kompleksitet të thjeshtë në këto operacione.

Pse nuk përdorim kursore për përputhjen e të dhënave?

Po.

Në LevelDB ose në mergeset ruhen pikërisht rreshtat e renditur. Mund të kalojmë me një kursor dhe të gjejmë përputhjen. Por pse nuk e përdorim? Sepse - është e ngadaltë. Sepse kursoret nënkuptojnë se për çdo rresht duhet të thërrasësh një funksion. Thirrja e funksionit - është 5 nanosekonda. Dhe nëse keni 100,000,000 rreshta, rezulton se ne shpenzojmë një të pestën e sekondës vetëm për thirrjen e funksionit.

Kjo ndodh, po. Dhe pyetja ime e fundit. Pyetja, ndoshta, mund të duket pak e çuditshme. Pse në momentin e pranimit të të dhënave nuk mund të llogariten të gjitha agregatet e nevojshme dhe të ruhen në formën e nevojshme? Pse të ruani volume të mëdha në sisteme si VictoriaMetrics, ClickHouse, etj., në mënyrë që pastaj të shpenzoni shumë kohë për to?

Më lejoni të jap një shembull për ta bërë më të qartë. Supozoni, si funksionon një spidometër i vogël lodër? Ai regjistron distancën që keni kaluar, duke e shtuar atë vazhdimisht në një vlerë, dhe në vlerën tjetër - kohën. Pastaj bën ndarjen për të marrë shpejtësinë mesatare. Mund të bëni diçka të ngjashme. Shtoni në fluturim të gjitha faktet e nevojshme.

Mirë, kuptova pyetjen. Shembulli juaj ka vend në jetë. Nëse e dini se cilat agregate ju duhen, atëherë kjo është zbatimi më i mirë. Por problemi është se njerëzit ruajnë këto metrika, disa të dhëna në ClickHouse dhe ata nuk e dinë ende se si do t'i agregojnë dhe filtrojnë ato në të ardhmen, prandaj duhet të ruajnë të gjitha të dhënat e papërpunuara. Por nëse e dini se ju nevojitet të llogaritni diçka mesatare, atëherë përse të mos e llogaritni atë, në vend që të ruani një sasi të madhe vlerash të papërpunuara? Por kjo është vetëm në rastin kur e dini saktësisht se çfarë ju nevojitet.

Për më tepër, bazat për ruajtjen e serive të kohës mbështesin llogaritjen e agregateve. Për shembull, Prometheus mbështet rregullat e regjistrimit. Kjo do të thotë se është e mundur ta bëni këtë, nëse e dini se cilat agregate do t'ju nevojiten. Në VictoriaMetrics kjo ende nuk ekziston, por zakonisht para saj vendoset Prometheus, ku mund ta bëni këtë në rregullat e regjistrimit.

Për shembull, në punën time të mëparshme duhej të llogarisja numrin e ngjarjeve në një dritare që lëviz për orën e fundit. Problemi ishte se duhej të përfundoja një implementim të personalizuar në Go, dmth., një shërbim për të llogaritur këtë gjë. Ky shërbim përfundimisht doli të ishte jo triviale, sepse është e vështirë të llogaritet. Një implementim mund të jetë i thjeshtë, nëse keni nevojë të llogaritni disa agregate në intervale fikse kohore. Nëse dëshironi të llogaritni ngjarjet në një dritare lëvizëse, atëherë kjo nuk është aq e lehtë sa duket. Mendoj se kjo nuk është realizuar ende në ClickHouse ose në bazat e të dhënave të serive të kohës, sepse është e komplikuar për t'u realizuar.

Dhe një pyetje tjetër. Tani po flisnim për mesataren, dhe më erdhi në mend që dikur kishte një gjë si Graphite me backend Carbon. Ai dinte të filtrojë të dhënat e vjetra, dmth. të linte një pikë për minutë, një pikë për orë dhe kështu me radhë. Në fakt, është mjaft e përshtatshme, nëse na duhen të dhëna të papërpunuara, thjesht për muajin, ndërsa gjithçka tjetër mund të filtrohet. Por Prometheus dhe VictoriaMetrics nuk e mbështesin këtë funksionalitet. A është planifikuar të mbështetet? Nëse jo, atëherë pse?

Faleminderit për pyetjen. Përdoruesit tanë e bëjnë këtë pyetje herë pas here. Ata kërkojnë të dinë kur do të shtojmë mbështetje për ngjeshjen e të dhënave (downsampling). Këtu ka disa probleme. Së pari, çdo përdorues ka kuptimin e tij për downsampling duke kërkuar një pikë të caktuar në një interval të caktuar, dikush dëshiron vlerat maksimale, minimale ose mesatare. Nëse në bazën tuaj janë duke i shkruar të dhëna shumë sisteme, nuk mund t'i trajtoni ato njëlloj. Mund të ndodhë që për çdo sistem të nevojitet përdorimi i një mënyre të ndryshme ngjeshjeje. Dhe kjo është e vështirë për t'u realizuar.

Dhe e dyta – Ă«shtĂ« qĂ« VictoriaMetrics, ashtu si ClickHouse, Ă«shtĂ« optimizuar pĂ«r tĂ« punuar me njĂ« volum tĂ« madh tĂ« dhĂ«nash tĂ« papĂ«rpunuara, kĂ«shtu qĂ« ajo mund tĂ« analizoje njĂ« miliard rreshta pĂ«r mĂ« pak se njĂ« sekondĂ«, nĂ«se keni shumĂ« bĂ«rthama nĂ« sistemin tuaj. Skarimi i pikave tĂ« serisĂ« temporale nĂ« VictoriaMetrics Ă«shtĂ« 50,000,000 pika nĂ« sekondĂ« pĂ«r njĂ« bĂ«rthamĂ«. Dhe kjo performancĂ« shkallĂ«zohet nĂ« bĂ«rthamat e disponueshme. Pra, nĂ«se keni 20 bĂ«rthama, pĂ«r shembull, do tĂ« arrini tĂ« analizoni njĂ« miliard pikash nĂ« sekondĂ«. Dhe kjo veçori e VictoriaMetrics dhe ClickHouse zvogĂ«lon nevojĂ«n pĂ«r downsampling.

NjĂ« tjetĂ«r veçori Ă«shtĂ« se VictoriaMetrics kompreson kĂ«to tĂ« dhĂ«na nĂ« mĂ«nyrĂ« efikase. Kompresimi mesatar nĂ« prodhim Ă«shtĂ« nga 0.4 deri nĂ« 0.8 byte pĂ«r pikĂ«. Çdo pikĂ« pĂ«rbĂ«het nga timestamp + vlera. Dhe ajo kompresohet mĂ« pak se njĂ« byte nĂ« mesatare.

Sergei. Kam një pyetje. Cili është kvanti minimal i kohës së regjistrimit?

NjĂ« milisekondĂ«. SĂ« fundmi, kishim njĂ« bisedĂ« me zhvillues tĂ« tjerĂ« tĂ« bazave tĂ« dhĂ«nash pĂ«r seritĂ« temporale. Kvanti minimal i tyre kohor Ă«shtĂ« njĂ« sekondĂ«. Edhe nĂ« Graphite, pĂ«r shembull, Ă«shtĂ« njĂ« sekondĂ«. NĂ« OpenTSDB gjithashtu Ă«shtĂ« njĂ« sekondĂ«. NĂ« InfluxDB – pikĂ«sia Ă«shtĂ« nĂ« nanosekonda. NĂ« VictoriaMetrics – Ă«shtĂ« njĂ« milisekondĂ«, sepse nĂ« Prometheus Ă«shtĂ« njĂ« milisekondĂ«. Dhe VictoriaMetrics Ă«shtĂ« zhvilluar si ruajtje e largĂ«t pĂ«r Prometheus fillimisht. Por tani ajo mund tĂ« ruajĂ« tĂ« dhĂ«na edhe nga sisteme tĂ« tjera.

Njeriu me tĂ« cilin bisedova thotĂ« se ndajnĂ« njĂ« pikĂ«si sekondĂ« – kjo Ă«shtĂ« e mjaftueshme pĂ«r ta, sepse varet nga lloji i tĂ« dhĂ«nave qĂ« ruhen nĂ« bazĂ«n e tĂ« dhĂ«nave temporale. NĂ«se janĂ« tĂ« dhĂ«na DevOps ose tĂ« dhĂ«na nga infrastruktura, ku i mbledhni ato me intervale prej 30 sekondash deri nĂ« njĂ« minutĂ«, atĂ«herĂ« njĂ« pikĂ«si sekondĂ« Ă«shtĂ« e mjaftueshme, mĂ« pak nuk Ă«shtĂ« e nevojshme. Por nĂ«se po mbledhni kĂ«to tĂ« dhĂ«na nga sisteme tregtimi me frekuencĂ« tĂ« lartĂ«, atĂ«herĂ« nevojitet pikĂ«si nĂ« nanosekonda.

Saktësia në milisekonda në VictoriaMetrics është e përshtatshme si për rastin e DevOps, ashtu edhe për shumicën e rasteve që përmenda në fillim të raportit. E vetmja gjë për të cilën ajo mund të mos jetë e përshtatshme është për sistemet e tregtisë me frekuencë të lartë.

Faleminderit! Dhe një pyetje tjetër. Cila është përputhshmëria në PromQL?

Përputhshmëria e plotë retroaktive. VictoriaMetrics mbështet plotësisht PromQL. Për më tepër, ajo shton një funksionalitet të zgjeruar mbi PromQL, i cili quhet MetricsQL. Për këtë funksionalitet të zgjeruar ka një raport në YouTube. E kam përshkruar në Monitoring Meetup pranverën e kaluar në Shën Petersburg.

Kanali i Telegramit VictoriaMetrics.

Vetëm përdoruesit e regjistruar mund të marrin pjesë në anketë. Hyni, ju lutem.

ÇfarĂ« ju pengon tĂ« kaloni nĂ« VictoriaMetrics si njĂ« magazinĂ« afatgjatĂ« pĂ«r Prometheus? (Shkruani nĂ« komentet, do ta shtoj nĂ« ankete))

  • 71,4%Nuk po pĂ«rdor Prometheus5

  • 28,6%Nuk e dija pĂ«r VictoriaMetrics2

7 përdorues kanë votuar. 12 përdorues janë abstenuar.

Burimi: habr.com

Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster