Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Vi cunsigliu di leghje a trascrizione di u rapportu di a fine di 2019 da Alexander Valyalkin "Vai ottimisazioni in VictoriaMetrics"

VictoriaMetrics - un DBMS rapidu è scalabile per almacenà è trasfurmà e dati in a forma di una serie temporale (u registru forma u tempu è un inseme di valori currispundenu à questu tempu, per esempiu, ottenuti attraversu sondaggi periodichi di u statutu di sensori o cullezzione di metrica).

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Eccu un ligame à u video di stu rapportu - https://youtu.be/MZ5P21j_HLE

Diapositive

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Parlaci di sè stessu. Sò Alexander Valyalkin. Quì u mo contu GitHub. Sò appassiunatu di Go è ottimisazione di u rendiment. Aghju scrittu assai biblioteche utili è micca cusì utili. Accumincianu cù sia fast, o cun quick prefissu.

Sò attualmente travagliendu in VictoriaMetrics. Chì ci hè è chì ci facciu ? Parlaraghju di questu in questa presentazione.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

U schema di u rapportu hè u seguitu:

  • Prima, vi dicu ciò chì hè VictoriaMetrics.
  • Allora vi dicu chì serie di tempu sò.
  • Allora vi dicu cumu funziona una basa di dati di serie temporale.
  • In seguitu, vi dicu nantu à l'architettura di basa di dati: di ciò chì hè custituitu.
  • E poi andemu à l'ottimisazioni chì VictoriaMetrics hà. Questa hè una ottimisazione per l'indici invertitu è ​​una ottimisazione per l'implementazione di bitset in Go.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Qualchissia in l'audienza sapi ciò chì hè VictoriaMetrics? Wow, un saccu di ghjente digià sanu. Hè una bona nutizia. Per quelli chì ùn sanu micca, questu hè una basa di dati di serie di tempu. Hè basatu annantu à l'architettura ClickHouse, nantu à certi dettagli di l'implementazione ClickHouse. Per esempiu, nantu à cum'è: MergeTree, calculu parallelu nantu à tutti i nuclei di prucessori dispunibuli è ottimisazione di u rendiment travagliendu nantu à i blocchi di dati chì sò posti in u cache di u processatore.

VictoriaMetrics furnisce una cumpressione di dati megliu cà altre basa di dati di serie temporale.

Scala verticalmente - vale à dì, pudete aghjunghje più processori, più RAM in un computer. VictoriaMetrics utilizerà bè queste risorse dispunibili è migliurà a produtividade lineare.

VictoriaMetrics scala ancu orizzontalmente - vale à dì, pudete aghjunghje nodi supplementari à u cluster VictoriaMetrics, è u so rendiment aumenterà quasi linearmente.

Cum'è avete capitu, VictoriaMetrics hè una basa di dati veloce, perchè ùn possu micca scrive à l'altri. È hè scrittu in Go, cusì ne parlu in questu meetingup.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Quale sà ciò chì hè una serie di tempu? Sapi ancu assai persone. Una serie di tempu hè una seria di coppie (timestamp, значение), induve sti coppie sò ordinati per u tempu. U valore hè un numeru in virgule flottante - float64.

Ogni serie di tempu hè identificatu unicu da una chjave. In chì consiste sta chjave? Hè custituitu da un settore micca vacanti di coppie chjave-valore.

Eccu un esempiu di una serie di tempu. A chjave di sta serie hè una lista di coppie: __name__="cpu_usage" hè u nome di a metrica, instance="my-server" - questu hè l'urdinatore nantu à quale sta metrica hè recullata, datacenter="us-east" - questu hè u centru di dati induve si trova stu computer.

Avemu finitu cù un nome di serie temporale custituitu da trè coppie chjave-valore. Sta chjave currisponde à una lista di coppie (timestamp, value). t1, t3, t3, ..., tN - Quessi sò timestamps, 10, 20, 12, ..., 15 - i valori currispundenti. Questu hè l'usu di CPU à un tempu determinatu per una serie determinata.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Induve pò esse usatu a serie di tempu? Qualchissia hà una idea ?

  • In DevOps, pudete misurà CPU, RAM, rete, rps, numeru di errori, etc.
  • IoT - pudemu misurà a temperatura, a pressione, geocoordenate è qualcosa altru.
  • Ancu finanza - pudemu monitorà i prezzi per ogni tipu di stock è valute.
  • Inoltre, a serie di u tempu pò esse usata in u monitoraghju di i prucessi di produzzione in e fabbriche. Avemu utilizatori chì utilizanu VictoriaMetrics per monitorà e turbine di ventu, per i robots.
  • A serie di tempu sò ancu utili per a cullizzioni di l'infurmazioni da i sensori di parechji dispositi. Per esempiu, per un mutore; per a misurazione di a pressione di i pneumatici; per misurà a vitezza, a distanza; per a misurazione di u cunsumu di benzina, etc.
  • A serie temporale pò ancu esse usata per monitorà l'aviò. Ogni aviò hà una scatula nera chì recullà serie di tempu per parechji paràmetri di a salute di l'aeronave. A serie temporale hè ancu aduprata in l'industria aerospaziale.
  • L'assistenza sanitaria hè a pressione di sangue, u pulsu, etc.

Ci pò esse più appiicazioni chì aghju scurdatu, ma spergu chì capisce chì e serie di tempu sò attivamente aduprate in u mondu mudernu. È u voluminu di u so usu cresce ogni annu.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Perchè avete bisognu di una basa di dati di serie di tempu? Perchè ùn pudete micca aduprà una basa di dati relazionale regulare per almacenà serie di tempu?

