Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Ju ftoj të njihemi me përmbledhjen e raportit të fundit të vitit 2019 nga Aleksandër Valjalkin "Optimizimet 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 seri temporale (njĂ« rekord formon kohĂ«n dhe njĂ« grup vlerash pĂ«rkatĂ«se, pĂ«r shembull, tĂ« marra pĂ«rmes sondazhit periodic tĂ« gjendjes sĂ« sensorĂ«ve ose mbledhjes sĂ« metrikeve).

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Ja njĂ« lidhje me videon e kĂ«tij raporti — https://youtu.be/MZ5P21j_HLE

Slajdet

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Do të flas pak për veten time. Unë jam Aleksandër Valjalkin. Ja akunty i imi në GitHub. Më pëlqen Go dhe optimizimi i performancës. Kam shkruar shumë biblioteka të dobishme dhe ndonjëherë jo aq të dobishme. Ato fillojnë ose me fast, ose me quick prefiksin.

Aktualisht po punoj mbi VictoriaMetrics. ÇfarĂ« Ă«shtĂ« kjo dhe çfarĂ« po bĂ«j atje? PĂ«r kĂ«tĂ« do tĂ« flas nĂ« kĂ«tĂ« prezantim.

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Plani i raportit është si vijon:

  • Fillimisht do t'ju tregoj se çfarĂ« Ă«shtĂ« VictoriaMetrics.
  • Pastaj do tĂ« flas pĂ«r seri temporale.
  • MĂ« pas do tĂ« tregoj si funksionon baza e tĂ« dhĂ«nave tĂ« seri temporale.
  • MĂ« tej do tĂ« flas pĂ«r arkitekturĂ«n e bazĂ«s sĂ« tĂ« dhĂ«nave: çfarĂ« pĂ«rfshin ajo.
  • Dhe pastaj do tĂ« kalojmĂ« nĂ« optimizimet qĂ« ekzistojnĂ« nĂ« VictoriaMetrics. KĂ«to janĂ« optimizimi i indeksit tĂ« invers dhe optimizimi pĂ«r implementimin e bitset nĂ« Go.

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

ÇfarĂ« Ă«shtĂ« VictoriaMetrics, a e di kush nĂ« audiencĂ«? Ehh, shumĂ« njerĂ«z tashmĂ« e dinĂ«. Kjo Ă«shtĂ« njĂ« lajm i mirĂ«. PĂ«r ata qĂ« nuk e dinĂ« – Ă«shtĂ« njĂ« bazĂ« tĂ« dhĂ«nash pĂ«r seri kohore. Ajo bazohet nĂ« arkitekturĂ«n ClickHouse, duke pĂ«rfshirĂ« disa detaje tĂ« zbatimit tĂ« ClickHouse. PĂ«r shembull, disa nga ato janĂ«: MergeTree, llogaritje paralele nĂ« tĂ« gjitha bĂ«rthamat e disponueshme tĂ« procesorit dhe optimizimi i performancĂ«s me punĂ«n 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 seri kohore.

Ajo shkallĂ«zohet nĂ« mĂ«nyrĂ« vertikale — domethĂ«nĂ«, mund tĂ« shtoni mĂ« shumĂ« procesorĂ«, mĂ« shumĂ« memorie RAM nĂ« njĂ« kompjuter. VictoriaMetrics do tĂ« shfrytĂ«zojĂ« me sukses kĂ«to burime tĂ« disponueshme dhe do tĂ« rrisĂ« performancĂ«n lineare.

Gjithashtu, VictoriaMetrics shkallĂ«zohet horizontalisht — domethĂ«nĂ«, mund tĂ« shtoni nĂ«n-produkte tĂ« tjera nĂ« klusterin VictoriaMetrics dhe performanca e saj do tĂ« rritet pothuajse linearisht.

Si e keni kuptuar, VictoriaMetrics është një bazë të dhënash e shpejtë, sepse nuk mund të flas për të tjerat. Po ashtu, është shkruar në Go, prandaj po flas për të në këtë mitap.

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Kush e di se çfarĂ« Ă«shtĂ« njĂ« seri kohore? ShumĂ« njerĂ«z e dinĂ«. NjĂ« seri kohore Ă«shtĂ« njĂ« sĂ«rĂ« çiftĂ«sh (timestamp, vlera), ku kĂ«to çifte janĂ« tĂ« renditura sipas kohĂ«s. Vlera pĂ«rfaqĂ«son njĂ« numĂ«r me pikĂ« tĂ« lĂ«vizshme – float64.

Çdo seri kohore identifikohet nĂ« mĂ«nyrĂ« unike nga njĂ« çelĂ«s. ÇfarĂ« pĂ«rbĂ«n ky çelĂ«s? Ai pĂ«rbĂ«het nga njĂ« grup i pandĂ«rprerĂ« çiftĂ«sh çelĂ«s-vlerĂ«.

Ja njĂ« shembull i njĂ« serie kohore. Ç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.

Kam marrĂ« emrin e njĂ« serie kohore, e cila pĂ«rbĂ«het nga tri çiftĂ« çelĂ«s-vlerĂ«. Ky çelĂ«s ka njĂ« listĂ« çiftĂ«sh (timestamp, vlera). t1, t3, t3, ..., tN — kĂ«to janĂ« timestamps, 10, 20, 12, ..., 15 — vlerat pĂ«rkatĂ«se. Kjo Ă«shtĂ« pĂ«rdorimi i CPU nĂ« kĂ«tĂ« moment pĂ«r kĂ«tĂ« seri.

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Ku mund të përdoren seritë kohore? Disa ide nga ndokush?

  • NĂ« DevOps mund tĂ« matni ngarkesĂ«n e CPU, RAM, rrjetit, rps, numrin e gabimeve etj.
  • IoT – ne mund tĂ« masim temperaturĂ«n, presionin, koordinatat gjeografike, dhe ndonjĂ« gjĂ« tjetĂ«r.
  • Po ashtu nĂ« financa – mund tĂ« monitorojmĂ« çmimet e aksioneve dhe valutave tĂ« ndryshme.
  • PĂ«r mĂ« tepĂ«r, seritĂ« temporale mund tĂ« pĂ«rdoren pĂ«r monitorimin e proceseve prodhuese nĂ« fabrika. Kemi pĂ«rdorues qĂ« pĂ«rdorin VictoriaMetrics pĂ«r monitorimin e turbina eolikĂ« dhe robotĂ«ve.
  • Gjithashtu, seritĂ« temporale janĂ« tĂ« dobishme pĂ«r mbledhjen e informacionit nga sensorĂ«t e ndryshĂ«m tĂ« pajisjeve. PĂ«r shembull, pĂ«r motorin; pĂ«r matjen e presionit nĂ« goma; pĂ«r matjen e shpejtĂ«sisĂ«, distancĂ«s; pĂ«r matjen e konsumit tĂ« benzinĂ«s, etj.
  • Gjithashtu, seritĂ« temporale mund tĂ« pĂ«rdoren pĂ«r monitorimin e avionĂ«ve. Çdo avion ka njĂ« kutinĂ« e zezĂ« qĂ« mbledh seritĂ« temporale sipas parametrave tĂ« ndryshĂ«m tĂ« shĂ«ndetit tĂ« avionit. Serie temporale jepet gjithashtu nĂ« industrinĂ« aviacione.
  • ShĂ«ndetĂ«sia – janĂ« presioni i gjakut, puls, etj.

