{"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\/ro\/blog\/administrirovanie\/go-optimizations-in-victoriametrics-aleksandr-valyalkin","title":{"rendered":"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><strong>V\u0103 propun s\u0103 consulta\u021bi transcrierea raportului din sf\u00e2r\u0219itul anului 2019 al lui Alexandr Valyalkin \"Optimizarile Go \u00een VictoriaMetrics\"<\/strong><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/victoriametrics.com\/\">VictoriaMetrics<\/a><\/noindex> \u2014 un DBMS rapid \u0219i scalabil pentru stocarea \u0219i procesarea datelor sub form\u0103 de serii temporale (\u00eenregistrarea formeaz\u0103 timpul \u0219i un set de valori corespunz\u0103toare acestui timp, de exemplu, ob\u021binute prin interogarea periodic\u0103 a st\u0103rii senzorilor sau colectarea de metrici).<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander 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>Iat\u0103 linkul c\u0103tre videoclipul acestei prezent\u0103ri \u2014 <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\">Slide-uri<\/a><\/noindex><\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/0073390d6dbcc6ab3e5305907ac6a229.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Voi spune c\u00e2teva cuvinte despre mine. Eu sunt Alexander Valyalkin. Iat\u0103 <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/valyala\">contul meu de GitHub<\/a><\/noindex>. M\u0103 pasioneaz\u0103 Go \u0219i optimizarea performan\u021bei. Am scris multe biblioteci utile \u0219i unele mai pu\u021bin utile. Acestea \u00eencep fie cu <code>fast<\/code>, fie cu <code>quick<\/code> prefix. <\/p>\n<p><\/p>\n<p>\u00cen prezent, lucrez la VictoriaMetrics. Ce este asta \u0219i ce fac eu acolo? Despre acestea voi vorbi \u00een aceast\u0103 prezentare. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/c9194bc3fee1f615d838979bec12f7d0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Planul prezent\u0103rii este urm\u0103torul:<\/p>\n<p><\/p>\n<ul>\n<li>La \u00eenceput, v\u0103 voi explica ce este VictoriaMetrics. <\/li>\n<li>Apoi voi explica ce sunt seriile temporale. <\/li>\n<li>Apoi voi vorbi despre cum func\u021bioneaz\u0103 o baz\u0103 de date pentru serii temporale.<\/li>\n<li>Mai departe, voi vorbi despre arhitectura bazei de date: din ce este alc\u0103tuit\u0103.<\/li>\n<li>\u0218i apoi vom trece la optimiz\u0103rile care exist\u0103 \u00een VictoriaMetrics. Acestea sunt optimizarea indexului invers \u0219i optimizarea pentru implementarea bitset \u00een Go.<\/li>\n<\/ul>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/3e0244b6b6fe952f660e4a094778921a.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>\u0218tie cineva din audien\u021b\u0103 ce este VictoriaMetrics? Wow, deja mul\u021bi oameni \u0219tiu. Asta este o veste bun\u0103. Pentru cei care nu \u0219tiu \u2013 aceasta este o baz\u0103 de date pentru serii temporale. Este bazat\u0103 pe arhitectura ClickHouse, pe unele detalii de implementare ale ClickHouse. De exemplu, pe aspecte precum: MergeTree, calcul paralel pe toate nucleele disponibile ale procesorului \u0219i optimizarea performan\u021bei prin lucrul cu blocuri de date care sunt plasate \u00een cache-ul procesorului. <\/p>\n<p><\/p>\n<p>VictoriaMetrics ofer\u0103 cea mai bun\u0103 compresie a datelor comparativ cu alte baze de date pentru serii temporale. <\/p>\n<p><\/p>\n<p>Se scaleaz\u0103 vertical \u2014 adic\u0103 pute\u021bi ad\u0103uga mai multe procesoare, mai mult\u0103 memorie RAM pe un singur computer. VictoriaMetrics va utiliza cu succes aceste resurse disponibile \u0219i va cre\u0219te performan\u021ba liniar\u0103.<\/p>\n<p><\/p>\n<p>De asemenea, VictoriaMetrics se scaleaz\u0103 orizontal \u2014 adic\u0103 pute\u021bi ad\u0103uga noduri suplimentare \u00een clusterul VictoriaMetrics, iar performan\u021ba sa va cre\u0219te aproape liniar.<\/p>\n<p><\/p>\n<p>Dup\u0103 cum a\u021bi ghicit, VictoriaMetrics este o baz\u0103 de date rapid\u0103, deoarece nu pot scrie despre altele. \u0218i este scris\u0103 \u00een Go, a\u0219a c\u0103 vorbesc despre ea la acest meetup.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/0121c9baead722eb1fe22fb6f900d4ad.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Cine \u0219tie ce este un \u0219ir temporar? Multe persoane \u0219tiu, de asemenea. Un \u0219ir temporar este o serie de perechi <code>(timestamp, valoare)<\/code>, unde aceste perechi sunt sortate \u00een func\u021bie de timp. Valoarea reprezint\u0103 un num\u0103r \u00een virgul\u0103 mobil\u0103 \u2013 float64.<\/p>\n<p><\/p>\n<p>Fiecare \u0219ir temporar este identificat \u00een mod unic printr-o cheie. Din ce este format\u0103 aceast\u0103 cheie? Este format\u0103 dintr-un set nevid de perechi cheie-valoare. <\/p>\n<p><\/p>\n<p>Iat\u0103 un exemplu de \u0219ir temporar. Cheia acestui \u0219ir este o list\u0103 de perechi: <code>__name__=\"cpu_usage\"<\/code> \u2013 acesta este numele metricii, <code>instance=\"my-server\"<\/code> \u2014 acesta este computerul pe care aceast\u0103 metric\u0103 a fost colectat\u0103, <code>datacenter=\"us-east\"<\/code> \u2014 acesta este data center-ul unde se afl\u0103 acest computer.<\/p>\n<p><\/p>\n<p>Am ob\u021binut un nume de \u0219ir temporar format din trei perechi cheie-valoare. Aceast\u0103 cheie corespunde unei liste de perechi <code>(timestamp, value)<\/code>. <code>t1, t2, t3, ..., tN<\/code> \u2014 acestea sunt timestampurile, <code>10, 20, 12, ..., 15<\/code> \u2014 valorile corespunz\u0103toare. Aceasta este utilizarea CPU \u00een acel moment pentru acest \u0219ir.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/76cab9db0263318d355ac607ceb7e0f6.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Unde pot fi folosite \u0219irurile temporale? Are cineva idei? <\/p>\n<p><\/p>\n<ul>\n<li>\u00cen DevOps, se pot m\u0103sura citirile \u00eenc\u0103rc\u0103rii CPU, RAM, re\u021belei, rps, num\u0103rul de erori etc. <\/li>\n<li>IoT \u2013 putem m\u0103sura temperatura, presiunea, coordonatele geoce \u0219i altele.<\/li>\n<li>De asemenea, \u00een finan\u021be \u2013 putem monitoriza pre\u021burile la diferite ac\u021biuni \u0219i valute. <\/li>\n<li>\u00cen plus, \u0219irurile temporale pot fi folosite \u00een monitorizarea proceselor de produc\u021bie \u00een fabrici. Avem utilizatori care folosesc VictoriaMetrics pentru monitorizarea turbinelor eoliene, pentru robo\u021bi.<\/li>\n<li>De asemenea, \u0219irurile temporale sunt utile pentru colectarea informa\u021biilor de la senzori ai diferitelor dispozitive. De exemplu, pentru motor; pentru m\u0103surarea presiunii \u00een anvelope; pentru m\u0103surarea vitezei, distan\u021bei; pentru m\u0103surarea consumului de benzin\u0103 etc.<\/li>\n<li>De asemenea, \u0219irurile temporale pot fi folosite pentru monitorizarea avioanelor. Fiecare avion are o cutie neagr\u0103 care colecteaz\u0103 \u0219iruri temporale pentru diferi\u021bi parametri de s\u0103n\u0103tate ai avionului. \u0218irurile temporale sunt utilizate \u0219i \u00een industria aerospa\u021bial\u0103. <\/li>\n<li>Sistemul de s\u0103n\u0103tate \u2013 este vorba despre tensiunea arterial\u0103, pulsul etc.<\/li>\n<\/ul>\n<p><\/p>\n<p>Poate exist\u0103 \u0219i alte aplica\u021bii despre care am uitat, dar sper c\u0103 a\u021bi \u00een\u021beles c\u0103 \u0219irurile temporale sunt folosite activ \u00een lumea modern\u0103. \u0218i volumul utiliz\u0103rii lor cre\u0219te de la an la an.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/f1da29d372163751268b6a8f86836d04.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>De ce este nevoie de o baz\u0103 de date pentru \u0219irurile temporale? De ce nu se poate folosi o baz\u0103 de date rela\u021bional\u0103 obi\u0219nuit\u0103 pentru stocarea \u0219irurilor temporale?<\/p>\n<p><\/p>\n<p>Pentru c\u0103 \u00een seriile temporale de obicei exist\u0103 un volum mare de informa\u021bii, ceea ce face dificil\u0103 stocarea \u0219i procesarea acestora \u00een baze de date obi\u0219nuite. De aceea au ap\u0103rut baze de date specializate pentru series temporale. Aceste baze stocheaz\u0103 eficient punctele <code>(timestamp, value)<\/code> cu o cheie specificat\u0103. Ele ofer\u0103 API-uri pentru citirea datelor stocate dup\u0103 cheie, fie o singur\u0103 pereche cheie-valoare, fie mai multe astfel de perechi, fie prin regexp. De exemplu, dac\u0103 dori\u021bi s\u0103 g\u0103si\u021bi utilizarea procesorului tuturor serviciilor dvs. din centrul de date din America, trebuie s\u0103 folosi\u021bi acest tip de pseudo-interogare.<\/p>\n<p><\/p>\n<p>De obicei, baze de date pentru serii temporale ofer\u0103 limbaje de interogare specializate, deoarece SQL nu se potrive\u0219te foarte bine cu seriile temporale. De\u0219i exist\u0103 baze de date care suport\u0103 SQL, acesta nu se potrive\u0219te foarte bine. Limbajele de interogare cum ar fi <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>. Sper c\u0103 cineva a auzit m\u0103car de unul dintre aceste limbaje. Probabil c\u0103 mul\u021bi au auzit de PromQL. Acesta este limbajul de interogare Prometheus.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/59e5b6d82a2f7861077bc5fe34519ee4.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Iat\u0103 cum arat\u0103 arhitectura unei baze de date moderne pentru serii temporale prin exemplul VictoriaMetrics.<\/p>\n<p><\/p>\n<p>Aceasta este format\u0103 din dou\u0103 p\u0103r\u021bi. Este un depozit pentru indexul inversat \u0219i un depozit pentru valorile seriilor temporale. Aceste depozite sunt separate. <\/p>\n<p><\/p>\n<p>C\u00e2nd vine o nou\u0103 \u00eenregistrare \u00een baza de date, ne adres\u0103m mai \u00eent\u00e2i indexului inversat pentru a g\u0103si identificatorul seriei temporale \u00een func\u021bie de setul dat <code>label=value<\/code> pentru aceast\u0103 metric\u0103. G\u0103sim acel identificator \u0219i salv\u0103m valoarea \u00een depozitul de date.<\/p>\n<p><\/p>\n<p>C\u00e2nd vine o interogare pentru extragerea datelor din TSDB, ne uit\u0103m mai \u00eent\u00e2i \u00een indexul inversat. Extragem toate <code>timeseries_ids<\/code> \u00eenregistr\u0103rile care se potrivesc cu acest set de <code>label=value<\/code>. Apoi extragem toate datele necesare din depozitul de date, indexate dup\u0103 <code>timeseries_ids<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/eb9b36fa3c6de2188b97b56ad3839b2c.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>S\u0103 lu\u0103m un exemplu despre cum o baz\u0103 de date pentru serii temporale proceseaz\u0103 o interogare de tip select.<\/p>\n<p><\/p>\n<ul>\n<li>Mai \u00eent\u00e2i, extrage toate <code>timeseries_ids<\/code> din indexul inversat, care con\u021bin perechile specificate <code>label=value<\/code>, sau care \u00eendeplinesc o expresie regulat\u0103 specificat\u0103.<\/li>\n<li>Apoi, extrage toate punctele de date din depozitul de date pe intervalul de timp specificat pentru cele g\u0103site <code>timeseries_ids<\/code>.<\/li>\n<li>Dup\u0103 aceea, baza de date efectueaz\u0103 anumite calcule asupra acestor puncte de date, conform cererii utilizatorului. \u0218i apoi returneaz\u0103 r\u0103spunsul.<\/li>\n<\/ul>\n<p><\/p>\n<p>\u00cen aceast\u0103 prezentare, v\u0103 voi vorbi despre prima parte. Este vorba despre c\u0103utare <code>timeseries_ids<\/code> pe indexul inversat. Pute\u021bi consulta apoi a doua \u0219i a treia parte <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/VictoriaMetrics\/VictoriaMetrics\">sursele VictoriaMetrics<\/a><\/noindex>, sau a\u0219tepta\u021bi s\u0103 preg\u0103tesc alte prezent\u0103ri \ud83d\ude42<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/3a7dab707dd1defb9bc674a342052a03.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>S\u0103 \u00eencepem cu indexul inversat. Mul\u021bi ar putea crede c\u0103 este simplu. C\u00e2\u021bi \u0219tiu ce este un index inversat \u0219i cum func\u021bioneaz\u0103? O, nu sunt at\u00e2t de multe persoane. Haide\u021bi s\u0103 \u00eencerc\u0103m s\u0103 \u00een\u021belegem despre ce este vorba. <\/p>\n<p><\/p>\n<p>De fapt, totul este simplu. Este pur \u0219i simplu un dic\u021bionar care mapeaz\u0103 o cheie pe o valoare. Ce este o cheie? Aceast\u0103 pereche <code>label=value<\/code>, unde <code>label<\/code> \u0219i <code>valoare<\/code> \u2014 sunt \u0219iruri. Iar valorile sunt un set <code>timeseries_ids<\/code>, care include perechea dat\u0103 <code>label=value<\/code>.<\/p>\n<p><\/p>\n<p>Indexul inversat permite g\u0103sirea rapid\u0103 a tuturor <code>timeseries_ids<\/code>, care au <code>label=value<\/code>.<\/p>\n<p><\/p>\n<p>\u0218i permite, de asemenea, g\u0103sirea rapid\u0103 <code>timeseries_ids<\/code> serii temporale pentru mai multe perechi <code>label=value<\/code>, sau pentru perechi <code>label=regexp<\/code>. Cum se \u00eent\u00e2mpl\u0103 asta? Prin identificarea intersec\u021biei mul\u021bimii <code>timeseries_ids<\/code> pentru fiecare pereche <code>label=value<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/837a15a078e8c9422c721f3f072c0c09.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>S\u0103 analiz\u0103m diferitele implement\u0103ri ale indexului inversat. S\u0103 \u00eencepem cu cea mai simpl\u0103 implementare naiv\u0103. Arat\u0103 a\u0219a. <\/p>\n<p><\/p>\n<p>Func\u021bia <code>getMetricIDs<\/code> prime\u0219te o list\u0103 de \u0219iruri. Fiecare \u0219ir con\u021bine <code>label=value<\/code>. Aceast\u0103 func\u021bie returneaz\u0103 o list\u0103 <code>metricIDs<\/code>.<\/p>\n<p><\/p>\n<p>Cum func\u021bioneaz\u0103 asta? Avem o variabil\u0103 global\u0103 numit\u0103 <code>invertedIndex<\/code>. Este un dic\u021bionar obi\u0219nuit (<code>map<\/code>), care mapeaz\u0103 un \u0219ir pe un slice de int-uri. \u0218irul con\u021bine <code>label=value<\/code>.<\/p>\n<p><\/p>\n<p>Implementarea func\u021biei: ob\u021binem <code>metricIDs<\/code> pentru primul <code>label=value<\/code>, apoi parcurgem toate celelalte <code>label=value<\/code>, ob\u021binem <code>metricIDs<\/code> pentru ele. \u0218i apel\u0103m func\u021bia <code>intersectInts<\/code>, despre care va fi vorba mai departe. Aceast\u0103 func\u021bie returneaz\u0103 intersec\u021bia acestor liste.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/709beca79884a9693098781974c26f4d.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Dup\u0103 cum vede\u021bi, implementarea indexului inversat nu este foarte complicat\u0103. Dar aceasta este o implementare naiv\u0103. Ce dezavantaje are? Principalul dezavantaj al implement\u0103rii naive este c\u0103 acest index inversat este stocat \u00een memoria RAM. Dup\u0103 repornirea aplica\u021biei, pierdem acest index. Nu exist\u0103 salvarea acestui index pe disc. Pentru o baz\u0103 de date, un astfel de index inversat este pu\u021bin potrivit.<\/p>\n<p><\/p>\n<p>Al doilea dezavantaj este, de asemenea, legat de memorie. Indexul inversat trebuie s\u0103 \u00eencap\u0103 \u00een memoria RAM. Dac\u0103 dep\u0103\u0219e\u0219te dimensiunea memoriei RAM, este evident c\u0103 vom ob\u021bine \u2013 eroare de memorie insuficient\u0103. \u0218i programul nu va func\u021biona.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/495394168ac03aa8cbcf56ccad1cb2b8.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Aceast\u0103 problem\u0103 poate fi rezolvat\u0103 folosind solu\u021bii gata f\u0103cute, cum ar fi <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/google\/leveldb\">LevelDB<\/a><\/noindex>, sau <noindex><a rel=\"nofollow\" href=\"https:\/\/rocksdb.org\/\">RocksDB<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>Pe scurt, avem nevoie de o baz\u0103 de date care s\u0103 permit\u0103 realizarea rapid\u0103 a trei opera\u021bii. <\/p>\n<p><\/p>\n<ul>\n<li>Prima opera\u021bie \u2013 aceasta este \u00eenregistrarea <code>cheie-valoare<\/code> \u00een aceast\u0103 baz\u0103 de date. O face foarte repede, unde <code>cheie-valoare<\/code> \u2013 sunt \u0219iruri arbitrare. <\/li>\n<li>A doua opera\u021bie \u2013 este c\u0103utarea rapid\u0103 a valorii dup\u0103 cheia specificat\u0103.<\/li>\n<li>\u0218i a treia opera\u021bie \u2013 este c\u0103utarea rapid\u0103 a tuturor valorilor dup\u0103 un anumit prefix. <\/li>\n<\/ul>\n<p><\/p>\n<p>LevelDB \u0219i RocksDB \u2013 aceste baze au fost dezvoltate de Google \u0219i Facebook. La \u00eenceput a ap\u0103rut LevelDB. Apoi, b\u0103ie\u021bii de la Facebook au preluat LevelDB \u0219i au \u00eenceput s\u0103 o \u00eembun\u0103t\u0103\u021beasc\u0103, cre\u00e2nd RocksDB. Acum, \u00een Facebook, aproape toate bazele de date interne lucreaz\u0103 pe RocksDB, inclusiv au migrat MySQL pe RocksDB. L-au numit <noindex><a rel=\"nofollow\" href=\"http:\/\/myrocks.io\/\">MyRocks<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>Un index inversat poate fi implementat folosind LevelDB. Cum se face asta? Salv\u0103m ca \u0219i cheie <code>label=value<\/code>. Iar ca \u0219i valoare \u2013 identificatorul seriei temporale \u00een care exist\u0103 perechea <code>label=value<\/code>.<\/p>\n<p><\/p>\n<p>Dac\u0103 avem multe serii temporale cu aceast\u0103 pereche <code>label=value<\/code>, atunci vor exista multe r\u00e2nduri \u00een aceast\u0103 baz\u0103 de date cu aceea\u0219i cheie \u0219i valori diferite. <code>timeseries_ids<\/code>. Pentru a ob\u021bine o list\u0103 cu toate <code>timeseries_ids<\/code>, care \u00eencep cu <code>label=prefix<\/code>, facem o scanare de interval, pentru care aceast\u0103 baz\u0103 de date este optimizat\u0103. Adic\u0103, alegem toate r\u00e2ndurile care \u00eencep cu <code>label=prefix<\/code> \u0219i ob\u021binem valorile necesare. <code>timeseries_ids<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/a40877ecb758ac3bc1979bcd568d7e83.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Iat\u0103 o implementare aproximativ\u0103, cum ar ar\u0103ta \u00een Go. Avem un index inversat. Aceasta este LevelDB.<\/p>\n<p><\/p>\n<p>Func\u021bia este aceea\u0219i ca pentru implementarea naiv\u0103. Se repet\u0103 aproape linie cu linie implementarea naiv\u0103. Singurul moment este c\u0103, \u00een loc s\u0103 apel\u0103m la <code>map<\/code> , apel\u0103m la indexul inversat. Extragem toate valorile pentru prima <code>label=value<\/code>. Apoi parcurgem toate perechile r\u0103mase <code>label=value<\/code> \u0219i extragem seturile corespunz\u0103toare de metricIDs pentru acestea. Apoi g\u0103sim intersec\u021bia. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/de11b15286816ada04b22727ff78e43d.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Se pare c\u0103 totul este \u00een regul\u0103, dar \u00een aceast\u0103 solu\u021bie exist\u0103 dezavantaje. VictoriaMetrics a implementat ini\u021bial un index inversat pe baza LevelDB. Dar, \u00een final, a fost nevoie s\u0103 renun\u021be la el.<\/p>\n<p><\/p>\n<p>De ce? Pentru c\u0103 LevelDB este mai lent dec\u00e2t implementarea naiv\u0103. \u00cen implementarea naiv\u0103, pentru cheia specificat\u0103, extragem imediat \u00eentregul slice <code>metricIDs<\/code>. Aceasta este o opera\u021bie foarte rapid\u0103 \u2013 \u00eentregul slice este gata pentru utilizare.<\/p>\n<p><\/p>\n<p>\u00cen LevelDB, \u00eens\u0103, la fiecare apel al func\u021biei <code>GetValues<\/code> trebuie s\u0103 parcurgem toate r\u00e2ndurile care \u00eencep cu <code>label=value<\/code>. \u0218i pentru fiecare r\u00e2nd, trebuie s\u0103 extragem valoarea <code>timeseries_ids<\/code>. Din aceste <code>timeseries_ids<\/code> colect\u0103m un slice acestor <code>timeseries_ids<\/code>. Este evident c\u0103 este mult mai lent dec\u00e2t simpla accesare a unui map obi\u0219nuit dup\u0103 cheie.<\/p>\n<p><\/p>\n<p>A doua problem\u0103 este c\u0103 LevelDB este scris \u00een C. Apelurile la func\u021biile C din Go nu sunt foarte rapide. Acestea dureaz\u0103 sute de nanosecunde. Nu este foarte rapid, deoarece, comparativ cu un apel obi\u0219nuit al unei func\u021bii scrise \u00een Go, care dureaz\u0103 1-5 nanosecunde, diferen\u021ba de performan\u021b\u0103 este de zeci de ori. Pentru VictoriaMetrics, aceasta a fost o problem\u0103 fatal\u0103 \ud83d\ude42<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/47cace825807b7b062064c2e790dc9d3.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>De aceea, am scris propria implementare a unui index invers. \u0218i l-am numit <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/VictoriaMetrics\/VictoriaMetrics\/tree\/master\/lib\/mergeset\">mergeset<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>Mergeset este bazat pe structura de date MergeTree. Aceast\u0103 structur\u0103 de date este \u00eemprumutat\u0103 din ClickHouse. Este clar c\u0103 mergeset trebuie optimizat pentru c\u0103ut\u0103ri rapide <code>timeseries_ids<\/code> dup\u0103 o cheie dat\u0103. Mergeset este scris complet \u00een Go. Pute\u021bi consulta <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/VictoriaMetrics\/VictoriaMetrics\">surs\u0103 VictoriaMetrics pe GitHub<\/a><\/noindex>. Implementarea mergeset se afl\u0103 \u00een folderul <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/VictoriaMetrics\/VictoriaMetrics\/tree\/master\/lib\/mergeset\">\/lib\/mergeset<\/a><\/noindex>. Pute\u021bi \u00eencerca s\u0103 \u00een\u021belege\u021bi ce se \u00eent\u00e2mpl\u0103 acolo.<\/p>\n<p><\/p>\n<p>API-ul mergeset este foarte similar cu LevelDB \u0219i RocksDB. Adic\u0103, permite salvarea rapid\u0103 a noilor \u00eenregistr\u0103ri \u0219i selectarea rapid\u0103 a \u00eenregistr\u0103rilor dup\u0103 un prefix dat.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/845704ed36f70beb468907d22701af59.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Vom discuta despre dezavantajele mergeset mai t\u00e2rziu. Acum, s\u0103 discut\u0103m despre problemele \u00eent\u00e2mpinate cu VictoriaMetrics \u00een produc\u021bie la implementarea indexului invers.<\/p>\n<p><\/p>\n<p>De ce au ap\u0103rut?<\/p>\n<p><\/p>\n<p>Prima cauz\u0103 este rata mare de schimbare. \u00cen traducere, aceasta \u00eenseamn\u0103 o schimbare frecvent\u0103 a seriilor temporale. Este cazul c\u00e2nd o serie temporal\u0103 se \u00eencheie \u0219i \u00eencepe una nou\u0103 sau c\u00e2nd \u00eencep multe serii temporale noi. \u0218i acest lucru se \u00eent\u00e2mpl\u0103 frecvent.<\/p>\n<p><\/p>\n<p>A doua cauz\u0103 este num\u0103rul mare de serii temporale. La \u00eenceput, c\u00e2nd monitorizarea c\u00e2\u0219tiga popularitate, num\u0103rul de serii temporale era mic. De exemplu, pentru fiecare computer era necesar s\u0103 monitoriz\u0103m \u00eenc\u0103rcarea procesorului, memoriei, re\u021belei \u0219i discului. 4 serii temporale pentru fiecare computer. A\u021bi avut, s\u0103 zicem, 100 de computere \u0219i 400 de serii temporale. Este foarte pu\u021bin. <\/p>\n<p><\/p>\n<p>Pe parcursul timpului, oamenii au venit cu ideea de a m\u0103sura informa\u021bii mai detaliate. De exemplu, s\u0103 m\u0103sur\u0103m \u00eenc\u0103rcarea nu doar a \u00eentregului procesor, ci separat pentru fiecare nucleu de procesor. Dac\u0103 ave\u021bi 40 de nuclee de procesor, atunci, \u00een mod corespunz\u0103tor, ave\u021bi 40 de ori mai multe serii temporale pentru m\u0103surarea \u00eenc\u0103rc\u0103rii procesorului. <\/p>\n<p><\/p>\n<p>Dar aceasta nu este tot. Fiecare nucleu de procesor poate avea mai multe st\u0103ri, cum ar fi idle, c\u00e2nd este inactiv. De asemenea, exist\u0103 activitatea \u00een user space, activitatea \u00een kernel space \u0219i alte st\u0103ri. \u0218i fiecare dintre aceste st\u0103ri poate fi, de asemenea, m\u0103surat\u0103 ca o serie temporal\u0103 separat\u0103. Aceasta cre\u0219te suplimentar num\u0103rul seriilor cu 7-8 ori.<\/p>\n<p><\/p>\n<p>Dintr-o singur\u0103 metric\u0103 am ob\u021binut 40 x 8 = 320 de metrici doar pentru un computer. \u00cenmul\u021bim cu 100, ob\u021binem 32 000 \u00een loc de 400. <\/p>\n<p><\/p>\n<p>Apoi a ap\u0103rut Kubernetes. \u0218i a \u00eengreunat lucrurile, pentru c\u0103 \u00een Kubernetes pot fi g\u0103zduite multe servicii diferite. Fiecare serviciu \u00een Kubernetes este compus din multe poduri. Tot acest lucru trebuie monitorizat. Pe l\u00e2ng\u0103 aceasta, avem un deployment constant de noi versiuni ale serviciilor voastre. Pentru fiecare nou\u0103 versiune trebuie s\u0103 cre\u0103m noi serii temporale. \u00cen cele din urm\u0103, num\u0103rul seriilor temporale cre\u0219te exponential \u0219i ne confrunt\u0103m cu problema unui num\u0103r mare de serii temporale, numit\u0103 high-cardinality. VictoriaMetrics face fa\u021b\u0103 cu succes acestei probleme comparativ cu alte baze de date pentru serii temporale. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/c11b7ce6d3294ae420243f72636af4b9.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>S\u0103 analiz\u0103m mai \u00een detaliu high churn rate. De ce apare high churn rate \u00een produc\u021bie? Pentru c\u0103 unele valori ale etichetelor \u0219i tagurilor se schimb\u0103 constant.<\/p>\n<p><\/p>\n<p>De exemplu, s\u0103 lu\u0103m Kubernetes, \u00een care exist\u0103 no\u021biunea <code>deployment<\/code>, adic\u0103 atunci c\u00e2nd este lansat\u0103 o nou\u0103 versiune a aplica\u021biei dumneavoastr\u0103. Dezvoltatorii Kubernetes au decis dintr-un motiv oarecare s\u0103 adauge ID-ul deployment-ului \u00een etichet\u0103.<\/p>\n<p><\/p>\n<p>Ce a dus la aceasta? La faptul c\u0103 la fiecare nou deployment, toate vechile serii temporale sunt \u00eentrerupte, \u0219i \u00een locul lor \u00eencep noi serii temporale cu o nou\u0103 valoare a etichetei. <code>deployment_id<\/code>. Astfel de serii pot fi sute de mii \u0219i chiar milioane.<\/p>\n<p><\/p>\n<p>O caracteristic\u0103 important\u0103 a tuturor acestor aspecte este c\u0103 num\u0103rul total de serii temporale cre\u0219te, dar num\u0103rul seriilor temporale care sunt \u00een prezent active, pentru care vin date, r\u0103m\u00e2ne constant. Aceast\u0103 stare este numit\u0103 \u2013 high churn rate.<\/p>\n<p><\/p>\n<p>Principala problem\u0103 a high churn rate este de a asigura o vitez\u0103 constant\u0103 de c\u0103utare pentru toate serii temporale pe baza unui set dat de etichete pe o anumit\u0103 perioad\u0103 de timp. De obicei, aceast\u0103 perioad\u0103 de timp este ultima or\u0103 sau ultima zi. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/ac18482b87cc37624d163b30c68cf7be.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Cum putem rezolva aceast\u0103 problem\u0103? Iat\u0103 prima variant\u0103. Aceasta este de a \u00eemp\u0103r\u021bi indexul inversat \u00een p\u0103r\u021bi independente \u00een func\u021bie de timp. Adic\u0103, trece un anumit interval de timp, termin\u0103m lucrul cu indexul inversat curent \u0219i cre\u0103m un nou index inversat. Trec iar un alt interval de timp, cre\u0103m \u00eenc\u0103 unul \u0219i tot a\u0219a. <\/p>\n<p><\/p>\n<p>\u0218i \u00een timpul extragerii din aceste indexuri inversate, g\u0103sim un set de indexuri inversate care intr\u0103 \u00een intervalul specificat. \u0218i, \u00een consecin\u021b\u0103, alegem de acolo id-urile seriilor temporale. <\/p>\n<p><\/p>\n<p>Aceasta permite economisirea resurselor, deoarece nu trebuie s\u0103 examin\u0103m p\u0103r\u021bile care nu se \u00eencadreaz\u0103 \u00een intervalul specificat. Adic\u0103, de obicei, dac\u0103 alegem datele din ultima or\u0103, atunci pentru intervalele temporale precedente, s\u0103rim peste cereri. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/1afa3a88caa7423941ae3494b9cec706.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Exist\u0103 \u0219i o alt\u0103 variant\u0103 de solu\u021bionare a acestei probleme. Aceasta este de a p\u0103stra pentru fiecare zi o list\u0103 separat\u0103 de id-uri ale seriilor temporale care s-au \u00eent\u00e2lnit \u00een acea zi.<\/p>\n<p><\/p>\n<p>Avantajul acestei solu\u021bii fa\u021b\u0103 de solu\u021bia anterioar\u0103 este c\u0103 nu duplic\u0103m informa\u021bia despre seriile temporale care nu dispar \u00een timp. Acestea sunt constant disponibile \u0219i nu se schimb\u0103. <\/p>\n<p><\/p>\n<p>Dezavantajul este c\u0103 o astfel de solu\u021bie este mai complex\u0103 \u00een implementare \u0219i mai dificil\u0103 \u00een depanare. \u0218i VictoriaMetrics a ales aceast\u0103 solu\u021bie. A\u0219a s-a \u00eent\u00e2mplat istoric. Aceast\u0103 solu\u021bie se dovede\u0219te, de asemenea, destul de bun\u0103, comparativ cu cea anterioar\u0103. Deoarece aceast\u0103 solu\u021bie nu a fost implementat\u0103 din cauza nevoii de a dubla datele \u00een fiecare partition pentru seriile temporale care nu se schimb\u0103, adic\u0103 care nu dispar \u00een timp. VictoriaMetrics a fost \u00een primul r\u00e2nd optimizat\u0103 pentru consumul de spa\u021biu pe disc, iar implementarea anterioar\u0103 a deteriorat consumul de spa\u021biu pe disc. Iar aceast\u0103 implementare se potrive\u0219te mai bine pentru minimizarea consumului de spa\u021biu pe disc, de aceea a fost aleas\u0103. <\/p>\n<p><\/p>\n<p>A trebuit s\u0103 lupt\u0103m cu ea. Lupta consta \u00een faptul c\u0103, \u00een aceast\u0103 implementare, trebuie totu\u0219i s\u0103 alegem o cantitate mult mai mare <code>timeseries_ids<\/code> pentru date, dec\u00e2t atunci c\u00e2nd indexul inversat este \u00eemp\u0103r\u021bit pe timp.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/5b10e61aaa803428ed3bfe31815f09d3.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Cum am rezolvat aceast\u0103 problem\u0103? Am rezolvat-o \u00eentr-un mod original \u2013 prin salvarea mai multor identificatori de serii temporale \u00een fiecare \u00eenregistrare a indexului inversat, \u00een loc de un singur identificator. Adic\u0103, avem o cheie <code>label=value<\/code>, care apare \u00een fiecare serie temporal\u0103. \u0218i acum p\u0103str\u0103m c\u00e2teva <code>timeseries_ids<\/code> \u00eentr-o singur\u0103 \u00eenregistrare.<\/p>\n<p><\/p>\n<p>Iat\u0103 un exemplu. \u00cen trecut aveam N \u00eenregistr\u0103ri, iar acum avem o singur\u0103 \u00eenregistrare, prefixul acesteia fiind acela\u0219i ca al tuturor celorlalte. \u00cenregistrarea anterioar\u0103 con\u021binea toate ID-urile seriilor temporale. <\/p>\n<p><\/p>\n<p>Acest lucru a permis cre\u0219terea vitezei de scanare a unui astfel de index inversat cu p\u00e2n\u0103 la 10 ori. \u0218i a redus consumul de memorie pentru cache, deoarece acum stoc\u0103m \u0219irul <code>label=value<\/code> doar o singur\u0103 dat\u0103 \u00een cache, \u00eempreun\u0103 cu N ori. Iar acest \u0219ir poate fi mare, dac\u0103 ave\u021bi \u00een etichete \u0219i taguri \u0219iruri lungi pe care Kubernetes le place s\u0103 le adauge.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/ae4f38a440203bad2d03cf2abd1faafe.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>O alt\u0103 op\u021biune pentru accelerarea c\u0103ut\u0103rii \u00een indexul inversat este shardarea. Crearea mai multor indec\u0219i inversa\u021bi \u00een loc de unul singur \u0219i shardarea datelor \u00eentre ace\u0219tia pe baza unei chei. Aceasta este o colec\u021bie <code>cheie=valoare<\/code> de perechi. Adic\u0103, ob\u021binem mai mul\u021bi indec\u0219i inversa\u021bi independen\u021bi, pe care \u00eei putem interoga \u00een paralel pe mai multe procesoare. Implement\u0103rile anterioare permiteau lucru doar \u00een modul uniprocesor, adic\u0103 scanarea datelor doar pe un singur nucleu. Aceast\u0103 solu\u021bie permite scanarea datelor simultan pe mai multe nuclei, a\u0219a cum \u00eei place lui ClickHouse s\u0103 fac\u0103. Acest lucru inten\u021bion\u0103m s\u0103 implement\u0103m.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/5ced6446efbf8ded8d7fb91694a999b0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Acum, s\u0103 ne \u00eentoarcem la subiect \u2013 la func\u021bia de intersec\u021bie <code>timeseries_ids<\/code>. S\u0103 analiz\u0103m ce implement\u0103ri ar putea exista. Aceast\u0103 func\u021bie permite g\u0103sirea <code>timeseries_ids<\/code> pentru un set dat de <code>label=value<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/073a155018a91120c6996c324772f9af.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Prima op\u021biune este implementarea naiv\u0103. Dou\u0103 bucle imbricate. Aici prime\u0219te ca input func\u021bia <code>intersectInts<\/code> dou\u0103 slice-uri\u2014 <code>a<\/code> \u0219i <code>b<\/code>. La ie\u0219ire, ar trebui s\u0103 ne returneze intersec\u021bia acestor slice-uri.<\/p>\n<p><\/p>\n<p>Implementarea naiv\u0103 arat\u0103 astfel. Parcurgem toate valorile din slice <code>a<\/code>, iar \u00een interiorul acestei bucle parcurgem toate valorile din slice <code>b<\/code>. \u0218i le compar\u0103m. Dac\u0103 se potrivesc, \u00eenseamn\u0103 c\u0103 am g\u0103sit intersec\u021bia. \u0218i o salv\u0103m \u00een <code>rezultat<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/b89dcbb4f6d73377f9cf4f50169d6dd7.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Ce dezavantaje exist\u0103? Complexitatea p\u0103tratic\u0103 \u2013 aceasta este principalul ei dezavantaj. De exemplu, dac\u0103 dimensiunile slice-urilor sunt <code>a<\/code> \u0219i <code>b<\/code> de un milion, atunci aceast\u0103 func\u021bie nu \u00ee\u021bi va da niciodat\u0103 un r\u0103spuns. Deoarece va trebui s\u0103 efectueze un trilion de itera\u021bii, ceea ce este foarte mult chiar \u0219i pentru computerele moderne.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/bad26b0092a2fd7fc813bcb79a9138ce.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>A doua implementare se bazeaz\u0103 pe map. Cre\u0103m o map\u0103. Introducem \u00een aceast\u0103 map\u0103 toate valorile din slice <code>a<\/code>. Apoi parcurgem un ciclu separat pe slice <code>b<\/code>. \u0218i verific\u0103m \u2013 exist\u0103 acea valoare din slice <code>b<\/code> \u00een map. Dac\u0103 exist\u0103, atunci \u00eel ad\u0103ug\u0103m \u00een rezultat. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/1e610acf641a9cfe4c80aac1fe26044c.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Care sunt avantajele? Avantajul const\u0103 \u00een faptul c\u0103 aici complexitatea este doar liniar\u0103. Adic\u0103, func\u021bia se va executa de mult mai rapid pentru dimensiuni mari de slices. Pentru un slice de dimensiune un milion, aceast\u0103 func\u021bie se va executa \u00een 2 milioane de itera\u021bii, spre deosebire de trilionul de itera\u021bii, cum era \u00een func\u021bia anterioar\u0103.<\/p>\n<p><\/p>\n<p>Dezavantajul este c\u0103 aceast\u0103 func\u021bie necesit\u0103 mai mult\u0103 memorie pentru a crea acest map.<\/p>\n<p><\/p>\n<p>Al doilea dezavantaj \u2013 este overhead-ul mare pe hash. Acest dezavantaj nu este foarte evident. \u0218i pentru noi nu a fost foarte evident niciodat\u0103, a\u0219a c\u0103 la \u00eenceput implementarea intersec\u021biei \u00een VictoriaMetrics a fost realizat\u0103 prin map. Dar apoi profilarea a ar\u0103tat c\u0103 timpul de procesare al procesorului este cheltuit \u00een principal pe scrierea \u00een map \u0219i pe verificarea existen\u021bei valorii \u00een acest map.<\/p>\n<p><\/p>\n<p>De ce se cheltuie timp de procesor \u00een aceste locuri? Pentru c\u0103 \u00een liniile respective, Go efectueaz\u0103 opera\u021bia de hashing. Adic\u0103, calculeaz\u0103 hash-ul cheii pentru a accesa apoi la indexul specificat din HashMap. Opera\u021bia de calculare a hash-ului se execut\u0103 \u00een zeci de nanosecunde. Este lent pentru VictoriaMetrics.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/969f10d875d88998e958894ca28b1181.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Am decis s\u0103 implementez un bitset, optimizat special pentru acest caz. Iat\u0103 cum arat\u0103 acum intersec\u021bia a dou\u0103 slices. Aici cre\u0103m un bitset. Ad\u0103ug\u0103m \u00een el elementele din primul slice. Apoi verific\u0103m existen\u021ba acestor elemente \u00een al doilea slice. \u0218i le ad\u0103ug\u0103m \u00een rezultat. Adic\u0103, aproape nu se deosebe\u0219te de exemplul anterior. Singurul aspect pe care l-am schimbat este c\u0103 am \u00eenlocuit accesul la map cu func\u021bii personalizate. <code>add<\/code> \u0219i <code>has<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/b4ec0753d30d9684868a031f605632c0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>La prima vedere, pare c\u0103 ar trebui s\u0103 func\u021bioneze mai \u00eencet, dac\u0103 \u00eenainte era folosit un map standard, iar acum sunt apelate \u0219i alte func\u021bii, dar profilarea arat\u0103 c\u0103 aceast\u0103 solu\u021bie func\u021bioneaz\u0103 de 10 ori mai repede dec\u00e2t un map standard pentru cazul cu VictoriaMetrics.<\/p>\n<p><\/p>\n<p>\u00cen plus, folose\u0219te mult mai pu\u021bin\u0103 memorie \u00een compara\u021bie cu implementarea pe map. Pentru c\u0103 aici p\u0103str\u0103m bi\u021bi \u00een loc de valori de 8 bi\u021bi.<\/p>\n<p><\/p>\n<p>Dezavantajul unei astfel de implement\u0103ri este c\u0103 nu este at\u00e2t de evident\u0103, nu este triviial\u0103. <\/p>\n<p><\/p>\n<p>O alt\u0103 deficien\u021b\u0103 pe care mul\u021bi ar putea s\u0103 nu o observe este c\u0103 aceast\u0103 implementare poate func\u021biona slab \u00een anumite cazuri. Adic\u0103, este optimizat\u0103 pentru un caz specific, pentru cazul de intersec\u021bie a id-urilor seriilor temporale \u00een VictoriaMetrics. Aceasta nu \u00eenseamn\u0103 c\u0103 se va potrivi tuturor cazurilor. Dac\u0103 este utilizat\u0103 incorect, vom ob\u021bine nu un c\u00e2\u0219tig de performan\u021b\u0103, ci o eroare de tip out of memory \u0219i o \u00eencetinire a performan\u021bei. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/94bd174afe43e67b3cf279dc7a1be17c.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>S\u0103 analiz\u0103m implementarea acestei structuri. Dac\u0103 dori\u021bi s\u0103 o vizualiza\u021bi, ea se afl\u0103 \u00een sursele VictoriaMetrics, \u00een folderul <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/VictoriaMetrics\/VictoriaMetrics\/tree\/master\/lib\/uint64set\">lib\/uint64set<\/a><\/noindex>. Este optimizat\u0103 special pentru cazul VictoriaMetrics, unde <code>timeseries_id<\/code> reprezint\u0103 o valoare de 64 de bi\u021bi, unde primii 32 de bi\u021bi sunt constan\u021bi, iar doar ultimii 32 de bi\u021bi se schimb\u0103.<\/p>\n<p><\/p>\n<p>Aceast\u0103 structur\u0103 de date nu este stocat\u0103 pe disc, func\u021bioneaz\u0103 doar \u00een memorie. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/e24ca65f860e2b04037e073f7acae0c1.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Iat\u0103 API-ul s\u0103u. Nu este foarte complex. API-ul este adaptat special pentru exemplul de utilizare VictoriaMetrics. Adic\u0103, nu exist\u0103 func\u021bii inutile aici. Aici sunt func\u021biile care sunt utilizate \u00een mod clar de VictoriaMetrics.<\/p>\n<p><\/p>\n<p>Exist\u0103 func\u021bia <code>add<\/code>, care adaug\u0103 valori noi. Exist\u0103 func\u021bia <code>has<\/code>, care verific\u0103 valori noi. \u0218i exist\u0103 func\u021bia <code>del<\/code>, care \u0219terge valori. Exist\u0103 o func\u021bie auxiliar\u0103 <code>len<\/code>, care returneaz\u0103 dimensiunea mul\u021bimii. Func\u021bia <code>clone<\/code> cloneaz\u0103 mul\u021bimea. Iar func\u021bia <code>appendto<\/code> transform\u0103 acest set \u00een slice. <code>timeseries_ids<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/874b04042d13f342d39f8f393e7fb78c.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Iat\u0103 cum arat\u0103 implementarea acestei structuri de date. \u00cen set exist\u0103 doi elementi:<\/p>\n<p><\/p>\n<ul>\n<li>\n<p><code>ItemsCount<\/code> \u2013 este un c\u00e2mp auxiliar, pentru a returna rapid num\u0103rul de elemente din set. S-ar putea s\u0103 se fi descurcat f\u0103r\u0103 acest c\u00e2mp auxiliar, dar a trebuit s\u0103 fie ad\u0103ugat aici, deoarece VictoriaMetrics interogheaz\u0103 frecvent lungimea bitset-ului \u00een algoritmii s\u0103i.<\/p>\n<p>\n<\/li>\n<li>\n<p>Al doilea c\u00e2mp este <code>buckets<\/code>. Acesta este un slice din structura <code>bucket32.<\/code>\u00cen fiecare structur\u0103 se stocheaz\u0103 <code>hi<\/code> c\u00e2mp. Acestea sunt cei 32 de bi\u021bi superiori. \u0218i dou\u0103 slice-uri \u2014 <code>b16his<\/code> \u0219i <code>buckets<\/code> din <code>bucket16<\/code> structuri. <\/p>\n<p>\n<\/li>\n<\/ul>\n<p><\/p>\n<p>Aici sunt stoca\u021bi cei 16 bi\u021bi superiori ai celei de-a doua p\u0103r\u021bi din structura de 64 de bi\u021bi. Iar aici sunt bitset-urile pentru cei 16 bi\u021bi inferiori ai fiec\u0103rui octet. <\/p>\n<p><\/p>\n<p><code>Bucket64<\/code> const\u0103 dintr-un tablou de <code>uint64.<\/code>Lungimea este calculat\u0103 folosind aceste constante. \u00centr-un <code>bucket16<\/code> pot fi stoca\u021bi maxim <code>2^16=65536<\/code> bi\u021bi. Dac\u0103 se \u00eemparte la 8, atunci este 8 kiloby\u021bi. Dac\u0103 se \u00eemparte din nou la 8, atunci este 1000 <code>uint64.<\/code> valori. Adic\u0103, <code>Bucket16<\/code> este o structur\u0103 de 8 kiloby\u021bi. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/1cca4e6bfafa3408a3c95e0118e26d80.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>S\u0103 analiz\u0103m cum este implementat\u0103 una dintre metodele acestei structuri pentru ad\u0103ugarea unei valori noi. <\/p>\n<p><\/p>\n<p>Totul \u00eencepe cu <code>uint64.<\/code> valoare. Calcul\u0103m primii 32 de bi\u021bi, calcul\u0103m urm\u0103torii 32 de bi\u021bi. Parcurgem toate <code>buckets<\/code>. Compar\u0103m primii 32 de bi\u021bi din fiecare bucket cu valoarea ad\u0103ugat\u0103. \u0218i dac\u0103 sunt identici, apel\u0103m func\u021bia <code>add<\/code> din structura b32 <code>buckets<\/code>. \u0218i ad\u0103ug\u0103m acolo urm\u0103torii 32 de bi\u021bi. \u0218i dac\u0103 aceasta a returnat <code>true<\/code>, atunci \u00eenseamn\u0103 c\u0103 am ad\u0103ugat o astfel de valoare acolo \u0219i nu am avut aceast\u0103 valoare \u00eenainte. Dac\u0103 returneaz\u0103 <code>false<\/code>, atunci o astfel de valoare a existat deja. Apoi, cre\u0219tem num\u0103rul de elemente din structur\u0103. <\/p>\n<p><\/p>\n<p>Dac\u0103 nu am g\u0103sit cifra necesar\u0103 <code>bucket<\/code> cu valoarea hi-corect\u0103, atunci apel\u0103m func\u021bia <code>addAlloc<\/code>, care aloc\u0103 un nou <code>bucket<\/code>, ad\u0103ug\u00e2ndu-l \u00een structura bucket.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/c5d1e5de767f47c7c40e01e12b6d0617.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Aceasta este implementarea func\u021biei <code>b32.add<\/code>. Este similar\u0103 cu implementarea anterioar\u0103. Calcul\u0103m primii 16 bi\u021bi, urm\u0103torii 16 bi\u021bi.<\/p>\n<p><\/p>\n<p>Apoi, parcurgem to\u021bi primii 16 bi\u021bi. G\u0103sim corespondente. \u0218i la coincidences apel\u0103m metoda add, pe care o vom analiza pe pagina urm\u0103toare pentru <code>bucket16<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/e98c4ef316f3a894fd7019c3c85696f7.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>\u0218i iat\u0103 cel mai de jos nivel, care trebuie s\u0103 fie maxim optimizat. Calcul\u0103m pentru <code>uint64.<\/code> id valoarea din slice bit, precum \u0219i <code>bitmask<\/code>. Aceasta este masca pentru aceast\u0103 valoare de 64 de bi\u021bi, care poate fi folosit\u0103 pentru a verifica prezen\u021ba acestui bit, sau pentru a-l seta. Verific\u0103m prezen\u021ba acestui bit, \u00eel set\u0103m \u0219i return\u0103m prezen\u021ba. Iat\u0103 o astfel de implementare care ne-a permis s\u0103 acceler\u0103m opera\u021bia de intersec\u021bie a ids-urilor serii temporale de 10 ori comparativ cu h\u0103r\u021bile obi\u0219nuite.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/4dfa5a6142829a3551be22d6c9c3ab92.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>\u00cen VictoriaMetrics, pe l\u00e2ng\u0103 aceast\u0103 optimizare, exist\u0103 multe alte optimiz\u0103ri. Majoritatea acestor optimiz\u0103ri au fost ad\u0103ugate nu doar a\u0219a, ci dup\u0103 profilarea codului \u00een produc\u021bie.<\/p>\n<p><\/p>\n<p>Aceasta este regula principal\u0103 a optimiz\u0103rii \u2013 s\u0103 nu ad\u0103ug\u0103m optimiz\u0103ri presupun\u00e2nd c\u0103 va exista o restric\u021bie, deoarece s-ar putea s\u0103 fie c\u0103 acolo nu exist\u0103 restric\u021bii. Optimizarea de obicei scade calitatea codului. De aceea, este mai bine s\u0103 optimiz\u0103m doar dup\u0103 profilare \u0219i, de preferat, \u00een produc\u021bie, astfel \u00eenc\u00e2t s\u0103 fie date reale. Pentru cei interesa\u021bi, pute\u021bi consulta sursele VictoriaMetrics \u0219i studia alte optimiz\u0103ri care exist\u0103 acolo.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Optimiz\u0103rile Go \u00een VictoriaMetrics. Alexander Valialkin\" src=\"\/wp-content\/uploads\/2020\/05\/7585c4dc782627bac5b649e6a55e2ffb.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><em>Am o \u00eentrebare despre bitset. Foarte asem\u0103n\u0103tor cu implementarea vectorului C++ bool, bitset optimizat. A\u021bi luat implementarea de acolo?<\/em><\/p>\n<p><\/p>\n<p>Nu, nu este a\u0219a. C\u00e2nd am implementat acest bitset, m-am bazat pe cuno\u0219tin\u021bele structurii acestor ids time series, care sunt utilizate \u00een VictoriaMetrics. Structura lor este astfel \u00eenc\u00e2t cei 32 de bi\u021bi superiori sunt \u00een principal constan\u021bi. Cei 32 de bi\u021bi inferiori pot varia. Cu c\u00e2t bitul este mai mic, cu at\u00e2t poate varia mai des. Prin urmare, aceast\u0103 implementare este optimizat\u0103 exact pentru aceast\u0103 structur\u0103 de date. Implementarea C++, at\u00e2t c\u00e2t \u0219tiu, este optimizat\u0103 pentru cazul general. Dac\u0103 faci optimizarea pentru cazul general, \u00eenseamn\u0103 c\u0103 nu va fi cea mai optim\u0103 pentru cazul specific.<\/p>\n<p><\/p>\n<p>\u00ce\u021bi recomand s\u0103 te ui\u021bi \u0219i la prezentarea lui Alexey Milovid. Acum o lun\u0103, a vorbit despre optimiz\u0103rile din ClickHouse pentru specializ\u0103ri specifice. El explic\u0103 exact c\u0103, \u00een general, implementarea C++ sau orice alt\u0103 implementare este adaptat\u0103 pentru a func\u021biona bine \u00een medie, \u00een majoritate. Pot exista cazuri \u00een care s\u0103 func\u021bioneze mai r\u0103u dec\u00e2t o implementare specializat\u0103 pentru cuno\u0219tin\u021bele specifice, a\u0219a cum este cazul nostru, c\u00e2nd \u0219tim c\u0103 cei 32 de bi\u021bi superiori sunt \u00een principal constan\u021bi.<\/p>\n<p><\/p>\n<p><em>Am a doua \u00eentrebare. Care este diferen\u021ba fundamental\u0103 fa\u021b\u0103 de InfluxDB?<\/em><\/p>\n<p><\/p>\n<p>Sunt multe diferen\u021be fundamentale. Dac\u0103 ne referim la performan\u021b\u0103 \u0219i consum de memorie, InfluxDB \u00een teste arat\u0103 un consum de memorie cu 10 ori mai mare pentru time series de \u00eenalt\u0103 cardinalitate, c\u00e2nd sunt multe, de exemplu, milioane. De exemplu, VictoriaMetrics consum\u0103 1 GB pentru un milion de serii active, iar InfluxDB consum\u0103 10 GB. \u0218i aceasta este o diferen\u021b\u0103 semnificativ\u0103. <\/p>\n<p><\/p>\n<p>A doua diferen\u021b\u0103 fundamental\u0103 este c\u0103 InfluxDB utilizeaz\u0103 limbaje de interogare ciudate \u2013 Flux \u0219i InfluxQL. Ele nu sunt foarte convenabile pentru lucrul cu time series comparativ cu <noindex><a rel=\"nofollow\" href=\"https:\/\/medium.com\/@valyala\/promql-tutorial-for-beginners-9ab455142085\">PromQL<\/a><\/noindex>, care este suportat \u00een VictoriaMetrics. PromQL este limbajul de interogare din Prometheus.<\/p>\n<p><\/p>\n<p>\u0218i o alt\u0103 diferen\u021b\u0103 este c\u0103 InfluxDB are un model de date pu\u021bin ciudat, unde fiecare linie poate avea mai multe fields cu un set diferit de tags. Aceste linii sunt \u00eemp\u0103r\u021bite \u00een diverse tabele. Aceste complica\u021bii suplimentare complic\u0103 lucrul ulterior cu aceast\u0103 baz\u0103. Este greu de \u00eentre\u021binut \u0219i \u00een\u021beles.<\/p>\n<p><\/p>\n<p>\u00cen VictoriaMetrics, totul este mult mai simplu. Acolo, fiecare time series reprezint\u0103 un key-value. Valoarea este un set de puncte \u2013 <code>(timestamp, value)<\/code>, iar cheia este un set <code>label=value<\/code>. Nu exist\u0103 nicio separare \u00eentre fields \u0219i measurements. Acest lucru v\u0103 permite s\u0103 selecta\u021bi orice date \u0219i apoi s\u0103 le combina\u021bi, s\u0103 le aduna\u021bi, s\u0103 le sc\u0103de\u021bi, s\u0103 le multiplica\u021bi, s\u0103 le \u00eemp\u0103r\u021bi\u021bi, spre deosebire de InfluxDB, unde calcul\u0103rile \u00eentre diferite serii nu sunt \u00een continuare implementate, din c\u00e2te \u0219tiu. Chiar dac\u0103 ar fi implementate, ar fi dificil, ar trebui s\u0103 scrie\u021bi o mul\u021bime de cod. <\/p>\n<p><\/p>\n<p><em>Am o \u00eentrebare de clarificare. Am \u00een\u021beles eu corect c\u0103 a existat o problem\u0103 despre care a\u021bi men\u021bionat c\u0103 acest index inversat nu \u00eencape \u00een memorie, de aceea exist\u0103 parti\u021bionarea?<\/em><\/p>\n<p><\/p>\n<p>La \u00eenceput, am prezentat o implementare naiv\u0103 a indexului inversat pe un map standard Go. Aceast\u0103 implementare nu este potrivit\u0103 pentru baze de date, deoarece acest index inversat nu este salvat pe disc, iar baza de date trebuie s\u0103 salveze pe disc pentru ca aceste date s\u0103 r\u0103m\u00e2n\u0103 disponibile dup\u0103 o repornire. \u00cen aceast\u0103 implementare, la repornirea aplica\u021biei, indexul inversat va disp\u0103rea. \u0218i ve\u021bi pierde accesul la toate datele, pentru c\u0103 nu ve\u021bi putea s\u0103 le g\u0103si\u021bi. <\/p>\n<p><\/p>\n<p><em>Bun\u0103 ziua! V\u0103 mul\u021bumesc pentru prezentare! M\u0103 numesc Pavel. Sunt de la compania Wildberries. Am c\u00e2teva \u00eentreb\u0103ri pentru dumneavoastr\u0103. Prima \u00eentrebare. Crede\u021bi c\u0103, dac\u0103 a\u021bi fi ales un alt principiu la construirea arhitecturii aplica\u021biei dumneavoastr\u0103 \u0219i a\u021bi fi parti\u021bionat datele \u00een func\u021bie de timp, a\u021bi fi putut s\u0103 face\u021bi intersec\u021bii ale datelor \u00een c\u0103utare, baz\u00e2ndu-v\u0103 doar pe faptul c\u0103 \u00eentr-o parti\u021bie se afl\u0103 datele pentru un anumit interval de timp? Adic\u0103 pentru un singur interval de timp \u0219i nu ar fi trebuit s\u0103 v\u0103 face\u021bi griji c\u0103 ave\u021bi date r\u0103sp\u00e2ndite diferit? \u00centrebarea num\u0103rul 2 \u2014 deoarece implementa\u021bi un astfel de algoritm cu bitset \u0219i tot restul, a\u021bi \u00eencercat s\u0103 utiliza\u021bi instruc\u021biunile procesorului? Poate a\u021bi \u00eencercat astfel de optimiz\u0103ri?<\/em><\/p>\n<p><\/p>\n<p>R\u0103spund imediat la a doua \u00eentrebare. P\u00e2n\u0103 acum nu am ajuns acolo. Dar dac\u0103 va fi nevoie, vom ajunge. Iar prima, care a fost \u00eentrebarea?<\/p>\n<p><\/p>\n<p><em>A\u021bi discutat dou\u0103 scenarii. \u0218i a\u021bi spus c\u0103 a\u021bi ales al doilea cu o implementare mai complex\u0103. \u0218i nu a\u021bi preferat primul, unde datele sunt parti\u021bionate \u00een func\u021bie de timp.<\/em> <\/p>\n<p><\/p>\n<p>Da. \u00cen primul caz, volumul total al indicelui ar fi fost mai mare, deoarece \u00een fiecare parti\u021bie ar fi trebuit s\u0103 stoc\u0103m duplicate de date pentru seriile temporale care continu\u0103 prin toate aceste parti\u021bii. \u0218i dac\u0103 rata de churn a seriilor temporale este mic\u0103, adic\u0103 acelea\u0219i serii sunt folosite constant, atunci \u00een primul caz am fi pierdut mult mai mult \u00een ceea ce prive\u0219te spa\u021biul de stocare comparativ cu al doilea caz.<\/p>\n<p><\/p>\n<p>A\u0219a este \u2013 parti\u021bionarea pe timp este o op\u021biune bun\u0103. O utilizeaz\u0103 Prometheus. Dar \u00een Prometheus exist\u0103 un alt dezavantaj. La unirea acestor fragmente de date, trebuie s\u0103 p\u0103streze \u00een memorie informa\u021biile meta pentru toate etichetele \u0219i seriile temporale. A\u0219adar, dac\u0103 fragmentele de date sunt mari, pe care le une\u0219te, consumul de memorie cre\u0219te foarte mult \u00een timpul unirii, spre deosebire de VictoriaMetrics. La unirea VictoriaMetrics, consumul de memorie este practic inexistent, se consum\u0103 c\u00e2\u021biva kilobi\u021bi, indiferent de dimensiunile fragmentelor de date unite.<\/p>\n<p><\/p>\n<p><em>Algoritmul pe care \u00eel utiliza\u021bi folose\u0219te memorie. Aici sunt notate etichetele seriilor temporale care au valori. Astfel, verifica\u021bi existen\u021ba pereche \u00een un array de date \u0219i \u00een altul. \u0218i \u00een\u021belege\u021bi \u2013 a avut loc un intersect sau nu. De obicei, bazele de date implementeaz\u0103 cursori, iteratori care stocheaz\u0103 starea lor curent\u0103 \u0219i care parcurg datele sortate, astfel \u00eenc\u00e2t ave\u021bi o complexitate simpl\u0103 pentru aceste opera\u021biuni.<\/em> <\/p>\n<p><\/p>\n<p>De ce nu folosim cursori pentru intersec\u021bia datelor?<\/p>\n<p><\/p>\n<p><em>Da.<\/em> <\/p>\n<p><\/p>\n<p>\u00cen LevelDB sau \u00een mergeset avem de fapt linii sortate. Putem s\u0103 ne plimb\u0103m cu un cursor \u0219i s\u0103 g\u0103sim intersec\u021bia. Dar de ce nu folosim? Pentru c\u0103 \u2013 este lent. Pentru c\u0103 cursori implic\u0103 apelarea unei func\u021bii pentru fiecare linie. Apelul unei func\u021bii dureaz\u0103 5 nanosecunde. \u0218i dac\u0103 ave\u021bi 100.000.000 de linii, se dovede\u0219te c\u0103 cheltuim o jum\u0103tate de secund\u0103 doar pentru apelul func\u021biei.<\/p>\n<p><\/p>\n<p><em>Exist\u0103 a\u0219a ceva, da. \u0218i ultima mea \u00eentrebare. \u00centrebarea poate p\u0103rea pu\u021bin ciudat\u0103. De ce, \u00een momentul \u00een care sosesc datele, nu putem calcula toate agregatele necesare \u0219i s\u0103 le salv\u0103m \u00een forma necesar\u0103? De ce s\u0103 p\u0103str\u0103m volume uria\u0219e \u00een sisteme precum VictoriaMetrics, ClickHouse etc., pentru a pierde apoi foarte mult timp cu ele?<\/em><\/p>\n<p><\/p>\n<p><em>Voi da un exemplu pentru a fi mai clar. S\u0103 presupunem c\u0103, cum func\u021bioneaz\u0103 un mic vitezometru de juc\u0103rie? Acesta \u00eenregistreaz\u0103 distan\u021ba parcurs\u0103, adun\u00e2nd constant \u00eentr-o m\u0103rime \u0219i timpul \u00een cealalt\u0103. Apoi \u00eemparte. \u0218i ob\u021bine viteza medie. Po\u021bi face ceva similar. Acumul\u00e2nd \u00een timp toate faptele necesare.<\/em><\/p>\n<p><\/p>\n<p>Bine, am \u00een\u021beles \u00eentrebarea. Exemplul t\u0103u are relevan\u021b\u0103. Dac\u0103 \u0219tii ce agregate sunt necesare, atunci aceasta este cea mai bun\u0103 implementare. Dar problema este c\u0103 oamenii p\u0103streaz\u0103 aceste metrici, unele date \u00een ClickHouse \u0219i nu \u0219tiu \u00eenc\u0103 cum vor aggrega, filtra aceste date \u00een viitor, a\u0219a c\u0103 sunt nevoi\u021bi s\u0103 p\u0103streze toate datele brute. Dar dac\u0103 \u0219tii c\u0103 trebuie s\u0103 calculezi ceva mediu, de ce s\u0103 nu-l calculezi, \u00een loc s\u0103 p\u0103strezi o mul\u021bime de valori brute acolo? Dar asta doar dac\u0103 \u0219tii exact ce ai nevoie.<\/p>\n<p><\/p>\n<p>\u00centre timp, bazele de date pentru stocarea seriilor temporale suport\u0103 calculul agregatelor. De exemplu, Prometheus suport\u0103 <noindex><a rel=\"nofollow\" href=\"https:\/\/prometheus.io\/docs\/prometheus\/latest\/configuration\/recording_rules\/\">reguli de \u00eenregistrare<\/a><\/noindex>. Adic\u0103, acest lucru poate fi realizat dac\u0103 \u0219tii ce agregate \u00ee\u021bi vor fi necesare. \u00cen VictoriaMetrics acest lucru nu exist\u0103 \u00eenc\u0103, dar de obicei este plasat Prometheus \u00eenainte de aceasta, unde se poate face \u00een regulile de \u00eenregistrare.<\/p>\n<p><\/p>\n<p>De exemplu, la locul meu anterior de munc\u0103 a fost necesar s\u0103 se calculeze num\u0103rul de evenimente \u00eentr-un interval mobil \u00een ultima or\u0103. Problema era c\u0103 a trebuit s\u0103 fac o implementare personalizat\u0103 \u00een Go, adic\u0103 un serviciu pentru a calcula acest lucru. Acest serviciu a fost, \u00een cele din urm\u0103, non-trivial, deoarece este complicat de calculat. Implementarea poate fi simpl\u0103 dac\u0103 trebuie s\u0103 calculezi anumite agregate pe intervale fixe de timp. Dac\u0103 vrei s\u0103 calculezi evenimente \u00eentr-un interval mobil, atunci nu este at\u00e2t de simplu cum pare. Cred c\u0103 acest lucru nu este \u00eenc\u0103 implementat \u00een ClickHouse sau \u00een bazele de date pentru serii temporale, deoarece este complicat de realizat.<\/p>\n<p><\/p>\n<p><em>\u0218i \u00eenc\u0103 o \u00eentrebare. Tocmai am discutat despre medie \u0219i mi-am adus aminte c\u0103 a fost odat\u0103 o solu\u021bie numit\u0103 Graphite cu backend-ul Carbon. \u0218i acesta putea s\u0103 reduc\u0103 datele vechi, adic\u0103 s\u0103 lase un punct pe minut, un punct pe or\u0103 etc. \u00cen principiu, este destul de convenabil dac\u0103 avem nevoie de date brute, s\u0103 spunem, pe parcursul unei luni, iar toate celelalte pot fi reduse. Dar Prometheus, VictoriaMetrics nu suport\u0103 aceast\u0103 func\u021bionalitate. Este planificat s\u0103 suporte? Dac\u0103 nu, de ce?<\/em><\/p>\n<p><\/p>\n<p>Mul\u021bumim pentru \u00eentrebare. Utilizatorii no\u0219tri o pun periodic. \u00centreab\u0103 c\u00e2nd vom ad\u0103uga suport pentru reducerea num\u0103rului de date (downsampling). Aici sunt c\u00e2teva probleme. \u00cen primul r\u00e2nd, fiecare utilizator \u00een\u021belege prin <code>downsampling<\/code> ceva diferit: unii doresc s\u0103 ob\u021bin\u0103 un punct arbitrar \u00eentr-un interval dat, al\u021bii doresc valorile maxime, minime sau medii. Dac\u0103 pentru baza dumneavoastr\u0103 de date scriu date multe sisteme, nu pute\u021bi s\u0103 le trata\u021bi pe toate la fel. Poate s\u0103 se dovedeasc\u0103 c\u0103 pentru fiecare sistem trebuie folosit un tip diferit de downsampling. \u0218i asta este complicat de implementat.<\/p>\n<p><\/p>\n<p>\u0218i al doilea aspect este c\u0103 VictoriaMetrics, la fel ca \u0219i ClickHouse, este optimizat\u0103 pentru a lucra cu volume mari de date brute, de aceea poate procesa un miliard de linii \u00een mai pu\u021bin de o secund\u0103, dac\u0103 ave\u021bi multe nuclee \u00een sistemul dumneavoastr\u0103. Scanarea punctelor din seria temporal\u0103 \u00een VictoriaMetrics este de 50.000.000 puncte pe secund\u0103 pe un nucleu. Iar aceast\u0103 performan\u021b\u0103 se scaleaz\u0103 pe nuclee disponibile. Adic\u0103, dac\u0103 ave\u021bi 20 de nuclee, de exemplu, ve\u021bi ob\u021bine scanarea unui miliard de puncte pe secund\u0103. \u0218i aceast\u0103 proprietate a VictoriaMetrics \u0219i ClickHouse reduce necesitatea de downsampling.<\/p>\n<p><\/p>\n<p>O alt\u0103 caracteristic\u0103 este c\u0103 VictoriaMetrics comprim\u0103 eficient aceste date. Comprimarea este \u00een medie \u00eentre 0,4 \u0219i 0,8 bi\u021bi pe punct \u00een produc\u021bie. Fiecare punct este un timestamp + valoare. \u0218i se comprim\u0103 la mai pu\u021bin de un byte \u00een medie. <\/p>\n<p><\/p>\n<p><em>Sergei. Am o \u00eentrebare. Care este cantitatea minim\u0103 de timp pentru scriere?<\/em><\/p>\n<p><\/p>\n<p>O milisecund\u0103. Recent am avut o discu\u021bie cu al\u021bi dezvoltatori de baze de date pentru serii temporale. La ei, cantitatea minim\u0103 de timp este de o secund\u0103. \u00cen Graphite, de exemplu, tot o secund\u0103. \u00cen OpenTSDB, de asemenea, o secund\u0103. \u00cen InfluxDB, precizia este de nanosecunde. \u00cen VictoriaMetrics \u2013 o milisecund\u0103, deoarece \u00een Prometheus este de o milisecund\u0103. \u0218i VictoriaMetrics a fost dezvoltat\u0103 ini\u021bial ca stocare extern\u0103 pentru Prometheus. Dar acum poate salva date \u0219i din alte sisteme. <\/p>\n<p><\/p>\n<p>Persoana cu care am discutat spune c\u0103 au o precizie de o secund\u0103 \u2013 le este suficient, deoarece depinde de tipul de date care sunt salvate \u00een baza de date pentru serii temporale. Dac\u0103 sunt date DevOps sau date de la infrastructur\u0103, unde le colecta\u021bi la un interval de 30 de secunde, \u00eentr-un minut, atunci precizia de o secund\u0103 este suficient\u0103, mai pu\u021bin nu este necesar. Dar dac\u0103 colecta\u021bi aceste date din sistemele de tranzac\u021bionare de \u00eenalt\u0103 frecven\u021b\u0103, atunci este nevoie de precizie de nanosecunde.<\/p>\n<p><\/p>\n<p>Precision in milliseconds with VictoriaMetrics is suitable for both DevOps cases and can work for the majority of the cases I mentioned at the beginning of the presentation. The only exception might be high frequency trading systems.<\/p>\n<p><\/p>\n<p><em>Thank you! And one more question. What is the compatibility in PromQL?<\/em><\/p>\n<p><\/p>\n<p>Complete backward compatibility. VictoriaMetrics fully supports PromQL. Additionally, it introduces extra extended functionality to PromQL called <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/VictoriaMetrics\/VictoriaMetrics\/wiki\/MetricsQL\">MetricsQL<\/a><\/noindex>. There is a presentation on this extended functionality available on YouTube. I spoke about it at the Monitoring Meetup in spring in St. Petersburg.<\/p>\n<p><\/p>\n<p>Telegram channel <noindex><a rel=\"nofollow\" href=\"https:\/\/t.me\/VictoriaMetrics_ru1\">VictoriaMetrics<\/a><\/noindex>.<\/p>\n<p class=\"for_users_only_msg\">Numai utilizatorii \u00eenregistra\u021bi pot participa la sondaj. <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/auth\/login\/\">Conecta\u021bi-v\u0103<\/a><\/noindex>, v\u0103 rug\u0103m.<\/p>\n<h2 class=\"default-block__polling-title\">What prevents you from switching to VictoriaMetrics as a long-term storage solution for Prometheus? (Please write in the comments, I will add it to the survey))<\/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>I'm not using Prometheus5<\/p>\n<\/li>\n<li class=\"poll-result__item\">\n<p>                <strong class=\"poll-result__data-percent\">28,6%<\/strong>I didn't know about VictoriaMetrics2<\/p>\n<\/li>\n<\/ul>\n<p>    7 users voted. 12 users abstained.<br \/>\n<br \/>Sursa: <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.2.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\/ro\/blog\/administrirovanie\/go-optimizations-in-victoriametrics-aleksandr-valyalkin\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.2.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"ro_RO\" \/>\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\/ro\/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\udd47Go optimizations in VictoriaMetrics. Alexander Valyalkin | ProHoster","description":"V\u0103 invit s\u0103 consulta\u021bi transcrierea prezent\u0103rii din sf\u00e2r\u0219itul anului 2019 a lui Alexander Valyalkin \"Optimiz\u0103ri Go \u00een VictoriaMetrics\"","canonical_url":"https:\/\/prohoster.info\/ro\/blog\/administrirovanie\/go-optimizations-in-victoriametrics-aleksandr-valyalkin","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"ro_RO","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\/ro\/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\/ro\/wp-json\/wp\/v2\/posts\/80733","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/comments?post=80733"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/posts\/80733\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/media\/80734"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/media?parent=80733"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/categories?post=80733"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/tags?post=80733"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}