Perchè a serie di tempu cuntene generalmente una grande quantità di informazioni, chì hè difficiule di almacenà è di processà in basa di dati cunvinziunali. Per quessa, apparsu basa di dati specializate per a serie di tempu. Queste basi immagazzinanu in modu efficace i punti (timestamp, value) cù a chjave datu. Forniscenu una API per leghje i dati almacenati per chjave, da una sola coppia chjave-valore, o da parechje coppie chjave-valore, o da regexp. Per esempiu, vo vulete truvà u CPU carica di tutti i vostri servizii in un centru di dati in America, allura vi tuccherà à aduprà sta pseudo-questione.

Di genere, e basa di dati di serie temporale furniscenu lingue di ricerca specializate perchè SQL di serie temporale ùn hè micca assai adattatu. Ancu s'ellu ci sò basa di dati chì supportanu SQL, ùn hè micca assai adattatu. Query lingue cum'è PromQL, InfluxQL, purtata, Q. Spergu chì qualchissia hà intesu almenu una di queste lingue. Parechje persone anu probabilmente intesu parlà di PromQL. Questa hè a lingua di dumanda Prometheus.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Hè ciò chì una architettura di basa di dati di serie di u tempu mudernu s'assumiglia cù VictoriaMetrics cum'è un esempiu.

Hè custituitu di dui parti. Questu hè u almacenamentu per l'indici invertitu è ​​u almacenamentu per i valori di serie temporale. Questi repositori sò separati.

Quandu un novu registru arriva in a basa di dati, accede prima à l'indici invertitu per truvà l'identificatore di a serie temporale per un determinatu set. label=value per una metrica data. Truvemu stu identificatore è salvemu u valore in a data store.

Quandu una dumanda vene per ricuperà dati da TSDB, andemu prima à l'indici invertitu. Pigliamu tuttu timeseries_ids records chì currispondenu à questu set label=value. E poi avemu tutti i dati nicissarii da u magazzinu di dati, indexed by timeseries_ids.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Fighjemu un esempiu di cumu una basa di dati di serie temporale processa una dumanda di selezzione entrante.

  • Prima di tuttu, ella riceve tuttu timeseries_ids da un indice invertitu chì cuntene i coppie datu label=value, o suddisfà una espressione regulare data.
  • Allora ripiglià tutti i punti di dati da l'almacenamiento di dati à un intervallu di tempu determinatu per quelli truvati timeseries_ids.
  • Dopu questu, a basa di dati faci qualchi calculi nantu à questi punti di dati, secondu a dumanda di l'utilizatori. È dopu chì torna a risposta.

In questa presentazione vi dicu di a prima parte. Questu hè una ricerca timeseries_ids par indice inversé. Pudete vede circa a seconda parte è a terza parte dopu Fonti VictoriaMetrics, o aspettate finu à chì preparu altri rapporti :)

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Passemu à l'indice inversu. Parechji pensanu chì questu hè simplice. Quale sà ciò chì hè un indice invertitu è ​​cumu funziona ? Oh, ùn ci hè più tanta ghjente. Pruvemu di capisce ciò chì hè.

Hè veramente simplice. Hè solu un dizziunariu chì mape una chjave à un valore. Cosa hè una chjave? Sta coppia label=valueinduve label и value - Quessi sò linii. È i valori sò un set timeseries_ids, chì include u paru datu label=value.

L'indice invertitu permette di truvà rapidamente tuttu timeseries_ids, chì anu datu label=value.

Hè ancu permette di truvà rapidamente timeseries_ids serie temporale per parechje coppie label=value, o per coppie label=regexp. Cumu succede questu? Per truvà l'intersezzione di u settore timeseries_ids per ogni paru label=value.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Fighjemu diverse implementazioni di l'indice invertitu. Cuminciamu cù l'implementazione ingenua più simplice. Ella pare cusì.

funziunava getMetricIDs riceve una lista di stringhe. Ogni linea cuntene label=value. Sta funzione torna una lista metricIDs.

Cumu funziona? Quì avemu una variabile globale chjamata invertedIndex. Questu hè un dizziunariu ordinariu (map), chì mapperà a stringa per slice ints. A linea cuntene label=value.

Implementazione di a funzione: get metricIDs per u primu label=value, allora andemu per tuttu u restu label=value, avemu capitu metricIDs per elli. È chjamate a funzione intersectInts, chì serà discutitu quì sottu. È sta funzione torna l'intersezzione di sti listi.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Comu pudete vede, implementà un indice invertitu ùn hè micca assai complicatu. Ma questu hè una implementazione ingenua. Chì svantaghji hà ? U principale svantaghju di l'implementazione ingenua hè chì un tali indice invertitu hè almacenatu in RAM. Dopu avè riavviatu l'applicazione, perdemu stu indice. Ùn ci hè micca salvamentu di questu indice à u discu. Un tali indice invertitu hè improbabile di esse adattatu per una basa di dati.

U sicondu inconveniente hè ancu ligatu à a memoria. L'indice invertitu deve esse inseritu in RAM. S'ellu supera a dimensione di RAM, allora ovviamente averemu - fora di l'errore di memoria. È u prugramma ùn hà micca travagliatu.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Stu prublema pò esse risolta cù suluzioni pronti cum'è Livellu DB, o RocksDB.

In corta, avemu bisognu di una basa di dati chì ci permette di fà trè operazioni rapidamente.

  • A prima operazione hè a registrazione ключ-значение à sta basa di dati. Ella face questu assai rapidamente, induve ключ-значение sò stringhe arbitrarie.
  • A seconda operazione hè una ricerca rapida per un valore cù una chjave data.
  • È a terza operazione hè una ricerca rapida per tutti i valori da un prefissu datu.