Ndoshta ka edhe aplikime të tjera që kam harruar, por shpresoj që keni kuptuar se seritë temporale përdoren aktivisht në botën moderne. Dhe volumi i përdorimit të tyre po rritet çdo vit.

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Për çfarë i nevojitet një bazë të dhënash për seritë e tyre? Pse nuk mund të përdoret një bazë e zakonshme relacionale për ruajtjen e serive të tyre?

Sepse në seritë e tyre zakonisht ka një volum të madh informacioni, që është e vështirë të ruhen dhe të procesohen në bazat e zakonshme të dhënash. Prandaj janë shfaqur BD të specializuara për seritë e dhënash. Këto baza ruajnë në mënyrë efektive pikat (timestamp, vlera) me një çelës të caktuar. Ato ofrojnë një API për të lexuar të dhënat e ruajtura sipas çelësit, me një çift çelës-vlerë, ose me disa të tilla, ose sipas regexp. Për shembull, nëse dëshironi të gjeni ngarkesën e procesorit të të gjithë shërbimeve tuaja në qendrën e të dhënave në Amerikë, duhet të përdorni një kërkesë të tillë.

Zakonisht bazat e dhënash për seritë e dhënash paraqesin gjuhë të specializuara kërkese, sepse SQL për seritë e dhënash nuk është shumë i përshtatshëm. Edhe pse ka baza të dhënash që mbështesin SQL, ai nuk është shumë i përshtatshëm. Më mirë i përshtaten gjuhët e kërkesave si PromQL, InfluxQL, Flux, Q. Shpresoj që dikush të ketë dëgjuar për ndonjë prej këtyre gjuhëve. O PromQL, ndoshta, kanë dëgjuar shumë. Kjo është gjuhë kërkese e Prometheus.

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Ja si është ndërtimi i arkitekturës moderne të një baze të dhënash për seritë kohore në shembullin e VictoriaMetrics.

Ajo përbëhet nga dy pjesë. Kjo është një depo për indeksin e inverzuar dhe një depo për vlerat e serive kohore. Këto depove janë të ndara.

Kur një regjistrim i ri vjen në bazën e të dhënave, ne së pari referohemi në indeksin e inverzuar për të gjetur identifikuesin e serisë kohore sipas një grupi të caktuar label=value për këtë metrikë. E gjejmë këtë identifikues dhe ruajmë vlerën në depo.

Kur vjen ndonjë kërkesë për marrjen e të dhënave nga TSDB, ne në radhë të parë shkojmë në indeksin e inverzuar. Nxjerrim të gjitha timeseries_ids regjistrimet që korrespondojnë me këtë grup label=value. Dhe më pas e nxjerrim të gjitha të dhënat e nevojshme nga depon e të dhënave, e indeksuar sipas timeseries_ids.

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Le të shqyrtojmë një shembull se si baza e të dhënave për seritë kohore trajton një kërkesë select të ardhshme.

  • SĂ« pari, ajo nxjerr tĂ« gjitha timeseries_ids nga indeksi i inverzuar, tĂ« cilat pĂ«rmbajnĂ« çiftet e caktuara label=value, ose qĂ« i pĂ«rgjigjen njĂ« shprehjeje rregullt tĂ« caktuar.
  • MĂ« pas ajo nxjerr tĂ« gjitha pikat e tĂ« dhĂ«nave nga depoja e tĂ« dhĂ«nave nĂ« njĂ« interval tĂ« caktuar kohor pĂ«r ato tĂ« gjetura. timeseries_ids.
  • Pas kĂ«saj, databaza kryen disa llogaritje mbi kĂ«to data points, sipas kĂ«rkesĂ«s sĂ« pĂ«rdoruesit. Dhe pas kĂ«saj, kthen njĂ« pĂ«rgjigje.

NĂ« kĂ«tĂ« prezantim do t'ju tregoj pĂ«r pjesĂ«n e parĂ«. Kjo Ă«shtĂ« kĂ«rkimi timeseries_ids nĂ« indeksin e invertuar. PĂ«r pjesĂ«n e dytĂ« dhe tĂ« tretĂ« mund tĂ« shikoni mĂ« vonĂ« burimet e VictoriaMetrics, ose tĂ« prisni deri sa tĂ« pĂ«rgatis dokumente tĂ« tjera 🙂

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Le t'ia fillojmë indeksit të invertuar. Disa mund të mendojnë se është e thjeshtë. Kush e di se çfarë është një indeks i invertuar dhe si funksionon? Oh, tashmë nuk ka shumë njerëz. Le t'ë përpiqemi të kuptojmë se çfarë është kjo.

NĂ« tĂ« vĂ«rtetĂ«, gjithçka Ă«shtĂ« e thjeshtĂ«. Ky Ă«shtĂ« thjesht njĂ« fjalor qĂ« shfaq çelĂ«sin nĂ« vlerĂ«. ÇfarĂ« Ă«shtĂ« çelĂ«si? Ky çift label=value, ku etiketĂ« dhe vlera – janĂ« strings. Dhe vlerat janĂ« njĂ« grup timeseries_ids, i cili pĂ«rfshin çiftin e caktuar label=value.

Indeksi i invertuar lejon të gjeni shpejt të gjitha timeseries_ids, të cilat kanë të caktuara label=value.

Po ashtu, ai lejon të gjeni shpejt timeseries_ids seritë e kohës për disa çifte label=value, ose për çifte label=regexp. Si ndodhi kjo? Përmes gjetjes së kryqëzimit të shumëllojshmërisë timeseries_ids për çdo çift label=value.

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Le të shqyrtojmë realizime të ndryshme të indeksit të inkorporuar. Le të fillojmë me realizimin më të thjeshtë naiv. Ai duket kështu.

Funksioni getMetricIDs merr listĂ«n e rreshtave. Çdo rresht pĂ«rmban label=value. Kjo funksion kthen njĂ« listĂ« metricIDs.

Si funksionon kjo? Këtu kemi një variabël globale që quhet invertedIndex. Ky është një fjalor i zakonshëm (map), që shndërron rreshtin në një slice int-ësh. Rreshti përmban label=value.

Realizimi i funksionit: nxjerrim metricIDs për të parin label=value, pastaj kalojmë përmes gjithë të tjerëve label=value, nxjerrim metricIDs për ta. Dhe thërrasim funksionin intersectInts, për të cilin do të flitet më vonë. Dhe ky funksion kthen kryqëzimin e këtyre listave.

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Siç e shihni, realizimi i indeksit tĂ« inkorporuar nuk Ă«shtĂ« shumĂ« i komplikuar. Por kjo Ă«shtĂ« njĂ« realizim naiv. ÇfarĂ« tĂ« metash ka? TĂ« metĂ«n kryesore tĂ« realizimit naiv Ă«shtĂ« se ky indeks i inkorporuar ruhet nĂ« memorie. Pas ribllokimit tĂ« aplikacionit, ne e humbasim kĂ«tĂ« indeks. Nuk ka ruajtje tĂ« kĂ«tij indeksi nĂ« disk. PĂ«r njĂ« bazĂ« tĂ« dhĂ«nash, ky indeks i inkorporuar vĂ«shtirĂ« se do tĂ« pĂ«rshtatet.

