{"id":80733,"date":"2020-05-08T13:42:31","date_gmt":"2020-05-08T11:42:31","guid":{"rendered":"https:\/\/prohoster.info\/blog\/administrirovanie\/go-optimizations-in-victoriametrics-aleksandr-valyalkin"},"modified":"2020-05-08T13:42:31","modified_gmt":"2020-05-08T11:42:31","slug":"go-optimizations-in-victoriametrics-aleksandr-valyalkin","status":"publish","type":"post","link":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/go-optimizations-in-victoriametrics-aleksandr-valyalkin","title":{"rendered":"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><strong>Vi invitiamo a prendere visione della trascrizione della relazione di fine 2019 di Alexander Valialkin \"Ottimizzazioni Go in VictoriaMetrics\"<\/strong><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/victoriametrics.com\/\">VictoriaMetrics<\/a><\/noindex> \u2013 un database rapido e scalabile per la memorizzazione e l'elaborazione di dati in forma di serie temporali (una registrazione forma il tempo e un insieme di valori corrispondenti a quel tempo, ad esempio, ottenuti attraverso il monitoraggio periodico dello stato dei sensori o la raccolta di metriche).<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/dd13ec10594aeb138be29ce439cc476a.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<p>Ecco il link al video di questa presentazione \u2013 <noindex><a rel=\"nofollow\" href=\"https:\/\/youtu.be\/MZ5P21j_HLE\">https:\/\/youtu.be\/MZ5P21j_HLE<\/a><\/noindex><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/docs.google.com\/presentation\/d\/1k7OjHvxTHA7669MFwsNTCx8hII-a8lNvpmQetLxmrEU\/edit?usp=sharing\">Diapositive<\/a><\/noindex><\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/0073390d6dbcc6ab3e5305907ac6a229.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Vi racconter\u00f2 qualcosa di me. Sono Aleksandr Valyalkin. Ecco <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/valyala\">il mio account GitHub<\/a><\/noindex>. Mi appassiono di Go e ottimizzazione delle prestazioni. Ho scritto molte librerie utili e non. Iniziano tutte con <code>fast<\/code>, o con <code>quick<\/code> un prefisso. <\/p>\n<p><\/p>\n<p>Attualmente sto lavorando su VictoriaMetrics. Che cos'\u00e8 e cosa ci faccio? Di questo parler\u00f2 in questa presentazione. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/c9194bc3fee1f615d838979bec12f7d0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Il piano della presentazione \u00e8 il seguente:<\/p>\n<p><\/p>\n<ul>\n<li>Inizier\u00f2 spiegandovi cos'\u00e8 VictoriaMetrics. <\/li>\n<li>Poi parler\u00f2 delle serie temporali. <\/li>\n<li>Quindi spiegher\u00f2 come funziona un database per serie temporali.<\/li>\n<li>Successivamente parler\u00f2 dell'architettura del database: di cosa \u00e8 composta.<\/li>\n<li>E infine passeremo alle ottimizzazioni presenti in VictoriaMetrics. Queste includono l'ottimizzazione dell'indice invertito e l'ottimizzazione per l'implementazione di bitset in Go.<\/li>\n<\/ul>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/3e0244b6b6fe952f660e4a094778921a.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Qualcuno in sala sa cos'\u00e8 VictoriaMetrics? Wow, gi\u00e0 molte persone lo sanno. Questa \u00e8 una buona notizia. Per chi non lo sa \u2013 \u00e8 un database per serie temporali. \u00c8 basato sull'architettura di ClickHouse, su alcuni dettagli di implementazione di ClickHouse. Ad esempio, su cose come: MergeTree, computazione parallela su tutti i nuclei della CPU disponibili e ottimizzazione delle prestazioni tramite l'elaborazione di blocchi di dati che vengono messi in cache dalla CPU. <\/p>\n<p><\/p>\n<p>VictoriaMetrics offre la migliore compressione dei dati rispetto ad altri database per serie temporali. <\/p>\n<p><\/p>\n<p>Si scala verticalmente \u2013 cio\u00e8 puoi aggiungere pi\u00f9 processori, pi\u00f9 memoria RAM su un singolo computer. VictoriaMetrics utilizzer\u00e0 con successo queste risorse disponibili e aumenter\u00e0 le prestazioni lineari.<\/p>\n<p><\/p>\n<p>Inoltre, VictoriaMetrics si espande orizzontalmente \u2013 cio\u00e8 puoi aggiungere nodi aggiuntivi nel cluster di VictoriaMetrics e le sue prestazioni cresceranno quasi linearmente.<\/p>\n<p><\/p>\n<p>Come avete indovinato, VictoriaMetrics \u00e8 un database veloce, perch\u00e9 non posso scrivere di altri. E poich\u00e9 \u00e8 scritto in Go, ne parlo in questo meetup.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/0121c9baead722eb1fe22fb6f900d4ad.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Chi sa cos'\u00e8 una serie temporale? Anche qui molte persone lo sanno. Una serie temporale \u00e8 una serie di coppie <code>(timestamp, valore)<\/code>, dove queste coppie sono ordinate nel tempo. Il valore rappresenta un numero in virgola mobile \u2013 float64.<\/p>\n<p><\/p>\n<p>Ogni serie temporale \u00e8 identifcata in modo univoco da una chiave. Da cosa \u00e8 composta questa chiave? \u00c8 composta da un insieme non vuoto di coppie chiave-valore. <\/p>\n<p><\/p>\n<p>Ecco un esempio di serie temporale. La chiave di questa serie \u00e8 un elenco di coppie: <code>__name__=\"cpu_usage\"<\/code> \u2013 \u00e8 il nome della metrica, <code>instance=\"my-server\"<\/code> \u2013 \u00e8 il computer su cui \u00e8 stata raccolta questa metrica, <code>datacenter=\"us-east\"<\/code> \u2013 \u00e8 il data center dove si trova questo computer.<\/p>\n<p><\/p>\n<p>Abbiamo ottenuto il nome di una serie temporale composto da tre coppie chiave-valore. A questa chiave corrisponde un elenco di coppie <code>(timestamp, value)<\/code>. <code>t1, t3, t3, ..., tN<\/code> \u2013 questi sono gli timestamps, <code>10, 20, 12, ..., 15<\/code> \u2013 i valori corrispondenti. Questo \u00e8 l'uso della CPU in questo momento per questa serie.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/76cab9db0263318d355ac607ceb7e0f6.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Dove possono essere utilizzate le serie temporali? Qualcuno ha idee? <\/p>\n<p><\/p>\n<ul>\n<li>In DevOps si possono misurare i carichi di lavoro della CPU, RAM, rete, rps, numero di errori, ecc. <\/li>\n<li>IoT \u2013 possiamo misurare temperatura, pressione, coordinate geografiche e altro ancora.<\/li>\n<li>Anche in finanza \u2013 possiamo monitorare i prezzi delle azioni e delle valute. <\/li>\n<li>Inoltre, le serie temporali possono essere utilizzate per monitorare i processi produttivi nelle fabbriche. Abbiamo utenti che utilizzano VictoriaMetrics per monitorare le turbine eoliche, per i robot.<\/li>\n<li>Inoltre, le serie temporali sono utili per raccogliere informazioni da vari sensori. Ad esempio, per il motore; per misurare la pressione nei pneumatici; per misurare velocit\u00e0, distanza; per misurare il consumo di benzina ecc.<\/li>\n<li>Le serie temporali possono anche essere usate per monitorare gli aerei. In ogni aereo c'\u00e8 una scatola nera che raccoglie serie temporali su vari parametri di stato dell'aeromobile. Le serie temporali sono utilizzate anche nell'industria aerospaziale. <\/li>\n<li>Sanit\u00e0 \u2013 si tratta di pressione sanguigna, polso ecc.<\/li>\n<\/ul>\n<p><\/p>\n<p>Forse ci sono altre applicazioni che ho dimenticato, ma spero che abbiate capito che le serie temporali sono ampiamente utilizzate nel mondo moderno. E il volume del loro utilizzo cresce di anno in anno.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/f1da29d372163751268b6a8f86836d04.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>A cosa serve un database per serie temporali? Perch\u00e9 non utilizzare un normale database relazionale per memorizzare le serie temporali?<\/p>\n<p><\/p>\n<p>Perch\u00e9 nei dati temporali di solito c'\u00e8 una grande quantit\u00e0 di informazioni, che \u00e8 difficile da memorizzare e gestire in normali database. Cos\u00ec sono nate le DB specializzate per i dati temporali. Questi database conservano efficacemente i punti <code>(timestamp, value)<\/code> con una chiave specifica. Forniscono API per leggere i dati memorizzati per chiave, per una coppia chiave-valore, o per pi\u00f9 coppie, oppure tramite regexp. Ad esempio, se desideri trovare il carico della CPU di tutti i tuoi servizi nel data center in America, devi utilizzare una query pseudo come questa.<\/p>\n<p><\/p>\n<p>In genere, i database per dati temporali presentano linguaggi di query specializzati, poich\u00e9 SQL non \u00e8 molto adatto per i dati temporali. Sebbene ci siano database che supportano SQL, non \u00e8 molto efficace. Linguaggi di query come <noindex><a rel=\"nofollow\" href=\"https:\/\/medium.com\/@valyala\/promql-tutorial-for-beginners-9ab455142085\">PromQL<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.influxdata.com\/influxdb\/v1.8\/query_language\/spec\/\">InfluxQL<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/www.influxdata.com\/products\/flux\/\">Flux<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/code.kx.com\/q\/\">Q<\/a><\/noindex>. Spero che qualcuno abbia sentito parlare almeno di uno di questi linguaggi. Probabilmente molti conoscono PromQL. Questo \u00e8 il linguaggio di query di Prometheus.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/59e5b6d82a2f7861077bc5fe34519ee4.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Ecco come appare l'architettura di un moderno database per dati temporali, prendendo come esempio VictoriaMetrics.<\/p>\n<p><\/p>\n<p>\u00c8 composto da due parti. C'\u00e8 il repository per l'indice invertito e il repository per i valori dei dati temporali. Questi repository sono separati. <\/p>\n<p><\/p>\n<p>Quando arriva un nuovo record nel database, prima consultiamo l'indice invertito per trovare l'identificatore del dato temporale per il set specificato di <code>label=value<\/code> per questa metrica. Troviamo questo identificatore e memorizziamo il valore nel repository dei dati.<\/p>\n<p><\/p>\n<p>Quando arriva una richiesta di campionamento dei dati dal TSDB, prima di tutto consultiamo l'indice invertito. Estrarre tutti i <code>timeseries_ids<\/code> i record che corrispondono a questo insieme. <code>label=value<\/code>Quindi estraendo tutti i dati necessari dal repository dei dati, indicizzati da <code>timeseries_ids<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/eb9b36fa3c6de2188b97b56ad3839b2c.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Consideriamo un esempio di come un database per dati temporali gestisce una query di selezione in ingresso.<\/p>\n<p><\/p>\n<ul>\n<li>Innanzitutto, estrae tutti i <code>timeseries_ids<\/code> dall'indice invertito che contengono le coppie specificate <code>label=value<\/code>, oppure soddisfano l'espressione regolare specificata.<\/li>\n<li>Successivamente estrae tutti i data points dal repository dei dati su un intervallo di tempo specificato per i trovati <code>timeseries_ids<\/code>.<\/li>\n<li>Dopo di ci\u00f2, il database esegue alcune elaborazioni su questi data points secondo la richiesta dell'utente e poi restituisce la risposta.<\/li>\n<\/ul>\n<p><\/p>\n<p>In questa presentazione, vi parler\u00f2 della prima parte, ovvero la ricerca <code>timeseries_ids<\/code> nell'indice invertito. Per la seconda e terza parte, potete poi controllare <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/VictoriaMetrics\/VictoriaMetrics\">il codice sorgente di VictoriaMetrics<\/a><\/noindex>, oppure aspettare che prepari altre presentazioni \ud83d\ude42<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/3a7dab707dd1defb9bc674a342052a03.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Iniziamo con l'indice invertito. Molti potrebbero pensare che sia semplice. Chi sa cos'\u00e8 un indice invertito e come funziona? Oh, non ci sono cos\u00ec tante persone. Cerchiamo di capire di cosa si tratta. <\/p>\n<p><\/p>\n<p>In realt\u00e0 \u00e8 tutto semplice. \u00c8 semplicemente un dizionario che mappa una chiave a un valore. Cosa intendiamo per chiave? Questa coppia <code>label=value<\/code>, dove <code>etichetta<\/code> e <code>value<\/code> \u2013 sono delle stringhe. E i valori \u2013 sono un insieme <code>timeseries_ids<\/code>, che include la coppia specificata. <code>label=value<\/code>.<\/p>\n<p><\/p>\n<p>L'indice invertito consente di trovare rapidamente tutti i <code>timeseries_ids<\/code>, privilegiando i <code>label=value<\/code>.<\/p>\n<p><\/p>\n<p>E permette anche di trovare rapidamente <code>timeseries_ids<\/code> dati temporali per pi\u00f9 coppie <code>label=value<\/code>, oppure per coppie <code>label=regexp<\/code>. Come avviene? Tramite l'intersezione di un insieme <code>timeseries_ids<\/code> per ogni coppia <code>label=value<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/837a15a078e8c9422c721f3f072c0c09.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Consideriamo diverse implementazioni dell'indice invertito. Iniziamo dalla pi\u00f9 semplice e ingenua. Essa appare cos\u00ec. <\/p>\n<p><\/p>\n<p>Funzione <code>getMetricIDs<\/code> ottiene un elenco di stringhe. Ogni stringa contiene <code>label=value<\/code>. Questa funzione restituisce un elenco di <code>metricIDs<\/code>.<\/p>\n<p><\/p>\n<p>Come funziona? Abbiamo una variabile globale chiamata <code>invertedIndex<\/code>. \u00c8 un normale dizionario (<code>map<\/code>), che mappa una stringa a un slice di interi. La stringa contiene <code>label=value<\/code>.<\/p>\n<p><\/p>\n<p>Implementazione della funzione: estraiamo <code>metricIDs<\/code> per il primo <code>label=value<\/code>, poi passiamo a tutti gli altri <code>label=value<\/code>, estraendo <code>metricIDs<\/code> per loro. E invochiamo la funzione <code>intersectInts<\/code>, di cui si parler\u00e0 pi\u00f9 avanti. Questa funzione restituisce l'intersezione di queste liste.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/709beca79884a9693098781974c26f4d.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Come potete vedere, l'implementazione dell'indice invertito non \u00e8 molto complessa. Ma \u00e8 un'implementazione ingenua. Quali sono i suoi svantaggi? Il principale svantaggio dell'implementazione ingenua \u00e8 che tale indice invertito viene memorizzato in memoria. Dopo il riavvio dell'applicazione, perdiamo questo indice. Non c'\u00e8 salvataggio di questo indice su disco. Un indice invertito di questo tipo \u00e8 poco utile per un database.<\/p>\n<p><\/p>\n<p>Il secondo svantaggio \u00e8 anch'esso legato alla memoria. L'indice invertito deve adattarsi nella memoria. Se supera il limite della memoria, \u00e8 ovvio che si otterr\u00e0 un errore di 'out of memory'. E il programma non funzioner\u00e0.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/495394168ac03aa8cbcf56ccad1cb2b8.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Questo problema pu\u00f2 essere risolto utilizzando soluzioni gi\u00e0 pronte, come <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/google\/leveldb\">LevelDB<\/a><\/noindex>, oppure <noindex><a rel=\"nofollow\" href=\"https:\/\/rocksdb.org\/\">RocksDB<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>In breve, abbiamo bisogno di un database che consenta di effettuare tre operazioni rapidamente. <\/p>\n<p><\/p>\n<ul>\n<li>La prima operazione consiste nell'inserimento <code>chiave-valore<\/code> in questo database. Lo fa molto rapidamente, dove <code>chiave-valore<\/code> sono stringhe arbitrarie. <\/li>\n<li>La seconda operazione \u00e8 una ricerca rapida di un valore tramite una chiave fornita.<\/li>\n<li>E la terza operazione \u00e8 una ricerca veloce di tutti i valori con un determinato prefisso. <\/li>\n<\/ul>\n<p><\/p>\n<p>LevelDB e RocksDB sono database sviluppati rispettivamente da Google e Facebook. Inizialmente \u00e8 stato creato LevelDB. Poi, ragazzi di Facebook hanno preso LevelDB e hanno iniziato a migliorarlo, creando RocksDB. Adesso, quasi tutti i database interni di Facebook si basano su RocksDB, incluso MySQL, che \u00e8 stato anch'esso convertito in RocksDB. L'hanno chiamato <noindex><a rel=\"nofollow\" href=\"http:\/\/myrocks.io\/\">MyRocks<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>Un indice invertito pu\u00f2 essere implementato utilizzando LevelDB. Come fare? Salviamo come chiave <code>label=value<\/code>. E come valore, l'identificatore della serie temporale in cui \u00e8 presente la coppia <code>label=value<\/code>.<\/p>\n<p><\/p>\n<p>Se abbiamo molte serie temporali con questa coppia <code>label=value<\/code>, ci saranno molte righe in questo database con la stessa chiave e valori diversi. <code>timeseries_ids<\/code>Per ottenere un elenco di tutti <code>timeseries_ids<\/code>, che iniziano con <code>label=prefix<\/code>, facciamo una scansione di intervallo, per la quale questo database \u00e8 ottimizzato. Cio\u00e8, selezioniamo tutte le righe che iniziano con <code>label=prefix<\/code> e otteniamo i valori necessari. <code>timeseries_ids<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/a40877ecb758ac3bc1979bcd568d7e83.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Ecco un'implementazione approssimativa, come apparirebbe in Go. Abbiamo un indice invertito. Questo \u00e8 LevelDB.<\/p>\n<p><\/p>\n<p>La funzione \u00e8 la stessa di quella dell'implementazione ingenua. Segue quasi riga per riga l'implementazione ingenua. L'unico punto \u00e8 che invece di fare riferimento a <code>map<\/code> ci riferiamo all'indice invertito. Estraiamo tutti i valori per la prima <code>label=value<\/code>. Poi scorrendo tutte le restanti coppie <code>label=value<\/code> estraiamo i relativi set di metricIDs per esse. Poi troviamo l'intersezione. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/de11b15286816ada04b22727ff78e43d.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Sembra tutto a posto, ma ci sono degli svantaggi in questa soluzione. VictoriaMetrics inizialmente implementava un indice invertito basato su LevelDB, ma alla fine ha dovuto abbandonarlo.<\/p>\n<p><\/p>\n<p>Perch\u00e9? Perch\u00e9 LevelDB \u00e8 pi\u00f9 lento dell'implementazione ingenua. Nell'implementazione ingenua, dato un determinato valore, estraiamo immediatamente l'intero slice. <code>metricIDs<\/code>Questa \u00e8 un'operazione molto veloce \u2014 l'intero slice \u00e8 pronto per l'uso.<\/p>\n<p><\/p>\n<p>In LevelDB, invece, ad ogni chiamata della funzione <code>GetValues<\/code> dobbiamo esaminare tutte le righe che iniziano con <code>label=value<\/code>. E per ogni riga estrarre il valore <code>timeseries_ids<\/code>. Da questi <code>timeseries_ids<\/code> costruire uno slice di questi. <code>timeseries_ids<\/code>. \u00c8 chiaro che questo \u00e8 molto pi\u00f9 lento rispetto a una semplice chiamata a una mappa comune per chiave.<\/p>\n<p><\/p>\n<p>Il secondo svantaggio \u00e8 che LevelDB \u00e8 scritto in C. La chiamata a funzioni in C da Go non \u00e8 molto veloce. Ci vogliono centinaia di nanosecondi. Non \u00e8 molto veloce, perch\u00e9 rispetto a una normale chiamata a una funzione scritta in Go, che richiede 1-5 nanosecondi, la differenza di prestazioni \u00e8 decine di volte. Per VictoriaMetrics, questo \u00e8 stato un difetto fatale \ud83d\ude42<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/47cace825807b7b062064c2e790dc9d3.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Perci\u00f2, ho scritto la mia implementazione di un indice invertito. E l'ho chiamata <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/VictoriaMetrics\/VictoriaMetrics\/tree\/master\/lib\/mergeset\">mergeset<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>Mergeset \u00e8 basato sulla struttura dati MergeTree. Questa struttura dati \u00e8 stata presa in prestito da ClickHouse. \u00c8 evidente che mergeset deve essere ottimizzato per una ricerca rapida <code>timeseries_ids<\/code> con una chiave fornita. Mergeset \u00e8 scritto interamente in Go. Puoi vedere <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/VictoriaMetrics\/VictoriaMetrics\">il codice sorgente di VictoriaMetrics su GitHub<\/a><\/noindex>. L'implementazione di mergeset si trova nella cartella <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/VictoriaMetrics\/VictoriaMetrics\/tree\/master\/lib\/mergeset\">\/lib\/mergeset<\/a><\/noindex>. Puoi provare a capire cosa succede l\u00ec.<\/p>\n<p><\/p>\n<p>L'API di mergeset \u00e8 molto simile a quella di LevelDB e RocksDB. Cio\u00e8, consente di inserire rapidamente nuove registrazioni e di effettuare rapidamente ricerche di registrazioni con un determinato prefisso.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/845704ed36f70beb468907d22701af59.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Parleremo pi\u00f9 avanti dei difetti di mergeset. Adesso discuteremo delle problematiche riscontrate da VictoriaMetrics in produzione durante l'implementazione dell'indice invertito.<\/p>\n<p><\/p>\n<p>Perch\u00e9 sono emerse queste problematiche?<\/p>\n<p><\/p>\n<p>La prima ragione \u00e8 il high churn rate. In italiano, questo significa un frequente cambio delle serie temporali. Quando una serie temporale termina e inizia una nuova serie, oppure iniziano molte nuove serie. E questo avviene frequentemente.<\/p>\n<p><\/p>\n<p>La seconda ragione \u00e8 il grande numero di serie temporali. Inizialmente, quando il monitoraggio stava guadagnando popolarit\u00e0, il numero di serie temporali era ridotto. Ad esempio, per ogni computer si deve monitorare il carico della CPU, della memoria, della rete e del disco. 4 serie temporali per ogni computer. Supponiamo di avere 100 computer e 400 serie temporali. \u00c8 molto poco. <\/p>\n<p><\/p>\n<p>Con il passare del tempo, le persone hanno pensato di misurare informazioni pi\u00f9 dettagliate. Ad esempio, misurare il carico non dell'intero processore, ma di ciascun core del processore. Se hai 40 core, avrai quindi 40 volte pi\u00f9 serie temporali per misurare il carico della CPU. <\/p>\n<p><\/p>\n<p>Ma non \u00e8 tutto. Ogni core del processore pu\u00f2 avere pi\u00f9 stati come idle, quando \u00e8 inattivo. Inoltre, lavori in user space, lavori in kernel space e altri stati. E ogni stato pu\u00f2 essere misurato come una serie temporale separata. Questo aumenta ulteriormente il numero di righe di 7-8 volte.<\/p>\n<p><\/p>\n<p>Da una sola metrica, abbiamo ottenuto 40 x 8 = 320 metriche solo per un computer. Moltiplicando per 100, otteniamo 32.000 invece di 400. <\/p>\n<p><\/p>\n<p>Poi \u00e8 arrivato Kubernetes. E la situazione \u00e8 peggiorata, perch\u00e9 con Kubernetes possono essere ospitati molti servizi diversi. Ogni servizio in Kubernetes \u00e8 composto da molti pod. Tutto questo deve essere monitorato. Inoltre, abbiamo un deployment continuo di nuove versioni dei vostri servizi. Per ogni nuova versione devono essere creati nuovi series temporali. Di conseguenza, il numero di series temporali cresce esponenzialmente e affrontiamo il problema dell'elevata cardinalit\u00e0. VictoriaMetrics gestisce questo problema con successo rispetto ad altri database per series temporali. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/c11b7ce6d3294ae420243f72636af4b9.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Analizziamo pi\u00f9 in dettaglio l'elevato tasso di abbandono. Da cosa nasce un elevato tasso di abbandono in produzione? Perch\u00e9 alcuni valori delle etichette e dei tag cambiano continuamente.<\/p>\n<p><\/p>\n<p>Ad esempio, prendiamo Kubernetes, in cui esiste il concetto di <code>deployment<\/code>, cio\u00e8 quando viene rilasciata una nuova versione della vostra applicazione. Gli sviluppatori di Kubernetes hanno deciso di aggiungere l'ID del deployment nell'etichetta.<\/p>\n<p><\/p>\n<p>A cosa ha portato tutto questo? Al fatto che ad ogni nuovo deployment tutte le vecchie serie temporali vengono interrotte e al loro posto iniziano nuove serie temporali con un nuovo valore dell'etichetta. <code>deployment_id<\/code>. Tali series possono essere centinaia di migliaia e persino milioni.<\/p>\n<p><\/p>\n<p>Un aspetto importante di tutto ci\u00f2 \u00e8 che il numero totale di series temporali cresce, ma il numero di series temporali attive, per le quali arrivano dati, rimane costante. Questa situazione \u00e8 chiamata elevato tasso di abbandono.<\/p>\n<p><\/p>\n<p>Il problema principale dell'elevato tasso di abbandono \u00e8 garantire una velocit\u00e0 costante di ricerca in tutte le series temporali per un dato insieme di etichette in un certo intervallo di tempo. Di solito, si tratta di un intervallo di tempo dell'ultima ora o dell'ultimo giorno. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/ac18482b87cc37624d163b30c68cf7be.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Come risolvere questo problema? Ecco la prima opzione. Si tratta di suddividere l'indice invertito in parti indipendenti per tempo. Cio\u00e8, passa un certo intervallo di tempo, smettiamo di lavorare con l'indice invertito attuale e creiamo un nuovo indice invertito. Passa un altro intervallo di tempo, creiamo un altro indice e cos\u00ec via. <\/p>\n<p><\/p>\n<p>E durante la selezione di questi indici invertiti troviamo un insieme di indici invertiti che rientrano nell'intervallo specificato. E, di conseguenza, selezioniamo gli ID delle series temporali da l\u00ec. <\/p>\n<p><\/p>\n<p>Questo consente di risparmiare risorse, perch\u00e9 non \u00e8 necessario esaminare parti che non rientrano nell'intervallo specificato. Cio\u00e8, di solito, se selezioniamo dati per l'ultima ora, saltiamo le richieste per i periodi temporali precedenti. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/1afa3a88caa7423941ae3494b9cec706.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>C'\u00e8 anche un'altra opzione per risolvere questo problema. Si tratta di mantenere per ogni giorno un elenco separato di ID delle series temporali che si sono verificate in quel giorno.<\/p>\n<p><\/p>\n<p>Il vantaggio di questa soluzione rispetto alla precedente \u00e8 che non dupliciamo le informazioni sulle series temporali che non scompaiono nel tempo. Rimangono costantemente disponibili e non cambiano. <\/p>\n<p><\/p>\n<p>Lo svantaggio \u00e8 che questa soluzione \u00e8 pi\u00f9 complessa da implementare e da debuggare. E VictoriaMetrics ha scelto questa soluzione. \u00c8 stata una decisione storica. Questa soluzione si \u00e8 dimostrata anche piuttosto efficace, rispetto alla precedente. Perch\u00e9 questa soluzione non \u00e8 stata implementata poich\u00e9 si dovevano duplicare i dati in ogni partizione per le series temporali che non cambiano, cio\u00e8 che non scompaiono nel tempo. VictoriaMetrics \u00e8 stata ottimizzata principalmente per il consumo di spazio su disco, e la precedente implementazione peggiorava tale consumo. Questa implementazione \u00e8 invece pi\u00f9 adatta a minimizzare il consumo di spazio su disco, quindi \u00e8 stata scelta. <\/p>\n<p><\/p>\n<p>Abbiamo dovuto affrontarla. La lotta consisteva nel fatto che in questa implementazione \u00e8 necessario comunque selezionare un numero di dati molto maggiore rispetto a quando l'indice invertito \u00e8 suddiviso per tempo. <code>timeseries_ids<\/code> Come abbiamo risolto questo problema? Lo abbiamo fatto in un modo originale: salvando diversi identificatori delle series temporali in ogni record dell'indice invertito anzich\u00e9 un solo identificatore. Cio\u00e8, abbiamo una chiave<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/5b10e61aaa803428ed3bfe31815f09d3.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>, che si trova in ogni series temporale. E ora salviamo diversi <code>label=value<\/code>in un solo record. <code>timeseries_ids<\/code> Ecco un esempio. Prima avevamo N record, ora ne abbiamo uno il cui prefisso \u00e8 lo stesso di tutti gli altri. Il precedente record conteneva tutti gli ID delle series temporali.<\/p>\n<p><\/p>\n<p>Questo ha consentito di aumentare la velocit\u00e0 di scansione di tale indice invertito fino a 10 volte. E ha permesso di ridurre il consumo di memoria per la cache, perch\u00e9 ora memorizziamo una stringa <\/p>\n<p><\/p>\n<p>Questo ha permesso di aumentare la velocit\u00e0 di scansione di un indice invertito fino a 10 volte. Ha anche ridotto il consumo di memoria per la cache, poich\u00e9 ora memorizziamo la stringa. <code>label=value<\/code> solo una volta nella cache insieme a N volte. E questa stringa pu\u00f2 essere lunga, se hai a lungo nei tag e nelle etichette, che Kubernetes ama inserire.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/ae4f38a440203bad2d03cf2abd1faafe.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Un'altra opzione per velocizzare la ricerca tramite l'indice invertito \u00e8 lo sharding. Creare pi\u00f9 indici invertiti anzich\u00e9 uno e frammentare i dati tra di essi in base a una chiave. Questo \u00e8 un insieme <code>key=value<\/code> di coppie. Cio\u00e8, otteniamo pi\u00f9 indici invertiti indipendenti, che possiamo interrogare in parallelo su pi\u00f9 processori. Le implementazioni precedenti permettevano di lavorare solo in modalit\u00e0 monoprocessore, cio\u00e8 di scansionare i dati solo su un core. Questa soluzione consente di scansionare i dati su pi\u00f9 core contemporaneamente, come ama fare ClickHouse. Questo intendiamo implementare.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/5ced6446efbf8ded8d7fb91694a999b0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>E ora torniamo ai nostri argomenti \u2013 alla funzione di intersezione <code>timeseries_ids<\/code>. Vediamo quali potrebbero essere le implementazioni. Questa funzione consente di trovare <code>timeseries_ids<\/code> per un insieme specifico <code>label=value<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/073a155018a91120c6996c324772f9af.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>La prima opzione \u00e8 un'implementazione naive. Due cicli annidati. Ecco cosa otteniamo in ingresso nella funzione <code>intersectInts<\/code> due slices \u2014 <code>a<\/code> e <code>b<\/code>. In uscita dovrebbe restituire l'intersezione di questi slices.<\/p>\n<p><\/p>\n<p>L'implementazione naive appare cos\u00ec. Scorriamo tutti i valori dallo slice <code>a<\/code>, all'interno di questo ciclo scorriamo tutti i valori dallo slice <code>b<\/code>. E li confrontiamo. Se coincidono, significa che abbiamo trovato un'intersezione. E lo salviamo in <code>result<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/b89dcbb4f6d73377f9cf4f50169d6dd7.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Quali sono gli svantaggi? La complessit\u00e0 quadratica \u2014 questo \u00e8 il suo principale svantaggio. Ad esempio, se hai dimensioni dello slice <code>a<\/code> e <code>b<\/code> di un milione, questa funzione non ti restituir\u00e0 mai una risposta. Perch\u00e9 dovr\u00e0 fare un trilione di iterazioni, che sono davvero molte anche per i computer moderni.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/bad26b0092a2fd7fc813bcb79a9138ce.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>La seconda implementazione si basa su una mappa. Creiamo una mappa. Inseriamo in questa mappa tutti i valori dallo slice <code>a<\/code>. Poi passiamo con un ciclo separato attraverso lo slice <code>b<\/code>. E controlliamo \u2013 se questo valore dallo slice <code>b<\/code> \u00e8 nella mappa. Se c'\u00e8, lo aggiungiamo al risultato. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/1e610acf641a9cfe4c80aac1fe26044c.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Quali sono i vantaggi? Il vantaggio \u00e8 che qui c'\u00e8 solo complessit\u00e0 lineare. Cio\u00e8, la funzione verr\u00e0 eseguita molto pi\u00f9 velocemente per grandi dimensioni degli slices. Per uno slice di un milione di dimensioni, questa funzione verr\u00e0 eseguita in 2 milioni di iterazioni, a differenza di un trilione di iterazioni, come nella funzione precedente.<\/p>\n<p><\/p>\n<p>E lo svantaggio \u00e8 che questa funzione richiede pi\u00f9 memoria per creare questa mappa.<\/p>\n<p><\/p>\n<p>Un secondo svantaggio \u00e8 l'elevato overhead per l'hashing. Questo svantaggio non \u00e8 molto evidente. E per noi non era molto evidente, quindi inizialmente l'implementazione dell'intersezione in VictoriaMetrics era tramite mappa. Ma poi il profiling ha mostrato che la maggior parte del tempo del processore veniva speso per la scrittura nella mappa e per controllare se il valore era presente in questa mappa.<\/p>\n<p><\/p>\n<p>Perch\u00e9 in questi punti viene speso tempo del processore? Perch\u00e9 in queste righe Go esegue l'operazione di hashing. Cio\u00e8, calcola l'hash dalla chiave, per poi accedere all'indice specificato nell'HashMap. L'operazione di calcolo dell'hash viene eseguita in decine di nanosecondi. \u00c8 lenta per VictoriaMetrics.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/969f10d875d88998e958894ca28b1181.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Ho deciso di implementare un bitset, ottimizzato specificamente per questo caso. Ecco come ora appare l'intersezione di due slices. Qui creiamo un bitset. Aggiungiamo ad esso gli elementi dal primo slice. Poi verifichiamo la presenza di questi elementi nel secondo slice. E li aggiungiamo al risultato. Cio\u00e8, non differisce molto dall'esempio precedente. L'unica cosa che abbiamo qui \u00e8 che abbiamo sostituito l'accesso alla mappa con funzioni personalizzate. <code>add<\/code> e <code>has<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/b4ec0753d30d9684868a031f605632c0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>A prima vista pu\u00f2 sembrare che questo dovrebbe funzionare pi\u00f9 lentamente, dato che prima veniva utilizzata la mappa standard e qui vengono chiamate alcune funzioni, ma il profiling mostra che questa soluzione funziona dieci volte pi\u00f9 velocemente rispetto alla mappa standard nel caso di VictoriaMetrics.<\/p>\n<p><\/p>\n<p>Inoltre, utilizza molta meno memoria rispetto all'implementazione su mappa. Perch\u00e9 qui memorizziamo bit invece di valori a otto byte.<\/p>\n<p><\/p>\n<p>Lo svantaggio di tale implementazione \u00e8 che non \u00e8 cos\u00ec ovvia, non \u00e8 banale. <\/p>\n<p><\/p>\n<p>Un altro svantaggio, che molti potrebbero non notare, \u00e8 che questa implementazione pu\u00f2 funzionare male in alcuni casi. Cio\u00e8, \u00e8 ottimizzata per un caso specifico, per questo caso di intersezione degli ids delle serie temporali in VictoriaMetrics. Ci\u00f2 non significa che si adatti a tutti i casi. Se viene utilizzata in modo errato, non otterremo un incremento delle prestazioni, ma un errore di esaurimento della memoria e un rallentamento delle prestazioni. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/94bd174afe43e67b3cf279dc7a1be17c.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Consideriamo l'implementazione di questa struttura. Se volete vedere, si trova nel codice sorgente di VictoriaMetrics, nella cartella <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/VictoriaMetrics\/VictoriaMetrics\/tree\/master\/lib\/uint64set\">lib\/uint64set<\/a><\/noindex>. \u00c8 ottimizzata specificamente per il caso di VictoriaMetrics, dove <code>timeseries_id<\/code> \u00e8 rappresentato come un valore a 64 bit, dove i primi 32 bit sono per la maggior parte costanti e cambiano solo gli ultimi 32 bit.<\/p>\n<p><\/p>\n<p>Questa struttura dati non viene memorizzata su disco, funziona solo in memoria. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/e24ca65f860e2b04037e073f7acae0c1.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Ecco la sua API. Non \u00e8 molto complessa. L'API \u00e8 stata modellata specificamente per un esempio di utilizzo di VictoriaMetrics. Cio\u00e8, non ci sono funzioni superflue. Ci sono funzioni che sono esplicitamente utilizzate da VictoriaMetrics.<\/p>\n<p><\/p>\n<p>Ci sono funzioni <code>add<\/code>, che aggiunge nuovi valori. C'\u00e8 una funzione <code>has<\/code>, che verifica nuovi valori. E c'\u00e8 una funzione <code>del<\/code>, che rimuove valori. C'\u00e8 una funzione ausiliaria <code>len<\/code>, che restituisce la dimensione dell'insieme. La funzione <code>clone<\/code> clona l'insieme. E la funzione <code>appendto<\/code> trasforma questo set in uno slice. <code>timeseries_ids<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/874b04042d13f342d39f8f393e7fb78c.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Ecco come appare l'implementazione di questa struttura dati. In set ci sono due elementi:<\/p>\n<p><\/p>\n<ul>\n<li>\n<p><code>ItemsCount<\/code> \u2013 \u00e8 un campo ausiliario, per restituire rapidamente il numero di elementi nel set. Si potrebbe fare a meno di questo campo ausiliario, ma \u00e8 stato necessario aggiungerlo qui, poich\u00e9 VictoriaMetrics spesso interroga la lunghezza del bitset nei suoi algoritmi.<\/p>\n<p>\n<\/li>\n<li>\n<p>Il secondo campo \u00e8 <code>buckets<\/code>. Questo \u00e8 uno slice della struttura <code>bucket32<\/code>. In ogni struttura \u00e8 memorizzato <code>hi<\/code> un campo. Questi sono i 32 bit superiori. E due slice \u2014 <code>b16his<\/code> e <code>buckets<\/code> di <code>bucket16<\/code> strutture. <\/p>\n<p>\n<\/li>\n<\/ul>\n<p><\/p>\n<p>Qui sono memorizzati i 16 bit superiori della seconda parte della struttura a 64 bit. E qui sono memorizzati i bitsets per i 16 bit inferiori di ciascun byte. <\/p>\n<p><\/p>\n<p><code>Bucket64<\/code> \u00e8 composto da un array di <code>uint64.<\/code>La lunghezza \u00e8 calcolata utilizzando queste costanti. In uno <code>bucket16<\/code> pu\u00f2 essere memorizzati al massimo <code>2^16=65536<\/code> bit. Se lo si divide per 8, sono 8 kilobyte. Se si divide ancora una volta per 8, si ottengono 1000 <code>uint64.<\/code> valore. Cio\u00e8, <code>Bucket16<\/code> \u2013 \u00e8 una struttura da 8 kilobyte. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/1cca4e6bfafa3408a3c95e0118e26d80.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Consideriamo come \u00e8 implementato uno dei metodi di questa struttura per l'aggiunta di un nuovo valore. <\/p>\n<p><\/p>\n<p>Tutto inizia con <code>uint64.<\/code> un valore. Calcoliamo i 32 bit superiori, calcoliamo i 32 bit inferiori. Scorriamo tutti <code>buckets<\/code>. Confrontiamo i 32 bit superiori in ciascun bucket con il valore da aggiungere. E se coincidono, chiamiamo la funzione <code>add<\/code> nella struttura b32. <code>buckets<\/code>E aggiungiamo i 32 bit inferiori l\u00ec. E se restituisce <code>true<\/code>, significa che abbiamo aggiunto quel valore l\u00ec e non avevamo quel valore. Se restituisce <code>false<\/code>, significa che tale valore era gi\u00e0 presente. Poi aumentiamo il numero di elementi nella struttura. <\/p>\n<p><\/p>\n<p>Se non abbiamo trovato il <code>bucket<\/code> con il valore hi corretto, chiamiamo la funzione <code>addAlloc<\/code>, che alloca un nuovo <code>bucket<\/code>, aggiungendolo nella struttura a bucket.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/c5d1e5de767f47c7c40e01e12b6d0617.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Questa \u00e8 l'implementazione della funzione <code>b32.add.<\/code>\u00c8 simile all'implementazione precedente. Calcoliamo i 16 bit superiori, i 16 bit inferiori.<\/p>\n<p><\/p>\n<p>Poi scorriamo tutti i 16 bit superiori. Troviamo corrispondenze. E al correlare chiamiamo il metodo add, che esamineremo nella pagina successiva per <code>bucket16<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/e98c4ef316f3a894fd7019c3c85696f7.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Ed ecco il livello pi\u00f9 basso, che deve essere ottimizzato al massimo. Calcoliamo per <code>uint64.<\/code> l'id valore nello slice bit, cos\u00ec come <code>bitmask.<\/code>Questa \u00e8 una maschera per il valore a 64 bit, con la quale \u00e8 possibile verificare la presenza di questo bit o impostarlo. Verifichiamo la presenza di questo bit, lo impostiamo e restituiamo la presenza. Ecco come \u00e8 strutturata questa implementazione, che ha permesso di accelerare l'operazione di intersezione degli ids delle serie temporali di 10 volte rispetto alle normali maps.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/4dfa5a6142829a3551be22d6c9c3ab92.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>In VictoriaMetrics, oltre a questa ottimizzazione, ci sono molte altre ottimizzazioni. La maggior parte di queste ottimizzazioni non \u00e8 stata aggiunta a caso, ma dopo aver profilato il codice in produzione.<\/p>\n<p><\/p>\n<p>Questa \u00e8 la regola principale dell'ottimizzazione: non aggiungere ottimizzazioni supponendo che ci sar\u00e0 un collo di bottiglia, perch\u00e9 potrebbe rivelarsi che non ci sar\u00e0. L'ottimizzazione di solito peggiora la qualit\u00e0 del codice. Pertanto, \u00e8 meglio ottimizzare solo dopo il profiling e idealmente in produzione, in modo da avere dati reali. Chi \u00e8 interessato, pu\u00f2 guardare i sorgenti di VictoriaMetrics e studiare altre ottimizzazioni che ci sono.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Ottimizzazioni Go in VictoriaMetrics. Aleksandr Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/7585c4dc782627bac5b649e6a55e2ffb.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><em>Ho una domanda sul bitset. Sembra molto simile all'implementazione di C++ vector bool, un bitset ottimizzato. Avete preso l'implementazione da l\u00ec?<\/em><\/p>\n<p><\/p>\n<p>No, non da l\u00ec. Quando ho implementato questo bitset, mi sono basato sulla conoscenza della struttura di questi ids delle serie temporali, che vengono utilizzati in VictoriaMetrics. La loro struttura \u00e8 tale che i 32 bit superiori sono principalmente costanti. I 32 bit inferiori possono cambiare. Pi\u00f9 il bit \u00e8 in basso, pi\u00f9 frequentemente pu\u00f2 cambiare. Pertanto, questa implementazione \u00e8 stata ottimizzata per questa specifica struttura dati. Per quanto ne so, l'implementazione in C++ \u00e8 ottimizzata per il caso generale. Se si effettua un'ottimizzazione per il caso generale, significa che non sar\u00e0 la pi\u00f9 ottimale per il caso specifico.<\/p>\n<p><\/p>\n<p>Ti consiglio di guardare anche la presentazione di Alexey Milovidov. Circa un mese fa ha parlato delle ottimizzazioni in ClickHouse per specializzazioni specifiche. Dice proprio che, nel caso generale, l'implementazione in C++ o qualsiasi altra implementazione \u00e8 progettata per funzionare bene in media. Potrebbe funzionare peggio rispetto a un'implementazione specializzata basata su conoscenze specifiche, come nel nostro caso, quando sappiamo che i 32 bit superiori sono principalmente costanti.<\/p>\n<p><\/p>\n<p><em>Ho una seconda domanda. Qual \u00e8 la differenza fondamentale rispetto a InfluxDB?<\/em><\/p>\n<p><\/p>\n<p>Ci sono molte differenze fondamentali. Per quanto riguarda le prestazioni e il consumo di memoria, InfluxDB nei test mostra un consumo di memoria dieci volte superiore per serie temporali ad alta cardinalit\u00e0, quando ne hai molte, ad esempio, milioni. Ad esempio, VictoriaMetrics consuma 1 GB per un milione di serie attive, mentre InfluxDB consuma 10 GB. E questa \u00e8 una grande differenza. <\/p>\n<p><\/p>\n<p>La seconda differenza fondamentale \u00e8 che in InfluxDB ci sono linguaggi di query piuttosto strani \u2013 Flux e InfluxQL. Non sono molto comodi per lavorare con le serie temporali rispetto a <noindex><a rel=\"nofollow\" href=\"https:\/\/medium.com\/@valyala\/promql-tutorial-for-beginners-9ab455142085\">PromQL<\/a><\/noindex>, che \u00e8 supportato in VictoriaMetrics. PromQL \u00e8 il linguaggio di query di Prometheus.<\/p>\n<p><\/p>\n<p>Un'altra differenza \u00e8 che InfluxDB presenta un modello dati un po' strano, dove in ogni riga possono essere memorizzati pi\u00f9 campi con set di tag diversi. Queste righe sono ulteriormente divise in varie tabelle. Questi ulteriori complessit\u00e0 rendono pi\u00f9 difficile lavorare con questo database. \u00c8 complicato da mantenere e capire.<\/p>\n<p><\/p>\n<p>In VictoriaMetrics tutto \u00e8 molto pi\u00f9 semplice. Ogni serie temporale \u00e8 rappresentata come una coppia chiave-valore. Il valore \u00e8 un insieme di punti \u2013 <code>(timestamp, value)<\/code>, mentre la chiave \u00e8 un insieme di <code>label=value<\/code>. Non c'\u00e8 alcuna suddivisione in campi e misurazioni. Questo ti permette di selezionare qualsiasi dato e poi combinarlo, sommarlo, sottrarlo, moltiplicarlo e dividerlo, a differenza di InfluxDB, dove i calcoli tra diverse serie non sono ancora stati implementati, per quanto ne so. Anche se fossero realizzati, sarebbe complicato e bisognerebbe scrivere un sacco di codice. <\/p>\n<p><\/p>\n<p><em>Ho una domanda di chiarimento. Ho capito correttamente che c'era un problema, di cui parlavi, riguardo al fatto che questo indice invertito non entra in memoria, quindi l\u00ec si procede con la partizione?<\/em><\/p>\n<p><\/p>\n<p>Inizialmente ho mostrato un'implementazione na\u00efve di un indice invertito su una mappa standard di Go. Tale implementazione non \u00e8 adatta per i database, perch\u00e9 questo indice invertito non viene salvato su disco, mentre un database deve salvare su disco affinch\u00e9 i dati rimangano accessibili dopo il riavvio. In questa implementazione, al riavvio dell'applicazione, l'indice invertito scomparir\u00e0. E perderete l'accesso a tutti i dati, perch\u00e9 non sarete in grado di trovarli. <\/p>\n<p><\/p>\n<p><em>Buongiorno! Grazie per la tua presentazione! Mi chiamo Pavel. Vengo dall'azienda Wildberries. Ho alcune domande per te. Prima domanda. Cosa ne pensi? Se avessi scelto un principio diverso nella costruzione dell'architettura della tua applicazione e avessi partizionato i dati per tempo, sarebbe stato possibile fare intersezioni dei dati in base esclusivamente al fatto che in una partizione ci sono dati per un dato intervallo di tempo, cio\u00e8 per un tempo specifico, senza dover preoccuparti del fatto che hai pezzi sparsi? Seconda domanda: visto che implementi un algoritmo simile con bitset e tutto il resto, hai mai provato a utilizzare istruzioni del processore? Forse hai provato tali ottimizzazioni?<\/em><\/p>\n<p><\/p>\n<p>Rispondo subito alla seconda domanda. Non ci siamo ancora arrivati. Ma se sar\u00e0 necessario, ci arriveremo. E la prima, qual era la domanda?<\/p>\n<p><\/p>\n<p><em>Hai discusso due scenari. E hai detto che hai scelto il secondo con un'implementazione pi\u00f9 complessa. E non hai preferito il primo, dove i dati sono partizionati per tempo.<\/em> <\/p>\n<p><\/p>\n<p>S\u00ec. Nel primo caso, il volume complessivo dell'indice sarebbe stato maggiore, perch\u00e9 in ogni partizione dovremmo memorizzare dati duplicati per quelle serie temporali che si estendono attraverso tutte queste partizioni. E se hai un basso tasso di churn nelle serie temporali, cio\u00e8 le stesse righe vengono utilizzate continuamente, nel primo caso perderemmo molto di pi\u00f9 in termini di spazio disco rispetto al secondo caso.<\/p>\n<p><\/p>\n<p>Cos\u00ec com'\u00e8, s\u00ec, la partizione per tempo \u00e8 una buona opzione. \u00c8 ci\u00f2 che utilizza Prometheus. Ma in Prometheus c'\u00e8 un altro inconveniente. Durante la fusione di questi pezzi di dati, ha bisogno di mantenere in memoria le informazioni sui metadati per tutte le etichette e le serie temporali. Pertanto, se i pezzi di dati sono grandi che sta unendo, il consumo di memoria cresce notevolmente durante la fusione, a differenza di VictoriaMetrics. Durante la fusione, VictoriaMetrics non consuma praticamente memoria, ci vogliono solo pochi kilobyte, indipendentemente dalle dimensioni dei pezzi di dati da unire.<\/p>\n<p><\/p>\n<p><em>L'algoritmo che stai usando consuma memoria. In essa vengono contrassegnate le etichette delle serie temporali, dove ci sono valori. E in questo modo verifichi la presenza congiunta in un array di dati e in un altro. E capisci cos\u00ec se si \u00e8 verificata un'intersezione o meno. Di solito nei database si implementano i cursori, gli iteratori, che tengono traccia del loro stato corrente e scorrono attraverso i dati ordinati grazie ai quali hai una complessit\u00e0 semplice operazioni.<\/em> <\/p>\n<p><\/p>\n<p>Perch\u00e9 non usiamo cursori per l'intersezione dei dati?<\/p>\n<p><\/p>\n<p><em>S\u00ec.<\/em> <\/p>\n<p><\/p>\n<p>In LevelDB or in mergeset, we store exactly the sorted lines. We can walk through with a cursor and find intersections. But why don\u2019t we use it? Because it\u2019s slow. Because cursors imply that a function needs to be called for each line. Calling a function takes 5 nanoseconds. And if you have 100,000,000 lines, it means we spend half a second just for the function call.<\/p>\n<p><\/p>\n<p><em>Yes, that exists. And I have one last question. This question might sound a bit strange. Why can't we calculate all necessary aggregates when the data comes in and store them in the required form? Why save huge volumes in systems like VictoriaMetrics, ClickHouse, etc., to later spend a lot of time processing them?<\/em><\/p>\n<p><\/p>\n<p><em>Let me give you an example for clarity. How does a small toy speedometer work? It records the distance you\u2019ve traveled, continuously adding it to one value, and another for time. It divides and gets the average speed. You can do something similar. Aggregate all necessary facts on the fly.<\/em><\/p>\n<p><\/p>\n<p>Okay, I understand the question. Your example is valid. If you know what aggregates you need, that's the best implementation. The problem is that people store these metrics and some data in ClickHouse, and they don\u2019t yet know how they will aggregate or filter them in the future, so they have to save all raw data. But if you know you need to calculate something average, why not calculate it instead of saving a bunch of raw values? But this only works if you know exactly what you need.<\/p>\n<p><\/p>\n<p>By the way, time series databases support aggregate calculations. For example, Prometheus supports <noindex><a rel=\"nofollow\" href=\"https:\/\/prometheus.io\/docs\/prometheus\/latest\/configuration\/recording_rules\/\">recording rules<\/a><\/noindex>. So this can be done if you know what aggregates you will need. VictoriaMetrics doesn\u2019t have this yet, but it\u2019s usually placed before Prometheus, where this can be done using recording rules.<\/p>\n<p><\/p>\n<p>For example, in my previous job, we needed to count events in a sliding window for the last hour. The problem was that we had to create a custom implementation in Go, meaning a service for counting this thing. This service turned out to be non-trivial because it's complicated to calculate. The implementation can be simple if you need to count aggregates at fixed intervals. However, if you want to count events in a sliding window, it\u2019s not as easy as it seems. I think it hasn\u2019t been implemented yet in ClickHouse or time series databases because it's challenging to implement.<\/p>\n<p><\/p>\n<p><em>And one more question. We were discussing averaging, and I remembered a thing called Graphite with a Carbon backend. It was capable of thinning out old data, leaving one point per minute, one point per hour, etc. This is quite convenient, as we might need raw data, so to speak, for a month, while everything else can be thinned out. But Prometheus and VictoriaMetrics don\u2019t support that functionality. Is there a plan to support it? If not, why?<\/em><\/p>\n<p><\/p>\n<p>Thank you for the question. Our users periodically ask it. They want to know when we will add support for downsampling. There are several issues. First, every user has their own definition of <code>downsampling<\/code> what it means: some want any arbitrary point at a given interval, while others want maximum, minimum, or average values. If multiple systems are writing data to your database, you can\u2019t just apply a one-size-fits-all approach. It may turn out that different thinning methods are needed for each system. And that\u2019s hard to implement.<\/p>\n<p><\/p>\n<p>Secondly, VictoriaMetrics, like ClickHouse, is optimized for working with large volumes of raw data, so it can process a billion lines in less than a second if you have many cores in your system. Scanning time series points in VictoriaMetrics is 50,000,000 points per second per core. And this performance scales with the available cores. So if you have 20 cores, for example, you'll get a billion points scanned per second. This characteristic of VictoriaMetrics and ClickHouse reduces the need for downsampling.<\/p>\n<p><\/p>\n<p>Another property is that VictoriaMetrics compresses this data efficiently. Compression averages between 0.4 to 0.8 bytes per point in production. Each point consists of a timestamp and a value. It compresses to less than one byte on average. <\/p>\n<p><\/p>\n<p><em>Sergey. I have a question. What is the minimum time quantum for recording?<\/em><\/p>\n<p><\/p>\n<p>Un millisecondo. Recentemente abbiamo avuto una conversazione con altri sviluppatori di database per le serie temporali. Il loro quantum di tempo minimo \u00e8 un secondo. Anche in Graphite, per esempio, \u00e8 un secondo. In OpenTSDB \u00e8 anch'esso un secondo. In InfluxDB, la precisione \u00e8 in nanosecondi. In VictoriaMetrics, \u00e8 un millisecondo, perch\u00e9 in Prometheus \u00e8 di un millisecondo. VictoriaMetrics \u00e8 stata progettata inizialmente come storage remoto per Prometheus. Ma ora pu\u00f2 conservare dati anche da altri sistemi. <\/p>\n<p><\/p>\n<p>La persona con cui ho parlato dice che la loro precisione \u00e8 di un secondo: \u00e8 sufficiente per loro, perch\u00e9 dipende dal tipo di dati memorizzati nel database delle serie temporali. Se si tratta di dati DevOps o dati sull'infrastruttura, dove li raccogli con un intervallo di 30 secondi o un minuto, allora un secondo di precisione \u00e8 sufficiente, meno non \u00e8 necessario. Ma se raccogli questi dati dai sistemi di trading ad alta frequenza, allora \u00e8 necessaria la precisione in nanosecondi.<\/p>\n<p><\/p>\n<p>La precisione in millisecondi in VictoriaMetrics \u00e8 adatta sia per i casi DevOps, sia per la maggior parte dei casi che ho menzionato all'inizio della relazione. L'unico caso per cui potrebbe non essere adatta \u00e8 per i sistemi di trading ad alta frequenza.<\/p>\n<p><\/p>\n<p><em>Grazie! E un'altra domanda. Qual \u00e8 la compatibilit\u00e0 con PromQL?<\/em><\/p>\n<p><\/p>\n<p>Compatibilit\u00e0 totale. VictoriaMetrics supporta completamente PromQL. Inoltre, aggiunge anche un'ulteriore funzionalit\u00e0 avanzata a PromQL, chiamata <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/VictoriaMetrics\/VictoriaMetrics\/wiki\/MetricsQL\">MetricsQL<\/a><\/noindex>. Riguardo a questa funzionalit\u00e0 avanzata, c'\u00e8 una relazione su YouTube. Ho parlato al Monitoring Meetup in primavera a San Pietroburgo.<\/p>\n<p><\/p>\n<p>canale Telegram <noindex><a rel=\"nofollow\" href=\"https:\/\/t.me\/VictoriaMetrics_ru1\">VictoriaMetrics<\/a><\/noindex>.<\/p>\n<p class=\"for_users_only_msg\">Solo gli utenti registrati possono partecipare al sondaggio. <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/auth\/login\/\">Accedi<\/a><\/noindex>, per favore.<\/p>\n<h2 class=\"default-block__polling-title\">Cosa ti impedisce di passare a VictoriaMetrics come archivio a lungo termine per Prometheus? (Scrivi nei commenti, aggiunger\u00f2 al sondaggio))<\/h2>\n<ul class=\"poll-result\">\n<li class=\"poll-result__item\">\n<p>                <strong class=\"poll-result__data-percent  poll-result__data-percent_winner\">71,4%<\/strong>Non utilizzo Prometheus5<\/p>\n<\/li>\n<li class=\"poll-result__item\">\n<p>                <strong class=\"poll-result__data-percent\">28,6%<\/strong>Non sapevo di VictoriaMetrics2<\/p>\n<\/li>\n<\/ul>\n<p>    Hanno votato 7 utenti. 12 utenti si sono astenuti.<br \/>\n<br \/>Fonte: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/500844\/\">habr.com<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u043a\u043e\u043d\u0446\u0430 2019 \u0433\u043e\u0434\u0430 \u0410\u043b\u0435\u043a\u0441\u0430\u043d\u0434\u0440\u0430 \u0412\u0430\u043b\u044f\u043b\u043a\u0438\u043d\u0430 &quot;Go optimizations in VictoriaMetrics&quot; VictoriaMetrics \u2014 \u0431\u044b\u0441\u0442\u0440\u0430\u044f \u0438 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u0443\u0435\u043c\u0430\u044f \u0421\u0423\u0411\u0414 \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u0444\u043e\u0440\u043c\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0433\u043e \u0440\u044f\u0434\u0430 (\u0437\u0430\u043f\u0438\u0441\u044c \u043e\u0431\u0440\u0430\u0437\u0443\u0435\u0442 \u0432\u0440\u0435\u043c\u044f \u0438 \u043d\u0430\u0431\u043e\u0440 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u044d\u0442\u043e\u043c\u0443 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0445 \u0447\u0435\u0440\u0435\u0437 \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043e\u043f\u0440\u043e\u0441 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0434\u0430\u0442\u0447\u0438\u043a\u043e\u0432 \u0438\u043b\u0438 \u0441\u0431\u043e\u0440 \u043c\u0435\u0442\u0440\u0438\u043a). \u0412\u043e\u0442 \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u0432\u0438\u0434\u0435\u043e \u044d\u0442\u043e\u0433\u043e \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u2014 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":80734,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-80733","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.0.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u043a\u043e\u043d\u0446\u0430 2019 \u0433\u043e\u0434\u0430 \u0410\u043b\u0435\u043a\u0441\u0430\u043d\u0434\u0440\u0430 \u0412\u0430\u043b\u044f\u043b\u043a\u0438\u043d\u0430 &quot;Go optimizations in VictoriaMetrics&quot;\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/go-optimizations-in-victoriametrics-aleksandr-valyalkin\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.0.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"it_IT\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47Go optimizations in VictoriaMetrics. \u0410\u043b\u0435\u043a\u0441\u0430\u043d\u0434\u0440 \u0412\u0430\u043b\u044f\u043b\u043a\u0438\u043d | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u043a\u043e\u043d\u0446\u0430 2019 \u0433\u043e\u0434\u0430 \u0410\u043b\u0435\u043a\u0441\u0430\u043d\u0434\u0440\u0430 \u0412\u0430\u043b\u044f\u043b\u043a\u0438\u043d\u0430 &quot;Go optimizations in VictoriaMetrics&quot;\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/go-optimizations-in-victoriametrics-aleksandr-valyalkin\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2020-05-08T11:42:31+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-05-08T11:42:31+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Ottimizzazioni in Go in VictoriaMetrics. Alexander Valjal'kin | ProHoster","description":"Ti invito a dare un'occhiata alla trascrizione della presentazione di fine 2019 di Aleksandr Valyalkin \"Ottimizzazioni di Go in VictoriaMetrics\"","canonical_url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/go-optimizations-in-victoriametrics-aleksandr-valyalkin","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"it_IT","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47Go optimizations in VictoriaMetrics. \u0410\u043b\u0435\u043a\u0441\u0430\u043d\u0434\u0440 \u0412\u0430\u043b\u044f\u043b\u043a\u0438\u043d | ProHoster","og:description":"\u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u043a\u043e\u043d\u0446\u0430 2019 \u0433\u043e\u0434\u0430 \u0410\u043b\u0435\u043a\u0441\u0430\u043d\u0434\u0440\u0430 \u0412\u0430\u043b\u044f\u043b\u043a\u0438\u043d\u0430 &quot;Go optimizations in VictoriaMetrics&quot;","og:url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/go-optimizations-in-victoriametrics-aleksandr-valyalkin","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2020-05-08T11:42:31+00:00","article:modified_time":"2020-05-08T11:42:31+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"80733","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":null,"breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 16:12:22","updated":"2022-09-29 05:16:51","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/80733","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/comments?post=80733"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/80733\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media\/80734"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media?parent=80733"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/categories?post=80733"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/tags?post=80733"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}