LevelDB è RocksDB - sti basa di dati sò stati sviluppati da Google è Facebook. Prima hè ghjuntu LevelDB. Allora i picciotti di Facebook anu pigliatu LevelDB è cuminciaru à migliurà, anu fattu RocksDB. Avà quasi tutte e basa di dati interni travaglianu in RocksDB in Facebook, cumprese quelli chì sò stati trasferiti à RocksDB è MySQL. L'anu chjamatu MyRocks.

Un indice inversu pò esse implementatu cù LevelDB. Cumu fà? Salvemu cum'è una chjave label=value. È u valore hè l'identificatore di a serie di tempu induve a coppia hè prisente label=value.

Se avemu parechje serie di tempu cù un paru datu label=value, allora ci saranu parechje fila in questa basa di dati cù a listessa chjave è diversa timeseries_ids. Per avè una lista di tutti timeseries_ids, chì cumincianu cù questu label=prefix, Facemu una scansione di gamma per quale sta basa di dati hè ottimizzata. Vale à dì, selezziunate tutte e linee chì cumincianu cù label=prefix è uttene u necessariu timeseries_ids.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Eccu un esempiu di implementazione di ciò chì parerebbe in Go. Avemu un indice inversu. Questu hè LevelDB.

A funzione hè a stessa cum'è per l'implementazione ingenua. Ripete l'implementazione ingenua quasi linea per linea. L'unicu puntu hè chì invece di vultà map avemu accede à l'indici invertitu. Avemu tutti i valori per u primu label=value. Allora andemu per tutti i pariglii restanti label=value è uttene u settore currispundente di metricIDs per elli. Allora truvamu l'intersezzione.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Tuttu pare esse bè, ma ci sò inconvenienti à sta suluzione. VictoriaMetrics hà inizialmente implementatu un indice invertitu basatu annantu à LevelDB. Ma à a fine aghju avutu a rinunzià.

Perchè? Perchè LevelDB hè più lento chè l'implementazione ingenua. In una implementazione ingenua, datu una chjave data, ricuperemu immediatamente a fetta sana metricIDs. Questa hè una operazione assai veloce - tutta a fetta hè pronta per l'usu.

In LevelDB, ogni volta chì una funzione hè chjamata GetValues avete bisognu di passà per tutte e linee chì cumincianu cù label=value. È uttene u valore per ogni linea timeseries_ids. Di tali timeseries_ids raccoglie una fetta di questi timeseries_ids. Ovviamente, questu hè assai più lento chè solu accede à una mappa regulare per chjave.

U sicondu inconveniente hè chì LevelDB hè scrittu in C. Calling C funzioni da Go ùn hè micca assai veloce. Ci vole centinaie di nanosecondi. Questu ùn hè micca assai veloce, perchè paragunatu à una chjama di funzione regulare scritta in go, chì piglia 1-5 nanosecondi, a diffarenza in u rendiment hè decine di volte. Per VictoriaMetrics era un difettu fatale :)

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Allora aghju scrittu a mo propria implementazione di l'indici invertitu. È l'hà chjamatu mergeset.

Mergeset hè basatu annantu à a struttura di dati MergeTree. Sta struttura di dati hè presa in prestu da ClickHouse. Ovviamente, mergeset deve esse ottimisatu per a ricerca rapida timeseries_ids secondu a chjave datu. Mergeset hè scrittu interamente in Go. Pudete vede Fonti VictoriaMetrics nantu à GitHub. L'implementazione di mergeset hè in u cartulare /lib/mergeset. Pudete pruvà à capisce ciò chì succede quì.

L'API mergeset hè assai simili à LevelDB è RocksDB. Vale à dì, vi permette di salvà rapidamente novi registri è selezziunate rapidamente i dischi da un prefissu datu.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Parleremu di i svantaghji di mergeset più tardi. Avà parlemu di quali prublemi sò ghjunti cù VictoriaMetrics in a pruduzzione quandu implementa un indice invertitu.

Perchè sò sorgiti ?

U primu mutivu hè l'alta rata di churn. Traduttu in russo, questu hè un cambiamentu frequente in a serie di tempu. Questu hè quandu una serie di tempu finisci è una nova serie principia, o parechje serie di u tempu cumincianu. È questu succede spessu.

U sicondu mutivu hè u gran numaru di serie di tempu. In u principiu, quandu u monitoraghju era guadagnatu pupularità, u numeru di serie di tempu era chjucu. Per esempiu, per ogni computer avete bisognu di monitorà CPU, memoria, rete è carica di discu. 4 serie di tempu per computer. Diciamu chì avete 100 computer è 400 serie di tempu. Questu hè assai pocu.

À u tempu, a ghjente hà capitu chì puderanu misurà infurmazione più granulare. Per esempiu, misurate a carica micca di tuttu u processatore, ma separatamente di ogni core di u processatore. Sì avete 40 core di processore, allora avete 40 volte più serie di tempu per misurà a carica di u processore.

Ma questu hè micca tuttu. Ogni core di processore pò avè parechji stati, cum'è idle, quandu hè inattivu. È ancu travaglià in u spaziu di l'utilizatori, travaglià in u spaziu kernel è altri stati. È ogni tali statu pò ancu esse misurata cum'è una serie di tempu separata. Questu aumenta ancu u numeru di fila da 7-8 volte.

Da una metrica avemu 40 x 8 = 320 metriche per un solu computer. Multiplicà per 100, avemu 32 000 invece di 400.

Allora Kubernetes hè ghjuntu. È hè peghju perchè Kubernetes pò accoglie parechji servizii diffirenti. Ogni serviziu in Kubernetes hè custituitu da parechji pods. È tuttu questu deve esse monitoratu. Inoltre, avemu una implementazione constante di novi versioni di i vostri servizii. Per ogni nova versione, deve esse creatu una nova serie temporale. In u risultatu, u numeru di serie di tempu cresce in modu esponenziale è avemu affruntatu u prublema di un gran numaru di serie di tempu, chì hè chjamatu high-cardinalità. VictoriaMetrics l'affronta cun successu cumparatu cù altre basa di dati di serie temporale.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Fighjemu un sguardu più vicinu à a rata di churn alta. Chì causa una alta rata di churn in a produzzione? Perchè certi significati di etichette è tag sò in constantemente cambiante.