KĂ«tu Ă«shtĂ« njĂ« problem i dytĂ« i lidhur me memorie. Indeksi i pĂ«rmbysur duhet tĂ« ruhet nĂ« memorien operative. NĂ«se e kalon pĂ«rmasĂ«n e memories operative, Ă«shtĂ« e qartĂ« se do tĂ« marrim njĂ« – gabim out of memory. Dhe programi nuk do tĂ« funksionojĂ«.

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Ky problem mund të zgjidhet me zgjidhje të gatshme siç janë 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Ă« regjistrimi ç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Ă« i vlerĂ«s pĂ«r njĂ« çelĂ«s tĂ« caktuar.
  • Dhe operacioni i tretĂ« – Ă«shtĂ« kĂ«rkimi i shpejtĂ« i tĂ« gjitha vlerave pĂ«r njĂ« prefiks tĂ« caktuar.

LevelDB dhe RocksDB – kĂ«to baza janĂ« zhvilluar nĂ« Google dhe Facebook. Fillimisht u shfaq LevelDB. Pjesa tjetĂ«r nga Facebook e mori LevelDB dhe filloi ta pĂ«rmirĂ«sojĂ«, duke krijuar RocksDB. Tani, nĂ« Facebook, pothuajse tĂ« gjitha bazat e tĂ« dhĂ«nave tĂ« brendshme punojnĂ« me RocksDB, pĂ«rfshirĂ« MySQL qĂ« e kanĂ« kaluar nĂ« RocksDB. Ata e quajtĂ«n MyRocks.

Indeksi i pĂ«rmbysur mund tĂ« realizohet me LevelDB. Si e bĂ«jmĂ« kĂ«tĂ«? RuajmĂ« si çelĂ«s label=value. Dhe si vlerĂ« – identifikuesin e serisĂ« temporale, ku ndodhet çifti label=value.

Nëse kemi shumë seri temporale me këtë çift label=value, do të ketë shumë rreshta në këtë bazë të dhënash me të njëjtin çelës dhe të ndryshëm timeseries_ids. Për të marrë një listë të gjithë timeseries_ids, që fillojnë me këtë label=prefix, ne bëjmë një skanim të gamës, për të cilin është optimizuar kjo bazë e të dhënave. Domethënë, zgjedhim të gjithë rreshtat që fillojnë me label=prefix dhe marrim të nevojshmet timeseries_ids.

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

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

Funksioni është i njëjtë, si për implementimin naive. Ajo pothuajse përsërit rresht në rresht implementimin naive. Vetëm momenti është, që në vend të aksesit te map ne i qasem indeksit të inversuar. Marrim të gjitha vlerat për të parën label=value. Pastaj kalojmë përmes të gjitha çiftëve të mbetur label=value dhe marrim grupet përkatëse të metricIDs për ta. Më pas gjejmë ndërprerjen.

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

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

Pse? Sepse LevelDB Ă«shtĂ« mĂ« i ngadalshĂ«m se implementimi naive. NĂ« implementimin naive, pĂ«r çelĂ«sin e dhĂ«nĂ« ne menjĂ«herĂ« marrim tĂ« gjithĂ« slice-in metricIDs. Kjo Ă«shtĂ« njĂ« operacion shumĂ« i shpejtĂ« — e gjithĂ« slice Ă«shtĂ« gati pĂ«r pĂ«rdorim.

Në LevelDB, gjatë çdo thirrjeje të funksionit GetValues duhet të kalosh përmes të gjitha rreshtave që fillojnë me label=value. Dhe për secilin rresht të nxjerrësh vlerën timeseries_ids. Nga të tillë timeseries_ids të grumbullosh një slice të këtyre timeseries_ids. është e qartë se kjo është shumë më e ngadalët se sa thjesht qasja në një map të zakonshëm sipas çelësit.

Mungesa e dytĂ« Ă«shtĂ« se LevelDB Ă«shtĂ« shkruar nĂ« C. Qasja nĂ« funksionet C nga Go nuk Ă«shtĂ« shumĂ« e shpejtĂ«. Kjo merr qindra nanosekonda. Kjo nuk Ă«shtĂ« shumĂ« e shpejtĂ«, sepse nĂ« krahasim me njĂ« thirrje tĂ« zakonshme tĂ« funksionit tĂ« shkruar nĂ« Go, qĂ« zĂ« 1-5 nanosekonda, diferenca nĂ« performancĂ« Ă«shtĂ« shumĂ«fish mĂ« e madhe. PĂ«r VictoriaMetrics, ky ishte njĂ« disavantazh fatal 🙂

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

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

Mergeset është e bazuar në strukturën e të dhënave MergeTree. Kjo strukturë të dhënash është marrë nga ClickHouse. E qartë është se mergeset duhet të jetë e optimizuar për kërkime të shpejta timeseries_ids për një çelës të caktuar. Mergeset është shkruar plotësisht në Go. Ju mund të shikoni 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 mergeset është shumë i ngjashëm me LevelDB dhe RocksDB. Kjo do të thotë se ai lejon ruajtjen e shpejtë të shënimeve të reja dhe shfrytëzimin e shpejtë të shënimeve sipas një prefiksi të caktuar.

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Për disavantazhet e mergeset do të flasim më vonë. Tani do të diskutojmë për problemet që janë shfaqur me VictoriaMetrics në produksion gjatë implementimit të indeksit të invertuar.

Pse ndodhen ato?

Arsyeja e parĂ« Ă«shtĂ« shkalla e lartĂ« e pĂ«rmbysjes. NĂ« pĂ«rkthim nĂ« shqip – kjo do tĂ« thotĂ« ndryshimi i shpeshtĂ« i serive temporale. Kjo ndodh kur njĂ« seri temporale pĂ«rfundon dhe fillon njĂ« tjetĂ«r, ose fillojnĂ« shumĂ« seria tĂ« reja. Dhe kjo ndodh shpesh.

Arsyeja e dytë është numri i madh i serive temporale. Në fillim, kur monitorimi po përhapej, numri i serive temporale ishte i vogël. Për shembull, për çdo kompjuter, duhet të monitoroni ngarkesën e procesorit, kujtesës, rrjetit dhe disqet. 4 seri temporale për çdo kompjuter. Keni, për shembull, 100 kompjuterë dhe 400 seria temporale. Kjo është shumë pak.

Me kohë, njerëzit shpikën mënyra për të matur informacion më të detajuar. Për shembull, të matësh ngarkesën jo të gjithë procesorit, por veç e veç të çdo bërthame procesori. Nëse keni 40 bërtha procesori, atëherë, sigurisht, keni 40 herë më shumë radhë të cilësuara për matjen e ngarkesës së procesorit.

