Soovitan teil lugeda Alexander Valyalkini 2019. aasta lõpu aruande „Optimeerimine VictoriaMetricsis” ärakirja.
Siin on link selle raporti videole -
Räägi meile endast. Mina olen Aleksander Valjalkin. Siin fast
või koos quick
eesliide.
Praegu töötan VictoriaMetricsi kallal. Mis see on ja mida ma seal teen? Ma räägin sellest selles esitluses.
Aruande ülevaade on järgmine:
- Esiteks räägin teile, mis on VictoriaMetrics.
- Siis ma ütlen teile, mis aegread on.
- Seejärel räägin teile, kuidas aegridade andmebaas töötab.
- Järgmisena räägin teile andmebaasi arhitektuurist: millest see koosneb.
- Ja siis liigume edasi VictoriaMetricsi optimeerimiste juurde. See on ümberpööratud indeksi optimeerimine ja Go bitikomplekti rakendamise optimeerimine.
Kas keegi publikust teab, mis on VictoriaMetrics? Vau, paljud inimesed juba teavad. See on hea uudis. Kes veel ei tea, siis see on aegridade andmebaas. See põhineb ClickHouse'i arhitektuuril, mõnel ClickHouse'i rakendamise üksikasjal. Näiteks: MergeTree, paralleelarvutus kõigi saadaolevate protsessorituumade kohta ja jõudluse optimeerimine protsessori vahemällu paigutatud andmeplokkidega töötades.
VictoriaMetrics pakub paremat andmete tihendamist kui teised aegridade andmebaasid.
See mastaabib vertikaalselt - see tähendab, et saate ühes arvutis lisada rohkem protsessoreid, rohkem RAM-i. VictoriaMetrics kasutab neid olemasolevaid ressursse edukalt ja parandab lineaarset tootlikkust.
VictoriaMetrics skaleerib ka horisontaalselt – see tähendab, et saate VictoriaMetricsi klastrisse lisada täiendavaid sõlme ja selle jõudlus suureneb peaaegu lineaarselt.
Nagu arvasite, on VictoriaMetrics kiire andmebaas, sest ma ei saa teisi kirjutada. Ja see on kirjas Go, nii et ma räägin sellest sellel kohtumisel.
Kes teab, mis on aegrida? Ta tunneb ka paljusid inimesi. Aegrida on paaride jada (timestamp, значение)
, kus need paarid on sorteeritud aja järgi. Väärtus on ujukomaarv – float64.
Iga aegrida on üheselt identifitseeritud võtmega. Millest see võti koosneb? See koosneb võtme-väärtuste paaride mittetühjast komplektist.
Siin on näide aegreast. Selle seeria võti on paaride loend: __name__="cpu_usage"
on mõõdiku nimi, instance="my-server"
- see on arvuti, kuhu see mõõdik kogutakse, datacenter="us-east"
- see on andmekeskus, kus see arvuti asub.
Saime aegrea nime, mis koosnes kolmest võtme-väärtuse paarist. See võti vastab paaride loendile (timestamp, value)
. t1, t3, t3, ..., tN
- need on ajatemplid, 10, 20, 12, ..., 15
— vastavad väärtused. See on antud rea protsessori kasutus teatud ajahetkel.
Kus saab aegridu kasutada? Kas kellelgi on aimu?
- DevOpsis saate mõõta protsessorit, RAM-i, võrku, rps-i, vigade arvu jne.
- IoT – saame mõõta temperatuuri, rõhku, geokoordinaate ja midagi muud.
- Samuti finantseerimine – saame jälgida kõikvõimalike aktsiate ja valuutade hindu.
- Lisaks saab aegridu kasutada tootmisprotsesside jälgimisel tehastes. Meil on kasutajaid, kes kasutavad VictoriaMetricsi tuuleturbiinide jälgimiseks robotite jaoks.
- Aegridad on kasulikud ka erinevate seadmete anduritelt info kogumisel. Näiteks mootori jaoks; rehvirõhu mõõtmiseks; kiiruse, vahemaa mõõtmiseks; bensiinikulu mõõtmiseks jne.
- Aegridu saab kasutada ka lennukite jälgimiseks. Igal lennukil on must kast, mis kogub aegridu lennuki tervise erinevate parameetrite kohta. Aegridu kasutatakse ka kosmosetööstuses.
- Tervishoid on vererõhk, pulss jne.
Võib-olla on rohkem rakendusi, mille ma unustasin, kuid loodan, et saate aru, et aegridu kasutatakse tänapäeva maailmas aktiivselt. Ja nende kasutusmaht kasvab iga aastaga.
Miks vajate aegridade andmebaasi? Miks ei saa aegridade salvestamiseks kasutada tavalist relatsiooniandmebaasi?
Kuna aegread sisaldavad tavaliselt suurel hulgal informatsiooni, mida tavapärastes andmebaasides on raske salvestada ja töödelda. Seetõttu ilmusid aegridade jaoks spetsiaalsed andmebaasid. Need alused salvestavad tõhusalt punkte (timestamp, value)
antud võtmega. Need pakuvad API-d salvestatud andmete lugemiseks võtme, ühe võtme-väärtuse paari või mitme võtme-väärtuse paari või regexpi järgi. Näiteks kui soovite leida Ameerikas asuvas andmekeskuses kõigi oma teenuste CPU koormust, siis peate kasutama seda pseudopäringut.
Tavaliselt pakuvad aegridade andmebaasid spetsiaalseid päringukeeli, kuna aegridade SQL ei sobi eriti hästi. Kuigi on andmebaase, mis toetavad SQL-i, pole see eriti sobiv. Päringukeeled nagu
Selline näeb välja kaasaegne aegridade andmebaasi arhitektuur, kasutades näitena VictoriaMetricsi.
See koosneb kahest osast. See on ümberpööratud indeksi ja aegridade väärtuste salvestamine. Need hoidlad on eraldatud.
Kui andmebaasi saabub uus kirje, pääseme esmalt juurde pöördindeksile, et leida antud komplekti aegrea identifikaator label=value
antud mõõdiku jaoks. Leiame selle identifikaatori ja salvestame väärtuse andmesalves.
Kui tuleb taotlus TSDB-st andmete toomiseks, läheme esmalt ümberpööratud indeksisse. Võtame kõik timeseries_ids
rekordid, mis sellele komplektile vastavad label=value
. Ja siis saame andmelaost kõik vajalikud andmed, mida indekseerib timeseries_ids
.
Vaatame näidet, kuidas aegridade andmebaas töötleb sissetulevat valikupäringut.
- Esiteks saab ta kõik
timeseries_ids
inverteeritud indeksist, mis sisaldab antud paarelabel=value
, või rahuldada etteantud regulaaravaldis. - Seejärel hangib see leitud punktide jaoks välja kõik andmepunktid andmesalvestusest teatud ajaintervalli järel
timeseries_ids
. - Pärast seda teeb andmebaas vastavalt kasutaja soovile nende andmepunktide kohta mõned arvutused. Ja pärast seda tagastab see vastuse.
Selles esitluses räägin teile esimesest osast. See on otsing timeseries_ids
ümberpööratud indeksi järgi. Teist ja kolmandat osa saate vaadata hiljem
Liigume edasi pöördindeksi juurde. Paljud võivad arvata, et see on lihtne. Kes teab, mis on pööratud indeks ja kuidas see töötab? Oh, inimesi pole enam nii palju. Proovime aru saada, mis see on.
See on tegelikult lihtne. See on lihtsalt sõnastik, mis kaardistab võtme väärtusele. Mis on võti? See paar label=value
Kus label
и value
- need on jooned. Ja väärtused on komplekt timeseries_ids
, mis sisaldab antud paari label=value
.
Pööratud indeks võimaldab teil kiiresti kõike leida timeseries_ids
, mis on andnud label=value
.
Samuti võimaldab see kiiresti leida timeseries_ids
aegridad mitmele paarile label=value
või paaridele label=regexp
. Kuidas see juhtub? Leides hulga ristumiskoha timeseries_ids
iga paari jaoks label=value
.
Vaatame ümberpööratud indeksi erinevaid rakendusi. Alustame kõige lihtsamast naiivsest teostusest. Ta näeb välja selline.
Funktsioon getMetricIDs
saab stringide nimekirja. Iga rida sisaldab label=value
. See funktsioon tagastab loendi metricIDs
.
Kuidas see töötab? Siin on globaalne muutuja nimega invertedIndex
. See on tavaline sõnastik (map
), mis vastendab stringi sisenditeks viiludeks. Rida sisaldab label=value
.
Funktsiooni rakendamine: hanki metricIDs
esimese jaoks label=value
, siis läbime kõik muu label=value
, saame aru metricIDs
neile. Ja helistage funktsioonile intersectInts
, mida arutatakse allpool. Ja see funktsioon tagastab nende loendite ristumiskoha.
Nagu näete, pole ümberpööratud indeksi rakendamine kuigi keeruline. Kuid see on naiivne teostus. Millised puudused sellel on? Naiivse teostuse peamine puudus on see, et selline ümberpööratud indeks salvestatakse RAM-i. Pärast rakenduse taaskäivitamist kaotame selle indeksi. Seda indeksit kettale ei salvestata. Selline ümberpööratud indeks tõenäoliselt andmebaasi ei sobi.
Teine puudus on samuti seotud mäluga. Pööratud indeks peab mahtuma RAM-i. Kui see ületab RAM-i suuruse, siis ilmselgelt saame - mälu veast välja. Ja programm ei tööta.
Seda probleemi saab lahendada valmislahenduste abil, näiteks
Lühidalt, me vajame andmebaasi, mis võimaldab kiiresti teha kolm toimingut.
- Esimene toiming on salvestamine
ключ-значение
sellesse andmebaasi. Ta teeb seda väga kiiresti, kusключ-значение
on suvalised stringid. - Teine toiming on väärtuse kiire otsimine antud võtme abil.
- Ja kolmas toiming on kõigi väärtuste kiire otsimine antud prefiksi järgi.
LevelDB ja RocksDB – need andmebaasid töötasid välja Google ja Facebook. Kõigepealt tuli LevelDB. Siis võtsid Facebooki poisid LevelDB ja hakkasid seda täiustama, tegid RocksDB. Nüüd töötavad peaaegu kõik sisemised andmebaasid Facebookis RocksDB-s, sealhulgas need, mis on üle viidud RocksDB-sse ja MySQL-i. Nad andsid talle nime
Inverteeritud indeksit saab rakendada LevelDB abil. Kuidas seda teha? Salvestame võtmena label=value
. Ja väärtus on selle aegrea identifikaator, kus paar esineb label=value
.
Kui meil on antud paariga palju aegridu label=value
, siis on selles andmebaasis palju sama võtmega ja erineva võtmega ridu timeseries_ids
. Kõigi nimekirja saamiseks timeseries_ids
, mis algavad sellega label=prefix
, teeme vahemiku skannimise, mille jaoks see andmebaas on optimeeritud. See tähendab, et valime kõik read, mis algavad label=prefix
ja hankige vajalik timeseries_ids
.
Siin on näide selle kohta, kuidas see Go-s välja näeks. Meil on tagurpidi indeks. See on LevelDB.
Funktsioon on sama, mis naiivsel teostusel. See kordab naiivset teostust peaaegu rida-realt. Ainus asi on selles, et selle asemel, et pöörduda map
pääseme juurde pööratud indeksile. Saame kõik väärtused esimeseks label=value
. Seejärel käime läbi kõik ülejäänud paarid label=value
ja hankige neile vastavad mõõdikute ID-d. Siis leiame ristmiku.
Tundub, et kõik on korras, kuid sellel lahendusel on puudusi. VictoriaMetrics rakendas algselt LevelDB-l põhineva ümberpööratud indeksi. Kuid lõpuks pidin sellest loobuma.
Miks? Kuna LevelDB on aeglasem kui naiivne rakendamine. Naiivses teostuses, kui antud võti on antud, hangime kohe kogu lõigu metricIDs
. See on väga kiire toiming – kogu viil on kasutamiseks valmis.
LevelDB-s iga kord, kui funktsiooni kutsutakse GetValues
peate läbima kõik read, mis algavad label=value
. Ja hankige iga rea väärtus timeseries_ids
. Sellistest timeseries_ids
koguge neist tükk timeseries_ids
. Ilmselgelt on see palju aeglasem kui lihtsalt tavalisele kaardile võtmega juurdepääs.
Teine puudus on see, et LevelDB on kirjutatud C-keeles. C-funktsioonide väljakutsumine Go-st ei ole väga kiire. See võtab sadu nanosekundeid. See pole väga kiire, sest võrreldes go-s kirjutatud tavalise funktsioonikutsega, mis võtab aega 1-5 nanosekundit, on jõudluse vahe kümneid kordi. VictoriaMetricsi jaoks oli see saatuslik viga :)
Nii et ma kirjutasin enda ümberpööratud indeksi teostuse. Ja ta kutsus teda
Mergeset põhineb MergeTree andmestruktuuril. See andmestruktuur on laenatud ClickHouse'ilt. Ilmselgelt tuleks mergeset kiireks otsimiseks optimeerida timeseries_ids
vastavalt antud võtmele. Mergeset on kirjutatud täielikult Go keeles. Sa näed
Mergeset API on väga sarnane LevelDB ja RocksDB-ga. See tähendab, et see võimaldab teil kiiresti uusi kirjeid sinna salvestada ja kiiresti kirjeid etteantud eesliite järgi valida.
Mergeseti puudustest räägime hiljem. Nüüd räägime sellest, millised probleemid tekkisid VictoriaMetricsiga tootmises pöördindeksi rakendamisel.
Miks need tekkisid?
Esimene põhjus on suur väljalangemise määr. Vene keelde tõlgituna on see aegridade sagedane muutus. See on siis, kui aegrida lõpeb ja algab uus seeria või algab palju uusi aegridu. Ja seda juhtub sageli.
Teine põhjus on aegridade suur arv. Alguses, kui seire kogus populaarsust, oli aegridade arv väike. Näiteks peate iga arvuti jaoks jälgima protsessori, mälu, võrgu ja ketta koormust. 4 aegrida arvuti kohta. Oletame, et teil on 100 arvutit ja 400 aegrida. Seda on väga vähe.
Aja jooksul said inimesed aru, et nad saavad mõõta üksikasjalikumat teavet. Näiteks mõõtke mitte kogu protsessori koormust, vaid iga protsessori tuuma eraldi. Kui teil on 40 protsessorituuma, on teil protsessori koormuse mõõtmiseks 40 korda rohkem aegridu.
Kuid see pole veel kõik. Igal protsessori tuumal võib jõudeoleku ajal olla mitu olekut, näiteks jõudeolek. Ja ka töötada kasutajaruumis, töötada tuumaruumis ja muudes olekutes. Ja iga sellist olekut saab mõõta ka eraldi aegreana. See suurendab lisaks ridade arvu 7-8 korda.
Ühest mõõdikust saime ainult ühe arvuti kohta 40 x 8 = 320 mõõdikut. Korrutades 100-ga, saame 32 asemel 000 400.
Siis tuli Kubernetes. Ja see läks hullemaks, kuna Kubernetes saab majutada paljusid erinevaid teenuseid. Iga Kubernetese teenus koosneb paljudest kaustadest. Ja seda kõike tuleb jälgida. Lisaks juurutame pidevalt teie teenuste uusi versioone. Iga uue versiooni jaoks tuleb luua uus aegrida. Selle tulemusena kasvab aegridade arv plahvatuslikult ja oleme silmitsi suure hulga aegridade probleemiga, mida nimetatakse kõrge kardinaalsuseks. VictoriaMetrics tuleb sellega võrreldes teiste aegridade andmebaasidega edukalt toime.
Vaatame lähemalt kõrget loobumismäära. Mis põhjustab tootmise kõrget katkestamise määra? Sest mõned siltide ja siltide tähendused muutuvad pidevalt.
Võtke näiteks Kubernetes, millel on kontseptsioon deployment
, st kui teie rakenduse uus versioon avaldatakse. Mingil põhjusel otsustasid Kubernetese arendajad lisada sildile juurutamise ID.
Milleni see viis? Lisaks katkevad iga uue kasutuselevõtuga kõik vanad aegread ja nende asemel algavad uued aegread uue sildi väärtusega deployment_id
. Selliseid ridu võib olla sadu tuhandeid ja isegi miljoneid.
Kõige selle juures on oluline see, et aegridade koguarv kasvab, kuid hetkel aktiivsete ja andmeid vastuvõtvate aegridade arv jääb muutumatuks. Seda olekut nimetatakse kõrgeks churn rate’iks.
Kõrge loobumissageduse peamine probleem on tagada kõigi aegridade jaoks konstantne otsimiskiirus teatud sildikomplekti jaoks teatud ajavahemiku jooksul. Tavaliselt on see viimase tunni või päeva ajavahemik.
Kuidas seda probleemi lahendada? Siin on esimene variant. Selle eesmärk on jagada ümberpööratud indeks aja jooksul sõltumatuteks osadeks. See tähendab, et mõni ajavahemik möödub, lõpetame töö praeguse inverteeritud indeksiga. Ja looge uus ümberpööratud indeks. Möödub järjekordne ajaintervall, loome teise ja teise.
Ja nendest ümberpööratud indeksitest valimi võttes leiame pöördindeksite komplekti, mis jäävad antud intervalli. Ja vastavalt sellele valime sealt aegrea id.
See säästab ressursse, sest me ei pea vaatama osi, mis ei jää antud intervallisse. See tähendab, et kui valime tavaliselt viimase tunni andmed, siis eelmiste ajavahemike puhul jätame päringud vahele.
Selle probleemi lahendamiseks on veel üks võimalus. Selle eesmärk on salvestada iga päeva jaoks eraldi loend sellel päeval toimunud aegridade ID-dest.
Selle lahenduse eeliseks eelmise lahenduse ees on see, et me ei dubleeri aegridade infot, mis aja jooksul ei kao. Nad on pidevalt kohal ega muutu.
Puuduseks on see, et sellist lahendust on keerulisem rakendada ja raskem siluda. Ja VictoriaMetrics valis selle lahenduse. Nii see ajalooliselt juhtus. See lahendus toimib hästi ka võrreldes eelmisega. Kuna seda lahendust ei rakendatud, kuna igas partitsioonis on vaja dubleerida andmeid aegridade jaoks, mis ei muutu, st mis aja jooksul ei kao. VictoriaMetrics oli peamiselt optimeeritud kettaruumi tarbimiseks ja eelmine rakendus muutis kettaruumi tarbimise hullemaks. Kuid see teostus sobib paremini kettaruumi tarbimise minimeerimiseks, nii et see valiti.
Ma pidin temaga võitlema. Võitlus seisnes selles, et selles teostuses peate ikkagi valima palju suurema numbri timeseries_ids
andmete jaoks kui siis, kui pöördindeks on ajaliselt jaotatud.
Kuidas me selle probleemi lahendasime? Lahendasime selle originaalsel viisil – salvestades igasse ümberpööratud indeksikirjesse ühe identifikaatori asemel mitu aegrea identifikaatorit. See tähendab, et meil on võti label=value
, mis esineb igas aegreas. Ja nüüd salvestame mitu timeseries_ids
ühes sissekandes.
Siin on näide. Varem oli meil N kirjet, kuid nüüd on meil üks kirje, mille eesliide on sama, mis kõigil teistel. Eelmise kirje puhul sisaldab väärtus kõiki aegridade ID-sid.
See võimaldas sellise ümberpööratud indeksi skaneerimiskiirust suurendada kuni 10 korda. Ja see võimaldas meil vähendada vahemälu mälutarbimist, sest nüüd salvestame stringi label=value
ainult üks kord vahemälus koos N korda. Ja see rida võib olla suur, kui salvestate oma siltidesse ja siltidesse pikki ridu, mida Kubernetesele meeldib sinna lükata.
Teine võimalus pööratud indeksis otsimise kiirendamiseks on sharding. Mitme ümberpööratud indeksi loomine ühe asemel ja andmete jagamine nende vahel võtmega. See on komplekt key=value
aur. See tähendab, et saame mitu sõltumatut ümberpööratud indeksit, mida saame päringuid teha paralleelselt mitmel protsessoril. Varasemad teostused lubasid töötada ainult ühe protsessori režiimis, st andmete skannimist ainult ühes tuumas. See lahendus võimaldab skannida andmeid korraga mitmel tuumal, nagu ClickHouse’ile meeldib. Seda plaanime ellu viia.
Nüüd pöördume tagasi oma lammaste juurde – ristumisfunktsiooni juurde timeseries_ids
. Mõelgem, millised rakendused võivad olla. See funktsioon võimaldab teil leida timeseries_ids
antud komplekti jaoks label=value
.
Esimene võimalus on naiivne teostus. Kaks pesastatud silmust. Siit saame funktsiooni sisendi intersectInts
kaks viilu - a
и b
. Väljundis peaks see meile tagastama nende viilude ristumiskoha.
Naiivne teostus näeb välja selline. Kordame üle kõik lõigu väärtused a
, selle tsükli sees käime läbi kõik lõigu väärtused b
. Ja me võrdleme neid. Kui need klapivad, siis oleme ristmiku leidnud. Ja salvestage see sisse result
.
Millised on puudused? Ruutiline keerukus on selle peamine puudus. Näiteks kui teie mõõtmed on viil a
и b
miljon korraga, siis see funktsioon ei anna teile kunagi vastust. Sest see peab tegema triljonit iteratsiooni, mis on isegi tänapäevaste arvutite jaoks palju.
Teine teostus põhineb kaardil. Koostame kaardi. Panime sellele kaardile kõik lõigu väärtused a
. Seejärel läbime viilu eraldi silmuses b
. Ja me kontrollime, kas see väärtus pärineb viilust b
kaardil. Kui see on olemas, lisage see tulemusele.
Mis kasu sellest on? Eeliseks on see, et on ainult lineaarne keerukus. See tähendab, et funktsioon käivitub suuremate viilude puhul palju kiiremini. Miljoni suuruse lõigu puhul täidetakse seda funktsiooni 2 miljoni iteratsiooniga, erinevalt eelmise funktsiooni triljonist iteratsioonist.
Negatiivne külg on see, et see funktsioon nõuab selle kaardi loomiseks rohkem mälu.
Teiseks puuduseks on räsimise suured üldkulud. See puudus pole eriti ilmne. Ja meie jaoks polnud see ka eriti ilmne, nii et algul toimus VictoriaMetricsis ristmiku rakendamine kaardi kaudu. Siis aga näitas profileerimine, et põhiprotsessori aeg kulub kaardile kirjutamisele ja selle kaardi väärtuse olemasolu kontrollimisele.
Miks raisatakse nendes kohtades CPU aega? Kuna Go teostab nendel ridadel räsioperatsiooni. See tähendab, et see arvutab võtme räsi, et pääseda sellele HashMapi antud indeksis juurde. Räsi arvutamise toiming viiakse lõpule kümnete nanosekundite jooksul. VictoriaMetricsi jaoks on see aeglane.
Otsustasin rakendada spetsiaalselt selle juhtumi jaoks optimeeritud bitikomplekti. Selline näeb nüüd välja kahe viilu ristumiskoht. Siin loome bitikomplekti. Lisame sellele elemendid esimesest viilust. Seejärel kontrollime nende elementide olemasolu teises viilus. Ja lisage need tulemusele. See tähendab, et see ei erine peaaegu eelmisest näitest. Ainus asi on see, et asendasime juurdepääsu kaardile kohandatud funktsioonidega add
и has
.
Esmapilgul tundub, et see peaks aeglasemalt toimima, kui varem oli seal kasutatud standardkaarti ja siis kutsutakse välja mingid muud funktsioonid, aga profileerimine näitab, et see asi töötab VictoriaMetricsi puhul 10 korda kiiremini kui tavakaart.
Lisaks kasutab see kaardi rakendamisega võrreldes palju vähem mälu. Sest me salvestame siia kaheksabaidiste väärtuste asemel bitte.
Selle teostuse puuduseks on see, et see pole nii ilmne, mitte triviaalne.
Teine puudus, mida paljud ei pruugi märgata, on see, et see rakendus ei pruugi mõnel juhul hästi toimida. See tähendab, et see on optimeeritud konkreetse juhtumi jaoks, selle VictoriaMetricsi aegridade ID-de ristumisjuhtumi jaoks. See ei tähenda, et see sobib kõikidel juhtudel. Kui seda kasutatakse valesti, ei saa me jõudluse kasvu, vaid mälu tühjenemise ja jõudluse aeglustumise.
Mõelgem selle struktuuri rakendamisele. Kui soovite vaadata, asub see VictoriaMetricsi allikates kaustas timeseries_id
on 64-bitine väärtus, kus esimesed 32 bitti on põhimõtteliselt konstantsed ja ainult viimased 32 bitti muutuvad.
Seda andmestruktuuri ei salvestata kettale, see töötab ainult mälus.
Siin on selle API. See ei ole väga keeruline. API on kohandatud konkreetselt VictoriaMetricsi kasutamise konkreetsele näitele. See tähendab, et siin pole tarbetuid funktsioone. Siin on funktsioonid, mida VictoriaMetrics selgesõnaliselt kasutab.
Funktsioonid on olemas add
, mis lisab uusi väärtusi. Funktsioon on olemas has
, mis kontrollib uusi väärtusi. Ja seal on funktsioon del
, mis eemaldab väärtused. Seal on abifunktsioon len
, mis tagastab komplekti suuruse. Funktsioon clone
kloonid palju. Ja funktsioon appendto
teisendab selle komplekti lõiguks timeseries_ids
.
Selline näeb välja selle andmestruktuuri rakendamine. komplekt sisaldab kahte elementi:
-
ItemsCount
on abiväli, mis võimaldab kiiresti tagastada komplekti elementide arvu. Saaks ka ilma selle abiväljata hakkama, aga see tuli siia lisada, sest VictoriaMetrics küsib sageli oma algoritmides bitsetile pikkust. -
Teine väli on
buckets
. See on osa struktuuristbucket32
. Iga struktuur salvestabhi
valdkonnas. Need on ülemised 32 bitti. Ja kaks viilu -b16his
иbuckets
kohtabucket16
struktuurid.
Siin on salvestatud 16-bitise struktuuri teise osa 64 ülemist bitti. Ja siin salvestatakse bitikomplektid iga baidi alumise 16 biti jaoks.
Bucket64
koosneb massiivist uint64
. Pikkus arvutatakse nende konstantide abil. Ühes bucket16
maksimaalselt saab salvestada 2^16=65536
natuke. Kui jagate selle 8-ga, on see 8 kilobaiti. Kui jagate uuesti 8-ga, on see 1000 uint64
tähenduses. See on Bucket16
– see on meie 8-kilobaidine struktuur.
Vaatame, kuidas rakendatakse üht selle struktuuri meetodit uue väärtuse lisamiseks.
Kõik algab sellest uint64
tähendusi. Arvutame ülemised 32 bitti, arvutame alumise 32 bitti. Käime kõik läbi buckets
. Võrdleme iga ämbri 32 parimat bitti lisatava väärtusega. Ja kui need ühtivad, kutsume funktsiooni add
struktuuris b32 buckets
. Ja lisage sinna alumised 32 bitti. Ja kui see tagasi tuleb true
, siis see tähendab, et me lisasime sinna sellise väärtuse ja meil sellist väärtust ei olnud. Kui see naaseb false
, siis oli selline tähendus juba olemas. Seejärel suurendame elementide arvu struktuuris.
Kui me ei leidnud seda, mida vajate bucket
nõutava hi-väärtusega, siis kutsume funktsiooni välja addAlloc
, mis toodab uue bucket
, lisades selle kopa struktuuri.
See on funktsiooni rakendamine b32.add
. See sarnaneb eelmise rakendusega. Arvutame kõige olulisemad 16 bitti, kõige vähem olulised 16 bitti.
Seejärel läbime kõik ülemised 16 bitti. Leiame vasteid. Ja kui vaste on, kutsume lisamismeetodit, mida järgmisel lehel kaalume bucket16
.
Ja siin on madalaim tase, mida tuleks võimalikult palju optimeerida. Arvutame eest uint64
id väärtus slice bitis ja ka bitmask
. See on antud 64-bitise väärtuse mask, mida saab kasutada selle biti olemasolu kontrollimiseks või selle seadistamiseks. Kontrollime, kas see bitt on seatud, määrame selle ja tagastame kohaloleku. See on meie teostus, mis võimaldas meil kiirendada aegridade ristuvate ID-de tööd tavaliste kaartidega võrreldes 10 korda.
Lisaks sellele optimeerimisele on VictoriaMetricsil palju muid optimeerimisi. Enamik neist optimeerimistest lisati põhjusel, kuid pärast koodi tootmises profileerimist.
See on optimeerimise peamine reegel – ära lisa optimeerimist, eeldades, et siin on kitsaskoht, sest võib selguda, et kitsaskohta seal ei ole. Optimeerimine halvendab tavaliselt koodi kvaliteeti. Seetõttu tasub optimeerida alles pärast profileerimist ja soovitavalt tootmises, et tegemist oleks reaalsete andmetega. Kui kedagi huvitab, võib vaadata VictoriaMetricsi lähtekoodi ja uurida muid seal leiduvaid optimeerimisi.
Mul on küsimus bitseti kohta. Väga sarnane C++ vektori bool-rakendusega, optimeeritud bitikomplekt. Kas võtsid teostuse sealt?
Ei, mitte sealt. Selle bitikomplekti rakendamisel lähtusin teadmistest VictoriaMetricsis kasutatavate ID-aegridade struktuurist. Ja nende struktuur on selline, et ülemised 32 bitti on põhimõtteliselt konstantsed. Alumised 32 bitti võivad muutuda. Mida madalam on bitt, seda sagedamini võib see muutuda. Seetõttu on see rakendus spetsiaalselt selle andmestruktuuri jaoks optimeeritud. C++ teostus on minu teada optimeeritud üldjuhtumi jaoks. Kui optimeerite üldise juhtumi jaoks, tähendab see, et see ei ole konkreetse juhtumi jaoks kõige optimaalsem.
Samuti soovitan teil vaadata Aleksei Milovidi aruannet. Umbes kuu aega tagasi rääkis ta ClickHouse'is optimeerimisest konkreetsete erialade jaoks. Ta ütleb vaid, et üldiselt on C++ teostus või mõni muu teostus kohandatud haiglas keskmiselt hästi töötama. See võib toimida halvemini kui teadmistepõhine teostus, nagu meie oma, kus me teame, et 32 parimat bitti on enamasti konstantsed.
Mul on teine küsimus. Mis on põhimõtteline erinevus InfluxDB-st?
Põhimõttelisi erinevusi on palju. Jõudluse ja mälukulu osas näitab InfluxDB testides 10 korda rohkem mälukulu kõrge kardinaalsusega aegridade puhul, kui sul on neid palju, näiteks miljoneid. Näiteks VictoriaMetrics kulutab 1 GB miljoni aktiivse rea kohta, InfluxDB aga 10 GB. Ja see on suur erinevus.
Teine põhimõtteline erinevus on see, et InfluxDB-l on kummalised päringukeeled - Flux ja InfluxQL. Võrreldes aegridadega pole need eriti mugavad
Ja veel üks erinevus on see, et InfluxDB-l on veidi kummaline andmemudel, kus igale reale saab salvestada mitu välja erineva siltide komplektiga. Need read on omakorda jagatud erinevateks tabeliteks. Need täiendavad komplikatsioonid raskendavad edasist tööd selle andmebaasiga. Seda on raske toetada ja mõista.
VictoriaMetricsis on kõik palju lihtsam. Seal on iga aegrida võtmeväärtus. Väärtus on punktide kogum - (timestamp, value)
, ja võti on komplekt label=value
. Väljade ja mõõtmiste vahel ei ole vahet. See võimaldab teil valida mis tahes andmeid ja seejärel kombineerida, liita, lahutada, korrutada, jagada, erinevalt InfluxDB-st, kus erinevate ridade vahelisi arvutusi pole minu teada ikka veel rakendatud. Isegi kui need on rakendatud, on see keeruline, peate kirjutama palju koodi.
Mul on täpsustav küsimus. Kas ma sain õigesti aru, et seal on mingi probleem, millest sa rääkisid, et see ümberpööratud indeks ei mahu mällu, seega on seal partitsioonid?
Esiteks näitasin ümberpööratud indeksi naiivset rakendamist tavalisel Go kaardil. See teostus ei sobi andmebaaside jaoks, kuna seda ümberpööratud indeksit ei salvestata kettale ja andmebaas peab salvestama kettale, et need andmed jääksid taaskäivitamisel kättesaadavaks. Selle rakenduse korral kaob teie ümberpööratud indeks rakenduse taaskäivitamisel. Ja kaotate juurdepääsu kõikidele andmetele, kuna te ei leia neid.
Tere! Täname raporti eest! Minu nimi on Pavel. Olen pärit Wildberryst. Mul on teile paar küsimust. Küsimus üks. Kas arvate, et kui oleksite oma rakenduse arhitektuuri ülesehitamisel valinud teistsuguse põhimõtte ja jaotanud andmed aja jooksul, siis oleksite ehk saanud andmeid otsides ristuda, tuginedes ainult sellele, et üks partitsioon sisaldab andmeid ühe jaoks ajavahemik , st ühes ajavahemikus ja te ei peaks muretsema selle pärast, et teie tükid on erinevalt laiali? Küsimus number 2 - kuna rakendate sarnast algoritmi bitsetiga ja kõige muuga, siis võib-olla proovisite kasutada protsessori juhiseid? Võib-olla olete proovinud selliseid optimeerimisi?
Teisele vastan kohe. Me pole veel selleni jõudnud. Aga kui vaja, siis jõuame. Ja esimene, mis küsimus oli?
Arutasite kahte stsenaariumi. Ja nad ütlesid, et valisid teise keerulisema teostusega. Ja nad ei eelistanud esimest, kus andmed on aja järgi jaotatud.
Jah. Esimesel juhul oleks indeksi kogumaht suurem, sest igas partitsioonis peaksime salvestama dubleerivad andmed nende aegridade kohta, mis jätkuvad läbi kõigi nende partitsioonide. Ja kui teie aegridade churn rate on väike, st samu jadaid kasutatakse pidevalt, siis esimesel juhul kaotaksime hõivatud kettaruumis palju rohkem kui teisel juhul.
Ja nii – jah, aja jagamine on hea valik. Prometheus kasutab seda. Kuid Prometheusel on veel üks puudus. Nende andmete ühendamisel peab see säilitama kõigi siltide ja aegridade metateabe. Seega, kui sellega liidetavad andmetükid on suured, siis erinevalt VictoriaMetricsist suureneb liitmise ajal mälutarbimine väga palju. Ühendamisel ei tarbi VictoriaMetrics üldse mälu, kulub vaid paar kilobaiti, olenemata liidetavate andmetükkide suurusest.
Teie kasutatav algoritm kasutab mälu. See tähistab väärtusi sisaldavad ajaridade sildid. Ja nii kontrollite paaris olemasolu ühes ja teises andmemassiivis. Ja saate aru, kas ristumine toimus või mitte. Tavaliselt rakendavad andmebaasid kursoreid ja iteraatoreid, mis salvestavad nende praeguse sisu ja käitavad sorteeritud andmeid nende toimingute lihtsa keerukuse tõttu.
Miks me ei kasuta andmete läbimiseks kursoreid?
Jah.
Sorditud read salvestame LevelDB-sse või ühendamisse. Saame kursorit liigutada ja ristmiku leida. Miks me seda ei kasuta? Sest see on aeglane. Kuna kursorid tähendavad, et peate iga rea jaoks funktsiooni kutsuma. Funktsioonikutse pikkus on 5 nanosekundit. Ja kui teil on 100 000 000 rida, siis selgub, et me kulutame pool sekundit lihtsalt funktsiooni kutsumisele.
Selline asi on jah. Ja minu viimane küsimus. Küsimus võib tunduda veidi kummaline. Miks ei ole võimalik andmete saabumise hetkel kõiki vajalikke agregaate lugeda ja neid vajalikul kujul salvestada? Miks säästa mõnes süsteemis, nagu VictoriaMetrics, ClickHouse jne, tohutuid mahtusid ja seejärel kulutada neile palju aega?
Toon näite, et asi selgem oleks. Ütleme, kuidas väike mänguasja spidomeeter töötab? See salvestab teie läbitud vahemaa, lisades selle kogu aeg ühele väärtusele ja teisel korral aja. Ja jagab. Ja saavutab keskmise kiiruse. Saate teha umbes sama asja. Lisage kõik vajalikud faktid käigu pealt kokku.
Olgu, ma saan küsimusest aru. Sinu eeskujul on oma koht. Kui teate, milliseid agregaate vajate, on see parim rakendus. Kuid probleem on selles, et inimesed salvestavad need mõõdikud, mõned andmed ClickHouse'i ja nad ei tea veel, kuidas nad neid tulevikus koondavad ja filtreerivad, seega peavad nad salvestama kõik algandmed. Aga kui teate, et peate midagi keskmiselt arvutama, siis miks mitte seda arvutada, selle asemel, et salvestada sinna hulk toorväärtusi? Kuid see on ainult siis, kui teate täpselt, mida vajate.
Muide, aegridade salvestamise andmebaasid toetavad agregaatide loendamist. Näiteks Prometheus toetab
Näiteks oma eelmises töökohas oli mul vaja lugeda libisevas aknas viimase tunni jooksul toimunud sündmuste arvu. Probleem on selles, et pidin Go-s tegema kohandatud teostuse, st selle asja loendamise teenuse. See teenus ei olnud lõppkokkuvõttes triviaalne, sest seda on raske arvutada. Rakendamine võib olla lihtne, kui peate loendama teatud ajavahemike järel mõningaid agregaate. Kui soovite libisevas aknas sündmusi loendada, pole see nii lihtne, kui tundub. Ma arvan, et seda pole ClickHouse'is ega ajaridade andmebaasides veel rakendatud, kuna seda on keeruline rakendada.
Ja veel üks küsimus. Rääkisime just keskmistamisest ja mulle meenus, et kunagi oli selline asi nagu Carbon taustaprogrammiga Graphite. Ja ta teadis, kuidas vanu andmeid harvendada, st jätta üks punkt minutis, üks punkt tunnis jne. Põhimõtteliselt on see üsna mugav, kui vajame toorandmeid suhteliselt kuu aega ja kõik muu saab hõreneda . Kuid Prometheus ja VictoriaMetrics seda funktsiooni ei toeta. Kas seda on plaanis toetada? Kui ei, siis miks mitte?
Täname küsimuse eest. Meie kasutajad küsivad seda küsimust perioodiliselt. Nad küsivad, millal lisame alladiskreetimise toe. Siin on mitmeid probleeme. Esiteks saab iga kasutaja aru downsampling
midagi muud: keegi tahab saada suvalise punkti antud intervallil, keegi soovib maksimum-, miinimum-, keskmisi väärtusi. Kui paljud süsteemid kirjutavad andmeid teie andmebaasi, ei saa te seda kõike ühte liita. Võib juhtuda, et iga süsteem vajab erinevat hõrenemist. Ja seda on raske rakendada.
Ja teine asi on see, et VictoriaMetrics, nagu ka ClickHouse, on optimeeritud suure hulga toorandmetega töötamiseks, nii et kui teie süsteemis on palju tuumasid, suudab see vähem kui sekundiga kühveldada miljard rida. Aegridade punktide skaneerimine VictoriaMetricsis – 50 000 000 punkti sekundis tuuma kohta. Ja see jõudlus laieneb olemasolevatele tuumadele. See tähendab, et kui teil on näiteks 20 tuuma, siis skannite miljard punkti sekundis. Ja see VictoriaMetricsi ja ClickHouse'i omadus vähendab allasammutamise vajadust.
Veel üks omadus on see, et VictoriaMetrics tihendab need andmed tõhusalt. Tootmises on tihendus keskmiselt 0,4–0,8 baiti punkti kohta. Iga punkt on ajatempel + väärtus. Ja see tihendatakse keskmiselt vähem kui üheks baidiks.
Sergei. Mul on küsimus. Mis on minimaalne salvestusaja kvantum?
Üks millisekund. Meil oli hiljuti vestlus teiste aegridade andmebaasi arendajatega. Nende minimaalne ajalõik on üks sekund. Ja näiteks grafiidis on see ka üks sekund. OpenTSDB-s on see samuti üks sekund. InfluxDB on nanosekundi täpsusega. VictoriaMetricsis on see üks millisekund, kuna Prometheuses on see üks millisekund. Ja VictoriaMetrics töötati algselt välja Prometheuse kaugsalvestuseks. Kuid nüüd saab see salvestada andmeid teistest süsteemidest.
Inimene, kellega ma rääkisin, ütleb, et neil on sekundist sekundini täpsus – sellest piisab, sest see sõltub aegridade andmebaasi salvestatavate andmete tüübist. Kui tegemist on DevOpsi andmete või infrastruktuuri andmetega, kuhu kogute neid 30-sekundiliste intervallidega minutis, siis piisab sekundilisest täpsusest, vähem polegi vaja. Ja kui kogute neid andmeid kõrgsageduskaubandussüsteemidest, siis vajate nanosekundi täpsust.
VictoriaMetricsi millisekundiline täpsus sobib ka DevOpsi juhtumi jaoks ja võib sobida enamiku juhtumite jaoks, mida mainisin aruande alguses. Ainus, mille jaoks see ei pruugi sobida, on kõrgsageduslikud kauplemissüsteemid.
Aitäh! Ja veel üks küsimus. Mis on PromQL-i ühilduvus?
Täielik tagasiühilduvus. VictoriaMetrics toetab täielikult PromQL-i. Lisaks lisab see PromQL-i täiendavaid täiustatud funktsioone, mida nimetatakse
Telegrammi kanal
Küsitluses saavad osaleda ainult registreerunud kasutajad.
Mis takistab teil Prometheuse pikaajaliseks salvestusruumiks VictoriaMetricsile üle minemast? (Kirjutage kommentaaridesse, lisan selle küsitlusesse))
-
71,4%Ma ei kasuta Prometheus5
-
28,6%Ei teadnud VictoriaMetrics2-st
7 kasutajat hääletas. 12 kasutajat jäi erapooletuks.
Allikas: www.habr.com