Per esempiu, pigliate Kubernetes, chì hà u cuncettu deployment, vale à dì quandu una nova versione di a vostra applicazione hè sparata. Per una certa ragione, i sviluppatori di Kubernetes anu decisu di aghjunghje l'id di implementazione à l'etichetta.

Chì hà purtatu questu? Inoltre, cù ogni nova implementazione, tutte e vechji serie di tempu sò interrotte, è invece di elli, a nova serie di tempu principia cù un novu valore di l'etichetta. deployment_id. Ci ponu esse centinaie di millaie è ancu milioni di tali fila.

L'impurtante di tuttu questu hè chì u numeru tutale di serie di u tempu cresce, ma u numeru di serie di u tempu chì sò attualmente attivu è riceve dati ferma constante. Stu statu hè chjamatu high churn rate.

U prublema principali di u tassu di churn altu hè di assicurà una veloce di ricerca constante per tutte e serie di tempu per un determinatu settore di etichette in un certu intervallu di tempu. Di genere, questu hè l'intervallu di tempu per l'ultima ora o l'ultimu ghjornu.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Cumu risolve stu prublema? Eccu a prima opzione. Questu hè di dividisce l'indici invertitu in parti indipendenti cù u tempu. Vale à dì, qualchì intervallu di tempu passa, finiscemu di travaglià cù l'indice invertitu attuale. È creanu un novu indice inversu. Un altru intervallu di tempu passa, creamu un altru è un altru.

Et lorsqu'on échantillonne à partir de ces indices inversés, on trouve un ensemble d'indices inversés qui tombent dans l'intervalle donné. È, per quessa, selezziunate l'id di a serie di u tempu da quì.

Questu salva risorse perchè ùn avemu micca bisognu di guardà parti chì ùn sò micca in l'intervallu datu. Questu hè, di solitu, se selezziunà dati per l'ultima ora, allora per intervalli di tempu precedente saltemu e dumande.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Ci hè una altra opzione per risolve stu prublema. Questu hè per almacenà per ogni ghjornu una lista separata di l'ID di serie temporale chì hè accadutu in quellu ghjornu.

U vantaghju di sta suluzione nantu à a suluzione precedente hè chì ùn duplicemu micca l'infurmazioni di serie di tempu chì ùn sparisce micca cù u tempu. Sò constantemente presenti è ùn cambianu micca.

U svantaghju hè chì una tale suluzione hè più difficiuli di implementà è più difficiuli di debug. E VictoriaMetrics hà sceltu sta suluzione. Hè cusì chì hè accadutu storicamente. Sta suluzione funziona ancu bè paragunatu à a precedente. Perchè sta suluzione ùn hè stata implementata per u fattu chì deve duplicà e dati in ogni partizione per a serie di tempu chì ùn cambia micca, vale à dì chì ùn sparisce micca cù u tempu. VictoriaMetrics hè stata ottimizzata principalmente per u cunsumu di spaziu di discu, è l'implementazione precedente hà peghju u cunsumu di spaziu di discu. Ma sta implementazione hè megliu adattatu per minimizzà u cunsumu di spaziu di discu, cusì hè stata scelta.

Aviu avutu a cummattiri. A lotta era chì in questa implementazione avete sempre bisognu di sceglie un numeru assai più grande timeseries_ids per i dati cà quandu l'indici invertitu hè partitu in u tempu.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Cumu avemu risolviu stu prublema? L'avemu risoltu in modu originale - almacenendu parechji identificatori di serie temporali in ogni entrata d'indice invertitu invece di un identificatore. Questu hè, avemu una chjave label=value, chì si trova in ogni serie di tempu. È avà salvemu parechji timeseries_ids in una entrata.

Eccu un esempiu. Prima avemu avutu N entrate, ma avà avemu una voce chì u prefissu hè u listessu cum'è tutti l'altri. Per l'entrata precedente, u valore cuntene tutti l'ID di serie temporale.

Questu hà permessu di aumentà a velocità di scanning di un tali indice invertitu finu à 10 volte. È ci hà permessu di riduce u cunsumu di memoria per u cache, perchè avà guardamu a stringa label=value solu una volta in a cache inseme N volte. E sta linea pò esse grande se guardate longu fili in i vostri tags è etichette, chì Kubernetes li piace à spinghje quì.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Un'altra opzione per accelerà a ricerca nantu à un indice invertitu hè sharding. Crià parechji indici invertiti invece di unu è sharding data trà elli per chjave. Questu hè un set key=value vapore. Questu hè, avemu parechji indici invertiti indipendenti, chì pudemu interrogà in parallelu nantu à parechji processori. L'implementazioni precedenti permettenu solu l'operazione in modu di un processatore unicu, vale à dì, scanning data nantu à un solu core. Sta suluzione permette di scansà e dati nantu à parechji nuclei à una volta, cum'è ClickHouse piace à fà. Questu hè ciò chì pensamu di implementà.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Avà vultemu à i nostri pecuri - à a funzione di intersezzione timeseries_ids. Cunsideremu chì implementazioni ci ponu esse. Sta funzione vi permette di truvà timeseries_ids per un determinatu set label=value.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

A prima opzione hè una implementazione ingenua. Dui loops nidificati. Quì avemu u input di funzione intersectInts dui fette - a и b. À l'output, deve turnà à noi l'intersezzione di sti fette.