Por kjo nuk Ă«shtĂ« gjithçka. Çdo bĂ«rthamĂ« procesori mund tĂ« ketĂ« disa gjendje si idle, kur Ă«shtĂ« nĂ« pritje. Gjithashtu, operimi nĂ« hapĂ«sirĂ«n e pĂ«rdoruesit, operimi nĂ« hapĂ«sirĂ«n e bĂ«rthamĂ«s dhe gjendje tĂ« tjera. Dhe çdo gjendje e tillĂ« gjithashtu mund tĂ« matet si njĂ« radhĂ« e veçantĂ«. Kjo shton pĂ«r afro 7-8 herĂ« numrin e radhĂ«ve.

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

MĂ« pas erdhi Kubernetes. Dhe ende nuk u pĂ«rmirĂ«sua, sepse nĂ« Kubernetes mund tĂ« hostohen shumĂ« shĂ«rbime tĂ« ndryshme. Çdo shĂ«rbim nĂ« Kubernetes pĂ«rbĂ«het nga shumĂ« podĂ«. TĂ« gjitha kĂ«to duhen monitoruar. PĂ«rveç kĂ«saj, ne kemi njĂ« zhvillim tĂ« vazhdueshĂ«m tĂ« versioneve tĂ« reja tĂ« shĂ«rbimeve tuaja. PĂ«r çdo version tĂ« ri, duhet tĂ« krijohen rreshta tĂ« rinj temporal. Si rezultat, numri i rreshtave temporal rritet nĂ« mĂ«nyrĂ« eksponenciale dhe pĂ«rballemi me problemin e njĂ« numri tĂ« madh rreshtash temporal, i njohur si high-cardinality. VictoriaMetrics e zgjidh kĂ«tĂ« mĂ« me sukses krahasuar me bazat e tjera tĂ« tĂ« dhĂ«nave pĂ«r rreshta temporal.

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Le të shqyrtojmë më me hollësi high churn rate. Për çfarë arsye krijohet high churn rate në production? Sepse disa vlera etiketash dhe tagësh ndryshojnë vazhdimisht.

PĂ«r shembull, tĂ« marim Kubernetes, nĂ« tĂ« cilin ka njĂ« koncept deployment, dmth. kur lansohet njĂ« version i ri i aplikacionit tuaj. Zhvilluesit e Kubernetes vendosĂ«n tĂ« shtonin papritur id’shĂ«n e deployment’it nĂ« label.

ÇfarĂ« rezultoi nga kjo? QĂ« nĂ« çdo deployment tĂ« ri, tĂ« gjitha rreshtat e vjetĂ«r temporal ndĂ«rpriten, dhe nĂ« vend tĂ« tyre nisin rreshta tĂ« rinj temporal me vlerĂ« tĂ« re etike. deployment_id. Mund tĂ« ketĂ« qindra mijĂ«ra dhe madje miliona tĂ« tilla.

NjĂ« karakteristikĂ« e rĂ«ndĂ«sishme e gjithçkaje Ă«shtĂ« se numri total i serive temporale po rritet, por numri i serive temporale qĂ« janĂ« aktualisht aktive, pĂ«r tĂ« cilat po vijnĂ« tĂ« dhĂ«na, mbetet konstant. Ky gjendje quhet – shkallĂ« e lartĂ« e ndryshueshmĂ«risĂ«.

Problemi kryesor i shkallës së lartë të ndryshueshmërisë është të sigurohet një shpejtësi e vazhdueshme e kërkimit për të gjitha seritë temporale sipas një grupi të caktuar etiketash për një interval të caktuar kohe. Zakonisht ky është një interval kohe për orën e fundit ose për ditën e fundit.

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Si mund ta zgjidhim këtë problem? Kjo është një mundësi e parë. Të ndajmë indeksin e kthyer në pjesë të pavarura mbi bazë të kohës. Kjo do të thotë se kalon një interval kohe, përfundojmë punën me indeksin e kthyer aktual. Dhe krijojmë një indeks të ri të kthyer. Kalon një tjetër interval kohe, krijojmë një tjetër dhe një tjetër.

Dhe kur seleksionojmë nga këto indekse të kthyer, ne gjejmë një grup indekse të kthyer që bien brenda intervalit të caktuar. Dhe, për pasojë, përzgjedhim nga aty id-të e serive temporale.

Kjo lejon që të kursejmë burimet, sepse nuk na nevojitet të shqyrtojmë pjesë që nuk bien brenda intervalit të caktuar. Pra, zakonisht, nëse zgjedhim të dhënat për orën e fundit, ne i kalojmë kërkesat për intervalet e mëparshme të kohës.

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Ka një mundësi tjetër për të zgjidhur këtë problem. Kjo është mbajtja e një liste të veçantë të identifikuesve të serive kohore për çdo ditë, të cilat janë hasur gjatë asaj dite.

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

Megjithatë, ky zgjidhje është më e komplikuar për t'u zbatuar dhe më sfiduese për tu debuguar. VictoriaMetrics ka zgjedhur këtë zgjidhje, e cila është formuar historikisht. Kjo zgjidhje gjithashtu tregon rezultate të mira krahasuar me të kaluarën. Për shkak se kjo zgjidhje nuk u implementua sepse duhej të dyfishoheshin të dhënat në çdo ndarje për seritë temporale që nuk ndryshojnë, pra që nuk zhduken me kalimin e kohës. VictoriaMetrics u optimizua kryesisht për konsumimin e hapësirës në disk, dhe implementimi i mëparshëm përkeqësoi konsumimin e saj. Kjo implementim tani është më i përshtatshëm për minimizimin e konsumit të hapësirës në disk, prandaj u zgjodh.

Kisha nevojë të përballesha me të. Lufta ishte se në këtë implementim duhet të zgjidhet një numër shumë më i madh timeseries_ids i të dhënave se sa kur indeksi i invertuar ndahet në kohë.

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Si e zgjidhëm këtë problem? E zgjidhëm në një mënyrë origjinale - duke ruajtur disa identifikues të serive temporale në çdo regjistrim të indeksit të invertuar në vend të një identifikuesi. Pra, ne kemi një çelës label=value, i cila gjendet në çdo rresht temporal. Dhe tani ne ruajmë disa timeseries_ids në një regjistrim.

Këtu është një shembull. Më parë ne kishim N regjistrime, tani kemi një regjistrim, me prefiksin e njëjtë si të gjithë të tjerët. Një regjistrim të mëparshëm kishte një vlerë që përmban të gjithë id-et e rreshtave temporale.