Una implementazione ingenua pare cusì. Iteremu nantu à tutti i valori da a fetta a, Dentru stu loop andemu attraversu tutti i valori di slice b. È li paragunemu. Se currispondenu, allora avemu trovu una intersezzione. È salvate in result.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Chì sò i disadvantages? A cumplessità quadratica hè u so inconveniente principale. Per esempiu, se e vostre dimensioni sò fette a и b un milione à tempu, allura sta funzione ùn vi mai turnà una risposta à voi. Perchè duverà fà un trilione di iterazioni, chì hè assai ancu per l'urdinatori muderni.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

A seconda implementazione hè basatu annantu à a mappa. Creemu mappa. Pudemu tutti i valori da a fetta in questa mappa a. Allora andemu per a fetta in un ciclu separatu b. È avemu verificatu se stu valore hè da a fetta b in mappa. S'ellu esiste, aghjunghje à u risultatu.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Chì sò i benefici? U vantaghju hè chì ci hè solu cumplessità lineari. Questu hè, a funzione eseguirà assai più veloce per fette più grande. Per una fetta di un milione, sta funzione eseguirà in 2 milioni di iterazioni, in uppusizione à i trilioni di iterazioni di a funzione precedente.

U svantaghju hè chì sta funzione richiede più memoria per creà sta mappa.

U sicondu svantaghju hè u grande overhead per l'hashing. Stu inconveniente ùn hè micca assai evidenti. È per noi ùn era micca assai ovvi, cusì à u principiu in VictoriaMetrics l'implementazione di l'intersezzione hè stata attraversu una mappa. Ma poi u prufilu dimustrava chì u tempu di u processatore principalu hè passatu à scrive à a mappa è cuntrollà a presenza di un valore in questa mappa.

Perchè u tempu di CPU hè persu in questi lochi? Perchè Go esegue una operazione di hashing in queste linee. Vale à dì, calcula l'hash di a chjave per accede dopu à un indici datu in u HashMap. L'operazione di calculu di hash hè cumpletata in decine di nanosecondi. Questu hè lento per VictoriaMetrics.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Aghju decisu di implementà un bitset ottimizzatu apposta per questu casu. Hè ciò chì l'intersezzione di dui fette avà pare. Quì avemu creatu un bitset. Aghjunghjemu elementi da a prima fetta. Allora cuntrollemu a prisenza di sti elementi in a seconda fetta. È aghjunghje à u risultatu. Questu hè, ùn hè quasi micca sfarente di l'esempiu precedente. L'unicu cosa quì hè chì avemu rimpiazzatu l'accessu à a mappa cù funzioni persunalizati add и has.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

À u primu sguardu, pare chì questu duverebbe travaglià più lento, se prima una mappa standard hè stata aduprata quì, è dopu alcune altre funzioni sò chjamate, ma u prufilu mostra chì sta cosa funziona 10 volte più veloce di a mappa standard in u casu di VictoriaMetrics.

Inoltre, usa assai menu memoria cumparatu cù l'implementazione di a mappa. Perchè guardemu bits quì invece di valori di ottu byte.

U svantaghju di sta implementazione hè chì ùn hè micca cusì evidenti, micca triviale.

Un altru svantaghju chì parechji ùn anu micca nutatu hè chì sta implementazione pò micca travaglià bè in certi casi. Vale à dì, hè ottimizatu per un casu specificu, per questu casu di intersezzione di l'ID di serie temporale VictoriaMetrics. Questu ùn significa micca chì hè adattatu per tutti i casi. S'ellu hè utilizatu in modu incorrectu, ùn averemu micca un aumentu di u rendiment, ma un errore di memoria è un rallentamentu di u rendiment.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Fighjemu l'implementazione di sta struttura. Sè vo vulete circà, si trova in i fonti VictoriaMetrics, in u cartulare lib/uint64set. Hè ottimizatu specificamente per u casu VictoriaMetrics, induve timeseries_id hè un valore di 64 bit, induve i primi 32 bit sò basicamente custanti è solu l'ultimi 32 bit cambianu.

Sta struttura di dati ùn hè micca guardatu nantu à u discu, opera solu in memoria.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Eccu u so API. Ùn hè micca assai cumplicatu. L'API hè adattatu specificamente à un esempiu specificu di usu VictoriaMetrics. Questu hè, ùn ci sò micca funzioni innecessarii quì. Eccu e funzioni chì sò esplicitamente utilizati da VictoriaMetrics.

Ci sò funzioni add, chì aghjunghje novi valori. Ci hè una funzione has, chì verifica per i novi valori. È ci hè una funzione del, chì elimina i valori. Ci hè una funzione helper len, chì torna a dimensione di u settore. Funzione clone clona assai. È funzione appendto converte stu set in fetta timeseries_ids.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Questu hè ciò chì l'implementazione di sta struttura di dati pare. set hà dui elementi:

  • ItemsCount hè un campu d'aiutu per rinvià rapidamente u numeru di elementi in un settore. Saria pussibile fà senza stu campu ausiliariu, ma duvia esse aghjuntu quì perchè VictoriaMetrics spessu interruga a lunghezza di bitset in i so algoritmi.

  • U sicondu campu hè buckets. Questu hè una fetta da a struttura bucket32. Ogni struttura magazzini hi campu. Quessi sò i 32 bits superiore. E duie fette - b16his и buckets из bucket16 strutture.

I primi 16 bits di a seconda parte di a struttura 64-bit sò almacenati quì. E quì i bitsets sò almacenati per i 16 bits più bassi di ogni byte.