Kjo ka lejuar të rritet shpejtësia e skanimit të këtij indeksi të përmbysur deri në 10 herë. Dhe ka lejuar të ulet konsumimi i memories për cache, sepse tani ruajmë rreshtin label=value vetëm një herë në cache së bashku me N herë. Dhe ky rresht mund të jetë i madh, nëse gjatë etiketimit dhe etiketave ruani rreshta të gjatë që i pëlqen të vendosë aty Kubernetes.

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Një tjetër variant për shpejtësimin e kërkimeve në indekset e përmbysura është sharding. Krijimi i disa indekseve të përmbysura në vend të një dhe sharding të dhënave mes tyre sipas çelësit. Ky është një grup key=value Pra. Që do të thotë se ne kemi disa indekse të pavarura të përmbysura, të cilat mund t'i konsultojmë paralelisht në disa procesorë. Zbatimet e mëparshme lejonin funksionimin vetëm në modin me një procesor, pra, skanuar të dhënat vetëm në një bërthamë. Kjo zgjidhje lejon skanimin e të dhënave menjëherë në disa bërthama, ashtu siç bën ClickHouse. Këtë planifikojmë ta realizojmë.

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Tani, le tĂ« kthehemi te dele tona – tek funksioni i ndĂ«rprerjes. timeseries_ids. Le tĂ« shqyrtojmĂ« se çfarĂ« zbatimesh mund tĂ« ekzistojnĂ«. Ky funksion lejon tĂ« gjenden timeseries_ids pĂ«r njĂ« grup tĂ« caktuar. label=value.

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Varianti i parĂ« – Ă«shtĂ« zbatimi naive. Dy cikle tĂ« ngjitur. KĂ«tu kemi nĂ« hyrje tĂ« funksionit intersectInts dy slices — a dhe b. NĂ« dalje, duhet tĂ« na kthejĂ« ndĂ«rprerjen e kĂ«tyre slices.

Zbatimi naive duket kështu. Ne kalojmë nëpër të gjitha vlerat nga slice a, brenda këtij cikli kalojmë nëpër të gjitha vlerat e slice b. Dhe i krahasojmë ato. Nëse ato përputhen, atëherë kemi gjetur ndërprerjen. Dhe e ruajmë në result.

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Cilat janĂ« disavantazhet? Kompleksiteti katror — ky Ă«shtĂ« disavantazhi i saj kryesor. PĂ«r shembull, nĂ«se keni dimensionet e slice a dhe b nĂ«se ka njĂ« milion, atĂ«herĂ« kjo funksion nuk do tĂ« kthejĂ« kurrĂ« njĂ« pĂ«rgjigje. Sepse do tĂ« duhet tĂ« kryejĂ« njĂ« trilion iteracione, gjĂ« qĂ« Ă«shtĂ« shumĂ« pĂ«r komputatorĂ«t modernĂ«.

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

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

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Cilat janë përfitimet? Avantazh është se këtu ekziston vetëm kompleksitet linear. Kështu që funksioni do të ekzekutohet shumë më shpejt për madhësi më të mëdha slice. Për një madhësi një milion të slice kjo funksion do të përfundojë për 2 milion iteracione, ndryshe nga trillioni i iteracioneve, si në funksionin e mëparshëm.

Cili është disavantazhi? Disavantazhi është se ky funksion kërkon më shumë memorie për të krijuar këtë map.

Disavantazhi i dytë është overhead-i i madh për hash-imin. Ky disavantazh nuk është shumë i dukshëm. Edhe për ne ai nuk ishte shumë i dukshëm, prandaj fillimisht në VictoriaMetrics implementimi i intersection ishte përmes map. Por më pas profilizimi tregoi se shumica e kohës së procesorit shpenzohet në shkrimin në map dhe në kontrollimin e ekzistencës së vlerës në këtë map.

Pse shpenzohet koha e procesorit në këto vende? Sepse në këto rreshta Go kryen operacionin e hash-it. Pra, ai llogarit hash-in nga çelësi, në mënyrë që më pas të referohet në indeksin e caktuar në HashMap. Operacioni i llogaritjes së hash-it realizohet për disa dhjetëra nanosekonda. Kjo është e ngadalshme për VictoriaMetrics.

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Unë vendosa të realizoj një bitset, të optimizuar posaçërisht për këtë rast. Ja si duket tani ndërveprimi i dy slices. Këtu krijojmë bitset-in. Shtojmë në të elementët nga slice-i i parë. Pastaj kontrollojmë praninë e këtyre elementëve në slice-in e dytë. Dhe i shtojmë ata në rezultat. Pra, pothuajse nuk ndryshon nga shembulli i mëparshëm. E vetmja gjë është se këtu e zëvendësuam qasjen në map me funksione të personalizuara. shto dhe ka.

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Në pamje të parë duket se kjo duhet të funksionojë më ngadalë, nëse më parë aty përdorej një map standard, dhe këtu thirren disa funksione, por profilizimi tregon se kjo gjë funksionon 10 herë më shpejt se map-i standard për rastin me VictoriaMetrics.

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

Një e metë e këtij implementimi është se nuk është kaq e dukshme, nuk është triviale.

Një tjetër e metë, që shumë mund të mos e vërejnë, është se ky implementim mund të funksionojë keq në disa raste. Kjo do të thotë se është optimizuar për një rast specifik, për këtë rast të prerjes së ids të serive të kohës në VictoriaMetrics. Kjo nuk do të thotë se do të përshtatet për çdo rast. Nëse përdoret gabimisht, do të kemi jo një rritje të performancës, por një gabim out of memory dhe ngadalësimin e performancës.

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Të shqyrtojmë implementimin e kësaj strukture. Nëse dëshironi ta shihni, ajo ndodhet në burimet e VictoriaMetrics, në dosjen lib/uint64set. Ajo është optimizuar saktësisht për rastin e VictoriaMetrics, ku timeseries_id me përfaqëson një vlerë 64-bitore, ku 32 bitët e parë janë konstantë dhe ndyshojnë vetëm 32 bitët e fundit.

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

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Ja API i saj. Ai nuk është shumë i komplikuar. API është përshtatur saktësisht për shembujt specifikë të përdorimit në VictoriaMetrics. Kjo do të thotë se nuk ka funksione të panevojshme. Këtu janë funksionet që përdoren qartë nga VictoriaMetrics.

Ka funksione shto, që shton vlera të reja. Ka një funksion ka, e cila kontrollon vlerat e reja. Dhe ka një funksion fshij, e cila heq vlerat. Ka një funksion ndihmës len, e cila kthen madhësinë e grupit. Funksioni clone klonon grupin. Dhe funksioni appendto e shndërron këtë set në një slice timeseries_ids.

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

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

  • ItemsCount – kjo Ă«shtĂ« njĂ« fushĂ« ndihmĂ«se, pĂ«r tĂ« kthyer shpejt numrin e elementeve nĂ« set. Do tĂ« ishte e mundur pa kĂ«tĂ« fushĂ« ndihmĂ«se, por ishte e nevojshme tĂ« shtohej kĂ«tu, pasi VictoriaMetrics shpesh pyet nĂ« algoritmet e saj pĂ«r gjatĂ«sinĂ« e 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 strukturave.

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

Bucket64 Ă«shtĂ« i pĂ«rbĂ«rĂ« nga njĂ« array uint64. GjatĂ«sia llogaritet me ndihmĂ«n e kĂ«tyre konstantave. NĂ« njĂ« bucket16 maksimumi mund tĂ« ruajĂ« 2^16=65536 bit. NĂ«se e ndan kĂ«tĂ« me 8, atĂ«herĂ« kemi 8 kilobyte. NĂ«se e ndan pĂ«rsĂ«ri me 8, atĂ«herĂ« Ă«shtĂ« 1000 uint64 vlerĂ«. Pra, Bucket16 – kjo Ă«shtĂ« njĂ« strukturĂ« 8-kilobyte.

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Le të shqyrtojmë se si është realizuar një nga metodologjitë e kësaj strukture për shtimin e një vlerë të re.

Çdo gjĂ« fillon me uint64 vlerĂ«n. Llogarisim 32 bitĂ«t e sipĂ«rm, llogarisim 32 bitĂ«t e poshtĂ«m. ShkruajmĂ« nĂ« tĂ« gjitha buckets. KrahasojmĂ« 32 bitĂ«t e sipĂ«rm nĂ« çdo bucket me vlerĂ«n qĂ« po shtojmĂ«. NĂ«se ato pĂ«rputhen, thĂ«rrasim funksionin shto nĂ« strukturĂ«n b32 buckets. Dhe shtojmĂ« aty 32 bitĂ«t e poshtĂ«m. NĂ«se ky Ă«shtĂ« kthyer e vĂ«rtetĂ«, atĂ«herĂ« kjo do tĂ« thotĂ« se ne e kemi shtuar njĂ« vlerĂ« tĂ« tillĂ« dhe nuk e kemi pasur mĂ« parĂ«. NĂ«se ai kthen false, atĂ«herĂ« njĂ« vlerĂ« e tillĂ« ka ekzistuar tashmĂ«. MĂ« pas rritim numrin e elementeve nĂ« strukturĂ«.

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

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Kjo është realizimi i funksionit b32.add. Ajo është e ngjashme me realizimin e mëparshëm. Ne llogarisim 16 bitët e lartë, 16 bitët e ulët.

Pastaj ne kalojmë në të gjitha 16 bitët e sipërm. Gjejmë përputhjet. Në rast përputhjeje, thërrasim metodën add, e cila do të shqyrtohet në faqen tjetër për bucket16.

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Dhe tani niveli më i ulët, i cili duhet të jetë maksimalisht i optimizuar. Ne llogarisim për uint64 vlerën id në slice bit, si dhe bitmask. Ky është një maskë për këtë vlerë 64-bit, sipas së cilës mund të kontrolloni praninë e këtyre bitëve, ose t'i vendosni ato. Ne kontrollojmë praninë e këtyre bitëve, i vendosim ata, dhe kthejmë praninë. Kjo është implementimi ynë, i cili ka lejuar të shpejtojë operacionin e përputhjes së IDs së serive të kohës deri në 10 herë krahasuar me maps normale.

Optimizimet 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 rastësisht, por pas profilizimit të kodit në production.

Kjo Ă«shtĂ« rregulli kryesor i optimizimit – mos shtoni optimizimin duke supozuar se aty do tĂ« jetĂ« njĂ« ngushticĂ«, sepse mund tĂ« rezultojĂ« qĂ« nuk do tĂ« ketĂ« ngushticĂ«. Optimizimi zakonisht keqĂ«son cilĂ«sinĂ« e kodit. Prandaj, Ă«shtĂ« mĂ« mirĂ« tĂ« optimizoni vetĂ«m pas profilizimit dhe, idealisht, nĂ« production, nĂ« mĂ«nyrĂ« qĂ« tĂ« jenĂ« tĂ« dhĂ«na reale. Ata qĂ« janĂ« tĂ« interesuar, mund tĂ« shikoni burimet e VictoriaMetrics dhe tĂ« studioni optimizime tĂ« tjera qĂ« ka.

Optimizimet Go në VictoriaMetrics. Aleksandër Valjalkin

Kam njĂ« pyetje pĂ«r bitset. ËshtĂ« shumĂ« e ngjashme me implementimin e C++ vector bool, bitset i optimizuar. A e keni marrĂ« implementimin nga aty?

Jo, jo nuk është nga aty. Kur realzoja këtë bitset, e shikoja ndihmën nga struktura e këtyre id-ëve të kohës, të cilat përdoren në VictoriaMetrics. Struktura 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ë bit-i, aq më shpesh mund të ndryshojë. Prandaj, kjo realizim është optimizuar për këtë strukturë të dhënash. Realizimi në C++, sa di unë, është optimizuar për rastin e përgjithshëm. Nëse bëni optimizim për rastin e përgjithshëm, atëherë do të thotë se ajo nuk do të jetë më optimale për rastin specifik.

Ju rekomandoj gjithashtu të shikoni prezantimin e Aleksej Milovidit. Ai fliste diku para një muaji për optimizimet në ClickHouse për specializime të caktuara. Ai vërtet tregon se në rastin e përgjithshëm realizimi në C++ ose ndonjë realizim tjetër janë fokaluar për një punë të mirë në mes të gjithë. Ajo mund të punojë më keq se një realizim të specializuar për njohuritë specifike, siç është rasti ynë, kur ne e dimë se 32 bitët e sipërm janë kryesisht të qëndrueshëm.

Kam një pyetje të dytë. Cila është ndryshimi kardinal nga InfluxDB?

Ka shumë dallime drastike. Nëse flasim për performancën dhe konsumimin e memories, InfluxDB në testime tregon deri në 10 herë më shumë konsumim të memories për seritë me cardinalitet të lartë, kur i keni shumë, për shembull, miliona. Për shembull, VictoriaMetrics konsumon 1 GB për një milion serish aktive, ndërsa InfluxDB konsumon 10 GB. Dhe kjo është një diferencë e madhe.

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

Dhe një dallim tjetër është se InfluxDB ka një model të dhënash pak të çuditshëm, ku në çdo rresht mund të ruhen disa fushë me grupe të ndryshme etiketash. Këto rreshta ndahen gjithashtu në tabela të ndryshme. Këto komplikime shtesë e bëjnë të vështirë punën e mëvonshme me këtë bazë. Ajo është e vështirë për t'u mbajtur dhe kuptuar.

NĂ« VictoriaMetrics gjithçka Ă«shtĂ« shumĂ« mĂ« e thjeshtĂ«. Çdo seri temporale pĂ«rfaqĂ«son njĂ« çelĂ«s-vlerĂ«. Vlera Ă«shtĂ« njĂ« grup pikash – (timestamp, vlera), ndĂ«rsa çelĂ«si Ă«shtĂ« njĂ« grup label=value. Nuk ka ndarje nĂ« fushat dhe matjet. Kjo ju lejon tĂ« zgjidhni çdo tĂ« dhĂ«nĂ« dhe pastaj t'i kombinoni, shtoni, zbritni, shumĂ«zoni, ndajeni ndryshe nga InfluxDB, ku llogaritjet midis radhĂ«ve tĂ« ndryshme ende nuk janĂ« realizuar, sa di unĂ«. NĂ«se janĂ« realizuar, atĂ«herĂ« Ă«shtĂ« e vĂ«shtirĂ«, duhet tĂ« shkruani shumĂ« kod.

Kam një pyetje të saktësimit. E kuptova saktë që kishte një problem, për të cilin keni folur, që ky indeks i invertuar nuk fiton në memorie, prandaj bëhet particionim?