Bucket64 custituitu da un array uint64. A durata hè calculata utilizendu sti custanti. In unu bucket16 massimu pò esse guardatu 2^16=65536 pocu. Se dividite questu per 8, allora hè 8 kilobytes. Se dividite per 8 di novu, hè 1000 uint64 significatu. Hè Bucket16 - Questa hè a nostra struttura di 8 kilobyte.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Fighjemu cumu hè implementatu unu di i metudi di sta struttura per aghjunghje un novu valore.

Tuttu principia cù uint64 significati. Calculemu i 32 bits superiori, calculemu i 32 bits più bassi. Andemu per tuttu buckets. Comparamu i primi 32 bits in ogni bucket cù u valore aghjuntu. È s'elli currispondenu, allora chjamemu a funzione add in struttura b32 buckets. È aghjunghje quì i 32 bits più bassi. È s'ellu tornò true, allora questu significa chì avemu aghjustatu un tali valore quì è ùn avemu micca un tali valore. S'ellu torna false, tandu un tali significatu esiste digià. Allora cresce u numeru di elementi in a struttura.

Se ùn avemu micca truvatu quellu chì avete bisognu bucket cù u hi-value nicissarii, tandu chjamemu a funzione addAlloc, chì pruducerà un novu bucket, aghjunghjendu à a struttura di bucket.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Questu hè l'implementazione di a funzione b32.add. Hè simile à l'implementazione precedente. Calculemu i 16 bits più significati, i 16 bits menu significativi.

Allora andemu per tutti i 16 bits superiore. Truvemu partiti. E s'ellu ci hè un match, chjamemu u metudu di aghjunghje, chì avemu da cunsiderà nantu à a pagina dopu bucket16.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

È quì hè u livellu più bassu, chì deve esse ottimizatu quantu pussibule. Calculemu per uint64 valore id in slice bit è ancu bitmask. Questa hè una maschera per un valore di 64 bit, chì pò esse usatu per verificà a presenza di stu bit, o stabilisce. Cuntrollamu per vede s'ellu hè stallatu questu bit è stabilisce, è torna a presenza. Questa hè a nostra implementazione, chì ci hà permessu di accelerà u funziunamentu di l'intersezione di l'ids di serie di u tempu da 10 volte in paragunà à e carte convenzionali.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

In più di questa ottimisazione, VictoriaMetrics hà parechje altre ottimisazioni. A maiò parte di sti ottimisazioni sò stati aghjuntu per una ragione, ma dopu à prufilu u codice in produzzione.

Questa hè a regula principale di l'ottimisazione - ùn aghjunghje micca l'ottimisazione assumendu chì ci sarà un collu di buttiglia quì, perchè pò esse chì ùn ci sarà micca un collu di buttiglia. L'optimizazione generalmente degrada a qualità di u codice. Per quessa, vale a pena ottimisà solu dopu à u prufilu è preferibile in a pruduzzione, perchè questu hè dati veri. Se qualchissia hè interessatu, pudete guardà u codice fonte VictoriaMetrics è scopre altre ottimisazioni chì ci sò.

Vai ottimisazioni in VictoriaMetrics. Alexander Valyalkin

Aghju una quistione nantu à u bitset. Assai simile à l'implementazione bool di vettore C++, bitset ottimizzatu. Avete pigliatu l'implementazione da quì?

Innò, micca da quì. Quandu l'implementazione di stu bitset, aghju guidatu da a cunniscenza di a struttura di sti serie temporali di ids, chì sò usati in VictoriaMetrics. È a so struttura hè tale chì i 32 bits superiori sò basicamente custanti. I 32 bits più bassi sò sottumessi à cambià. U più bassu u bit, u più spessu pò cambià. Per quessa, sta implementazione hè specificamente ottimizzata per sta struttura di dati. L'implementazione C ++, quant'è cunnoscu, hè ottimizzata per u casu generale. Se ottimisate per u casu generale, questu significa chì ùn serà micca u più ottimale per un casu specificu.

Vi cunsigliu ancu di vede u rapportu di Alexey Milovid. Circa un mese fa, hà parlatu di l'ottimisazione in ClickHouse per spicializazioni specifiche. Dice solu chì in u casu generale, una implementazione C++ o qualchì altra implementazione hè adattata per travaglià bè in media in un hospitalu. Puderà esse peggiu di una implementazione specifica di cunniscenza cum'è a nostra, induve sapemu chì i 32 bits superiori sò soprattuttu custanti.

Aghju una seconda dumanda. Chì ghjè a diferenza fundamentale da InfluxDB?

Ci sò parechje differenzi fundamentali. In quantu à u rendiment è u cunsumu di memoria, InfluxDB in teste mostra 10 volte più cunsumu di memoria per a serie di tempu di alta cardinalità, quandu avete assai di elli, per esempiu, milioni. Per esempiu, VictoriaMetrics cunsuma 1 GB per milione di fila attivu, mentri InfluxDB cunsuma 10 GB. È questu hè una grande diferenza.

A seconda diferenza fundamentale hè chì InfluxDB hà strane lingue di dumanda - Flux è InfluxQL. Ùn sò micca assai convenienti per travaglià cù serie di tempu cumparatu PromQL, chì hè sustinutu da VictoriaMetrics. PromQL hè una lingua di dumanda di Prometheus.

È una più diffarenza hè chì InfluxDB hà un mudellu di dati pocu stranu, induve ogni linea pò almacenà parechji campi cù un inseme differenti di tag. Queste linee sò più divisu in parechje tavule. Queste complicazioni supplementari complicanu u travagliu dopu cù sta basa di dati. Hè difficiule di sustene è capisce.

In VictoriaMetrics tuttu hè assai più simplice. Quì, ogni serie di tempu hè una chjave-valore. U valore hè un inseme di punti - (timestamp, value), è a chjave hè u set label=value. Ùn ci hè micca una separazione trà campi è misure. Permette di selezziunà qualsiasi dati è poi cumminà, aghjunghje, sottrae, multiplicà, dividite, à u cuntrariu di InfluxDB induve i calculi trà e diverse fila ùn sò ancu implementati per quantu a so. Ancu s'ellu sò implementati, hè difficiule, avete da scrive assai codice.