NĂ« fillim, kam treguar njĂ« implementim naiv tĂ« indeksit tĂ« invertuar nĂ« map’in standard tĂ« Go. Kjo implementim nuk Ă«shtĂ« e pĂ«rshtatshme pĂ«r databaza, sepse ky indeks i invertuar nuk ruhet nĂ« disk, ndĂ«rsa databaza duhet tĂ« ruajĂ« nĂ« disk qĂ« tĂ« dhĂ«nat tĂ« mbeten tĂ« aksesueshme pas rinisjes. NĂ« kĂ«tĂ« implementim, pas rinisjes sĂ« aplikacionit, indeksi i invertuar do tĂ« humbasĂ«. Dhe do tĂ« humbni qasje nĂ« tĂ« gjitha tĂ« dhĂ«nat sepse nuk do tĂ« jeni nĂ« gjendje t'i gjeni ato.

PĂ«rshĂ«ndetje! Faleminderit pĂ«r raportin! Quhem Pavel. Jam nga kompania Wildberries. Kam disa pyetje pĂ«r ju. Pyetja e parĂ«. Si mendoni, nĂ«se do tĂ« zgjidhnit njĂ« parim tjetĂ«r pĂ«r ndĂ«rtimin e arkitekturĂ«s sĂ« aplikacionit tuaj dhe do tĂ« ndanit tĂ« dhĂ«nat sipas kohĂ«s, ndoshta do tĂ« kishit mundĂ«si tĂ« bĂ«nte ndĂ«rprerje tĂ« tĂ« dhĂ«nave gjatĂ« kĂ«rkimit, duke u bazuar vetĂ«m nĂ« atĂ« se nĂ« njĂ« ndarje ndodhen tĂ« dhĂ«nat pĂ«r njĂ« periudhĂ« kohe, pra pĂ«r njĂ« interval kohor, dhe nuk do t'ju duhej tĂ« shqetĂ«soheshit se copat janĂ« tĂ« shpĂ«rndara ndryshe? Pyetja numĂ«r 2 — pasi realizoni njĂ« algoritĂ«m tĂ« tillĂ« me bitset dhe gjithçka tjetĂ«r, ndoshta keni provuar tĂ« pĂ«rdorni instrukcionet e procesorit? Ndoshta keni provuar optimizime tĂ« tilla?

Për pyetjen e dytë do të përgjigjem menjëherë. Ne ende nuk kemi arritur aty. Por nëse do të nevojitet, do të arrijmë. Dhe pyetja e parë, cila ishte ajo?

Keni diskutuar dy skenarë. Dhe keni thënë se keni zgjedhur të dytin me një implementim më të komplikuar. Dhe nuk e preferuat të parin, ku të dhënat janë të ndara sipas kohës.

Po. Në rastin e parë, vëllimi total i indeksit do të ishte më i madh, sepse në çdo ndarje do të duhej të mbanim të dhëna të dyfishta për ato seri temporale që vazhdojnë përmes të gjitha ndarjeve. Dhe nëse ju keni një raport churn të ulët për seritë temporale, do të thotë se vazhdimisht përdoren të njëjtat seria, atëherë në rastin e parë ne do të humbnim shumë më tepër në hapësirën e diskut të përdorur krahasuar me rastin e dytë.

Po, ndarja sipas kohës është një opsion i mirë. Këtë e përdor Prometheus. Por në Prometheus ka një mangësi tjetër. Gjatë bashkimit të këtyre copave të dhënash, ai kërkon të mbajë në memorie metainformacionin për të gjitha etiketat dhe seritë temporale. Prandaj, nëse copat e dhënave janë të mëdha që ai po bashkon, konsumimi i memories rritet shumë gjatë bashkimit, në krahasim me VictoriaMetrics. Gjatë bashkimit, VictoriaMetrics në tërësi nuk konsumon memorie, disa kilobajt përdoren, pa marrë parasysh përmasat e copave të dhënash që po bashkohen.

Algoritmi, qĂ« pĂ«rdorni, konsumon memorie. NĂ« tĂ« shĂ«nohen etiketat e serisĂ« temporale, ku ka vlera. KĂ«shtu kontrolloni praninĂ« e çiftĂ«zuar nĂ« njĂ« grumbull tĂ« dhĂ«nash dhe nĂ« tjetrin. Dhe kuptoni – a ka ndodhur ndĂ«rprerja apo jo. Zakonisht, nĂ« bazat e tĂ« dhĂ«nave implementohen kursore, itere, qĂ« mbajnĂ« gjendjen e tyre aktuale dhe shkelen nĂ« tĂ« dhĂ«na tĂ« renditura, pĂ«r shkak se keni njĂ« kompleksitet tĂ« thjeshtĂ« nĂ« kĂ«to operacione.

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

Po.

NĂ« LevelDB apo nĂ« mergeset kemi pikĂ«risht rreshtat e renditur. Mund tĂ« kalojmĂ« me kursor dhe tĂ« gjejmĂ« ndĂ«rprerjen. Dhe pse nuk e pĂ«rdorim? Sepse – Ă«shtĂ« e ngadaltĂ«. Sepse kursorĂ«t supozojnĂ« 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, atĂ«herĂ« rezulton se harxhojmĂ« gjysmĂ« sekonde vetĂ«m pĂ«r thirrjen e funksionit.

Po, ka një pyetje tjetër. Pyetja ndoshta do të tingëllojë pak e çuditshme. Pse në momentin e marrjes së të dhënave nuk mund të llogariten të gjithë aggregatët e nevojshëm dhe të ruhet forma e duhur e tyre? Pse të ruajmë volumet e mëdha në disa sisteme si VictoriaMetrics, ClickHouse, etj., vetëm për të shpenzuar shumë kohë më pas për to?

Do tĂ« jap njĂ« shembull pĂ«r ta bĂ«rĂ« mĂ« tĂ« qartĂ«. Supozoni, si funksionon njĂ« speedometĂ«r i vogĂ«l lodĂ«r? Ai regjistron distancĂ«n qĂ« keni udhĂ«tuar, duke e shtuar vazhdimisht atĂ« nĂ« njĂ« matje, dhe nĂ« tĂ« dytĂ«n – kohĂ«n. Dhe e ndan. Dhe merr shpejtĂ«sinĂ« mesatare. Mund tĂ« bĂ«ni diçka tĂ« ngjashme. TĂ« mbledhni tĂ« gjitha faktet e nevojshme nĂ« fluturim.

Mirë, e kuptova pyetjen. Shembulli juaj ka vend për jetë. Nëse e dini se cilat agregate ju nevojiten, kjo është zgjidhja më e mirë. Por problemi është se njerëzit ruajnë këto metrika, disa të dhëna në ClickHouse dhe ata nuk dinë se si do t'i agregojnë apo filtrojnë ato në të ardhmen, prandaj duhet të ruajnë të gjitha të dhënat e papërpunuara. Po sikur të dini se çfarë duheni të llogarisni mesatare, pse të mos e llogarisni atë, në vend që të ruani një sasi të madhe të dhënash të papërpunuara? Por kjo është vetëm në rast se e dini saktësisht se çfarë ju nevojitet.

Për më tepër, bazat e të dhënave për ruajtjen e serive temporale mbështesin llogaritjen e agregateve. Për shembull, Prometheus mbështet rregullat e regjistrimit. Pra, kjo mund të bëhet, nëse e dini se cilat agregate do t'ju nevojiten. Në VictoriaMetrics kjo nuk është ende e disponueshme, por zakonisht përpara saj vendoset Prometheus, në të cilin mund ta bëni atë në rregullat e regjistrimit.