Aghju una quistione di clarificazione. Aghju capitu bè chì ci era qualchì tipu di prublema chì avete parlatu, chì questu indice invertitu ùn si mette micca in memoria, dunque ci hè particione quì?

Prima, aghju dimustratu una implementazione ingenua di un indice invertitu nantu à una mappa Go standard. Questa implementazione ùn hè micca adattata per e basa di dati perchè questu indice invertitu ùn hè micca salvatu à u discu, è a basa di dati deve esse salvata à u discu in modu chì sta dati resta dispunibule nantu à u riavviu. In questa implementazione, quandu riavviate l'applicazione, u vostru indice invertitu sparirà. È vi perderà l 'accessu à tutti i dati perchè ùn sarà capaci à truvà lu.

Bonghjornu! Grazie per u rapportu! Mi chjamu Pavel. Sò di Wildberries. Aghju uni pochi di dumande per voi. Una dumanda. Pensate chì s'è vo avete sceltu un principiu sfarente quandu custruite l'architettura di a vostra applicazione è particione i dati in u tempu, allora forse avaristi pussutu intersecà i dati quandu cercate, basatu solu nantu à u fattu chì una partizione cuntene dati per unu. piriudu di tempu, vale à dì, in un intervallu di tempu è ùn avete micca da preoccupari di u fattu chì i vostri pezzi sò spargugliati in modu diversu? Quistione numeru 2 - postu chì implementate un algoritmu simili cù bitset è tuttu u restu, allora forse avete pruvatu à utilizà l'istruzzioni di u processatore? Forse avete pruvatu tali ottimisazioni?

Rispondu subitu à a seconda. Ùn avemu micca ghjuntu à quellu puntu. Ma s'ellu hè necessariu, ci ghjunghjeremu. È u primu, chì era a quistione ?

Avete discututu dui scenarii. E anu dettu chì anu sceltu u sicondu cù una implementazione più cumplessa. È ùn anu micca preferitu u primu, induve i dati sò spartuti da u tempu.

Iè. In u primu casu, u voluminu tutale di l'indici seria più grande, perchè in ogni partizione averemu da almacenà dati duplicati per quelli serie di tempu chì cuntinueghjanu attraversu tutte queste partizioni. È se a vostra rata di churn di a serie di u tempu hè chjuca, vale à dì chì a listessa serie hè constantemente utilizata, allora in u primu casu, perdemu assai più in a quantità di spaziu di discu occupata cumparatu cù u sicondu casu.

È cusì - iè, a partizione di u tempu hè una bona opzione. Prometheus l'utilice. Ma Prometheus hà un altru inconveniente. Quandu si fusione sti pezzi di dati, ci vole à mantene in memoria meta infurmazione per tutte e etichette è serie temporali. Dunque, se i pezzi di dati chì si fusione sò grande, u cunsumu di memoria aumenta assai durante a fusione, à u cuntrariu di VictoriaMetrics. Quandu si fusiona, VictoriaMetrics ùn cunsuma micca memoria; solu un paru di kilobyte sò cunsumati, indipendentemente da a dimensione di i pezzi di dati uniti.

L'algoritmu chì vo aduprate usa memoria. Marca i tag di serie temporali chì cuntenenu valori. È in questu modu verificate a presenza accoppiata in un array di dati è in un altru. È capisci se l'intersezzione hè accaduta o micca. Di genere, e basa di dati implementanu cursori è iteratori chì almacenanu u so cuntenutu attuale è scorri à traversu i dati ordinati per via di a cumplessità simplice di queste operazioni.

Perchè ùn avemu micca aduprà i cursori per traversà i dati?

Sì.

Almacenemu e file ordinate in LevelDB o mergeset. Pudemu move u cursore è truvà l'intersezzione. Perchè ùn avemu micca aduprà? Perchè hè lentu. Perchè i cursori significanu chì avete bisognu di chjamà una funzione per ogni linea. Una chjama di funzione hè 5 nanosecondi. È s'è vo avete 100 linii, allura si gira fora chì avemu passatu a meza siconda solu chjamà a funzione.

Ci hè una cosa cusì, sì. È a mo ultima dumanda. A quistione pò sona un pocu strana. Perchè ùn hè micca pussibule di leghje tutti l'agregati necessarii à u mumentu chì i dati ghjunghjenu è salvà in a forma necessaria? Perchè salvà volumi enormi in certi sistemi cum'è VictoriaMetrics, ClickHouse, etc., è poi passanu assai tempu nantu à elli?

Daraghju un esempiu per fà più chjaru. Diciamu cumu funziona un picculu tachimetru di ghjoculu? Registra a distanza chì avete viaghjatu, tuttu u tempu aghjunghjendu à un valore, è u sicondu - tempu. E divide. È ottene una velocità media. Pudete fà circa a stessa cosa. Aghjunghjite tutti i fatti necessarii nantu à a mosca.

Va bè, capiscu a quistione. U vostru esempiu hà u so postu. Se sapete ciò chì aggregati avete bisognu, allora questu hè a megliu implementazione. Ma u prublema hè chì e persone salvanu queste metriche, qualchi dati in ClickHouse è ùn sanu micca ancu cumu si aggreganu è filtrà in u futuru, perchè anu da salvà tutti i dati crudi. Ma se sapete chì avete bisognu di calculà qualcosa in media, allora perchè micca calculà invece di almacenà una mansa di valori crudi quì? Ma questu hè solu se sapete esattamente ciò chì avete bisognu.