Për shembull, në punën time të mëparshme, ishte e nevojshme të llogaritej numri i ngjarjeve në një dritare lëvizëse për orën e fundit. Problemi është se, për këtë, duhej të krijoja një implementim të personalizuar në Go, pra një shërbim për llogaritjen e këtij elementi. Ky shërbim përfundimisht nuk ishte i thjeshtë, sepse është e vështirë të llogaritet. Implementimi mund të jetë i thjeshtë nëse ju nevojitet të llogaritni disa aggragate në intervale fikse kohore. Nëse dëshoni të llogaritni ngjarjet në një dritare lëvizëse, atëherë nuk është aq e thjeshtë sa duket. Mendoj se kjo akoma nuk është realizuar në ClickHouse ose në bazat e të dhënave të kohës, sepse është e komplikuar për t'u zbatuar.

Dhe njĂ« pyetje tjetĂ«r. Tani po flisnim pĂ«r average dhe mĂ« erdhi nĂ« mend se dikur kishte njĂ« gjĂ« si Graphite me backend Carbon. Ai e kishte aftĂ«sinĂ« tĂ« filtronte tĂ« dhĂ«nat e vjetra, pra tĂ« linte njĂ« pikĂ« nĂ« minutĂ«, njĂ« pikĂ« nĂ« orĂ« dhe kĂ«shtu me radhĂ«. NĂ« parim, kjo Ă«shtĂ« mjaft e pĂ«rshtatshme nĂ«se na duhen tĂ« dhĂ«na tĂ« papĂ«rpunuara, gjysmĂ« tĂ« thĂ«nĂ«, pĂ«r njĂ« muaj, dhe gjithĂ« tĂ« tjerat mund tĂ« filtrohen. Por Prometheus dhe VictoriaMetrics nuk e mbĂ«shtesin kĂ«tĂ« funksionalitet. ËshtĂ« parashikuar tĂ« mbĂ«shtetet? NĂ«se jo, pse?

Faleminderit pĂ«r pyetjen. PĂ«rdoruesit tanĂ« e bĂ«jnĂ« atĂ« ndonjĂ«herĂ«. Ata pyesin kur do ta shtojmĂ« mbĂ«shtetjen pĂ«r downsampling. KĂ«tu ka disa probleme. SĂ« pari, çdo pĂ«rdorues e kupton nĂ«n downsampling diçka tĂ« tijĂ«n: dikush dĂ«shiron tĂ« marrĂ« çdo pikĂ« tĂ« rastĂ«sishme nĂ« njĂ« interval tĂ« caktuar, dikush dĂ«shiron vlerat maksimale, minimale, mesatare. NĂ«se shumĂ« sisteme shkruajnĂ« tĂ« dhĂ«na nĂ« bazĂ«n tuaj, nuk mund t’i trajtoni ato tĂ« gjitha njĂ«soj. Mund tĂ« dalĂ« qĂ« pĂ«r çdo sistem duhet tĂ« pĂ«rdoret njĂ« downsampling i ndryshĂ«m. Dhe kjo Ă«shtĂ« e vĂ«shtirĂ« pĂ«r t'u zbatuar.

Dhe e dyta është se VictoriaMetrics, ashtu si ClickHouse, është optimizuar për të punuar me një volum të madh të të dhënave të papërpunuara, prandaj ajo mund të përpunojë një miliard rreshta për më pak se një sekondë, nëse keni shumë bërthama në sistemin tuaj. Skandimi i pikave të serive temporale në VictoriaMetrics është 50,000,000 pika në sekondë për çdo bërthamë. Dhe këto performanca shkallëzohen në bërthamat ekzistuese. Pra, nëse keni 20 bërthama, për shembull, do të arrini të skanoni një miliard pikash në sekondë. Dhe kjo karakteristikë e VictoriaMetrics dhe ClickHouse zvogëlon nevojën për downsampling.

NjĂ« karakteristikĂ« tjetĂ«r Ă«shtĂ« se VictoriaMetrics comprimim kĂ«to tĂ« dhĂ«na me efikasitet. Kompresimi mesatar nĂ« prodhim Ă«shtĂ« nga 0.4 deri nĂ« 0.8 byte pĂ«r pikĂ«. Çdo pikĂ« pĂ«rfshin njĂ« timestamp + njĂ« vlerĂ«. Dhe kjo kompresohet mĂ« pak se njĂ« byte nĂ« mesatare.

Sergei. Kam një pyetje. Cila është kuanti minimal i kohës për regjistrim?

NjĂ« milisekondĂ«. SĂ« fundmi, kishim njĂ« bisedĂ« me zhvillues tĂ« tjerĂ« tĂ« bazave tĂ« tĂ« dhĂ«nave pĂ«r seritĂ« e pĂ«rkohshme. Kuanti minimal i kohĂ«s te ata Ă«shtĂ« njĂ« sekondĂ«. Edhe nĂ« Graphite, pĂ«r shembull, Ă«shtĂ« gjithashtu njĂ« sekondĂ«. NĂ« OpenTSDB gjithashtu Ă«shtĂ« njĂ« sekondĂ«. NĂ« InfluxDB – saktĂ«si nanosekondash. NĂ« VictoriaMetrics – njĂ« milisekondĂ«, sepse nĂ« Prometheus Ă«shtĂ« njĂ« milisekondĂ«. Dhe VictoriaMetrics Ă«shtĂ« zhvilluar fillimisht si ruajtje e largĂ«t pĂ«r Prometheus. Por tani ajo mund tĂ« ruajĂ« tĂ« dhĂ«na edhe nga sisteme tĂ« tjera.

Njeriu me të cilin kam biseduar thotë se ata kanë saktësi në sekonda - kjo është e mjaftueshme për ta, sepse varet nga lloji i të dhënave që ruhen në bazën e të dhënave të serive temporale. Nëse janë të dhëna DevOps ose të dhëna nga infrastruktura, ku i mbledhni ato me interval prej 30 sekondash deri në një minutë, atëherë saktësia në sekonda është e mjaftueshme; më pak nuk është e nevojshme. Por nëse po mbledhni këto të dhëna nga sistemet e tregtisë me frekuencë të lartë, atëherë saktësia në nanosekonda është e nevojshme.

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

Faleminderit! Dhe njĂ« pyetje tjetĂ«r. ÇfarĂ« pĂ«rputhshmĂ«rie ka nĂ« PromQL?

Përputhje e plotë e anasjelltë. VictoriaMetrics mbështet plotësisht PromQL. Përveç kësaj ajo shton edhe funksionalitete të avancuara mbi PromQL, që quhet MetricsQL. Në lidhje me këtë funksionalitet të avancuar ka një raport në YouTube. Unë kam folur në Monitoring Meetup këtë pranverë në Shën Petersburg.

kanalin Telegram VictoriaMetrics.

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

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

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

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

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

Burimi: habr.com

Bli njĂ« hosting tĂ« besueshĂ«m pĂ«r faqet me mbrojtje DDoS, VPS VDS serverĂ« đŸ”„ Bli njĂ« hosting tĂ« besueshĂ«m pĂ«r faqet me mbrojtje DDoS, VPS VDS serverĂ« | ProHoster