Per via, e basa di dati per l'almacenamiento di serie temporali supportanu a cuntazione di l'agregati. Per esempiu, Prometheus sustene regule di registrazione. Questu hè, questu pò esse fattu se sapete chì unità avete bisognu. VictoriaMetrics ùn hà micca ancu questu, ma hè generalmente precedutu da Prometheus, in quale questu pò esse fattu in e regule di ricudificazione.

Per esempiu, in u mo travagliu precedente, avia bisognu di cuntà u numeru di avvenimenti in una finestra scorrevule annantu à l'ultima ora. U prublema hè chì aghju avutu à fà una implementazione persunalizata in Go, vale à dì un serviziu per cuntà sta cosa. Stu serviziu era ultimamente micca trivial, perchè hè difficiule di calculà. L'implementazione pò esse simplice se avete bisognu di cuntà qualchi aggregati à intervalli di tempu fissi. Se vulete cuntà l'avvenimenti in una finestra scorrevule, ùn hè micca cusì simplice quantu pare. Pensu chì questu ùn hè micca statu ancu implementatu in ClickHouse o in basa di dati di serie temporale, perchè hè difficiule di implementà.

È una altra dumanda. Avemu parlatu solu di a media, è aghju ricurdatu chì ci era una volta una cosa cum'è Grafite cù un backend di Carbon. È hà sappiutu per diluisce i dati antichi, vale à dì, lascià un puntu per minutu, un puntu per ora, etc. In principiu, questu hè abbastanza còmuda si avemu bisognu di dati crudi, relativamente parlante, per un mesi, è tuttu u restu pò. esse diluita. Ma Prometheus è VictoriaMetrics ùn sustene micca sta funziunalità. Hè previstu di sustene? Se no, perchè micca?

Grazie per a quistione. I nostri utilizatori ponenu sta quistione periodicamente. Ci dumandanu quandu aghjunghjemu u supportu per u downsampling. Ci sò parechji prublemi quì. Prima, ogni utilizatore capisce downsampling qualcosa di sfarente: qualcunu vole ottene ogni puntu arbitrariu nantu à un intervallu datu, qualchissia vole valori massimi, minimi, medii. Se parechji sistemi scrivenu dati à a vostra basa di dati, allora ùn pudete micca cullà tutti inseme. Pò esse chì ogni sistema necessita di diluzioni differenti. È questu hè difficiule di implementà.

È a seconda cosa hè chì VictoriaMetrics, cum'è ClickHouse, hè ottimizatu per travaglià nantu à grande quantità di dati crudi, perchè pò scaccià un miliardo di linee in menu di una seconda si avete assai core in u vostru sistema. Scanning time series points in VictoriaMetrics - 50 punti per seconda per core. E sta prestazione scala à i core esistenti. Questu hè, sè vo avete 000 nuclei, per esempiu, scansate un miliardo di punti per seconda. È sta pruprietà di VictoriaMetrics è ClickHouse riduce a necessità di downsamling.

Un'altra funzione hè chì VictoriaMetrics comprime in modu efficace sta dati. A cumpressione media in a produzzione hè da 0,4 à 0,8 bytes per puntu. Ogni puntu hè un timestamp + valore. È hè cumpressu in menu di un byte in media.

Sergey. Aghju una quistione. Chì ghjè u quantu di tempu minimu di registrazione?

Un millisecondu. Recentemente avemu avutu una conversazione cù altri sviluppatori di basa di dati di serie temporale. U so tempu minimu slice hè un secondu. È in Graphite, per esempiu, hè ancu un secondu. In OpenTSDB hè ancu un secondu. InfluxDB hà una precisione nanoseconda. In VictoriaMetrics hè un millisecondu, perchè in Prometheus hè un millisecondu. E VictoriaMetrics hè stata urigginariamente sviluppata cum'è almacenamiento remoto per Prometheus. Ma avà pò salvà dati da altri sistemi.

A persona ch'e aghju parlatu dice chì anu una precisione di seconda à seconda - hè abbastanza per elli perchè dipende da u tipu di dati chì sò stati almacenati in a basa di dati di a serie temporale. S'ellu si tratta di dati DevOps o di dati da l'infrastruttura, induve a cullettate à intervalli di 30 seconde, per minutu, allora a seconda precisione hè abbastanza, ùn avete micca bisognu di menu. È s'è vo cullate sta dati da i sistemi di cummerciu d'alta frequenza, allora avete bisognu di precisione in nanosecondi.

A precisione di millisecondu in VictoriaMetrics hè ancu adattatu per u casu DevOps, è pò esse adattatu per a maiò parte di i casi chì aghju citatu à u principiu di u rapportu. L'unicu ciò chì pò esse micca adattatu hè i sistemi di cummerciale d'alta frequenza.

Grazie! È una altra quistione. Cosa hè a cumpatibilità in PromQL?

Piena cumpatibilità retrocede. VictoriaMetrics supporta pienamente PromQL. Inoltre, aghjusta funziunalità avanzata supplementu in PromQL, chì hè chjamatu MetricsQL. Ci hè una discussione in YouTube nantu à sta funziunalità allargata. Aghju parlatu à u Monitoring Meetup in a primavera in San Petruburgu.

Canale Telegram VictoriaMetrics.

Solu l'utilizatori registrati ponu participà à l'indagine. Firmà lu, per piacè.

Cosa vi impedisce di passà à VictoriaMetrics cum'è u vostru almacenamentu à longu andà per Prometheus? (Scrivi in ​​i cumenti, aghju aghjunghje à u sondaghju))

  • 71,4%Ùn aghju micca aduprà Prometheus5

  • 28,6%Ùn sapia micca di VictoriaMetrics2

7 utilizatori anu vutatu. 12 utilizatori si sò astenuti.

Source: www.habr.com

Add a comment