{"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\/sv\/blog\/administrirovanie\/go-optimizations-in-victoriametrics-aleksandr-valyalkin","title":{"rendered":"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><strong>Jag f\u00f6resl\u00e5r att du bekantar dig med avrapporteringen av Alexander Valjalkins rapport fr\u00e5n slutet av 2019, \"Go optimizations in VictoriaMetrics\"<\/strong><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/victoriametrics.com\/\">VictoriaMetrics<\/a><\/noindex> \u2013 en snabb och skalbar databas f\u00f6r lagring och behandling av tidsseriedata (en post best\u00e5r av tid och en upps\u00e4ttning v\u00e4rden som motsvarar den tiden, till exempel insamlade genom periodisk avl\u00e4sning av sensorer eller insamling av m\u00e4tv\u00e4rden).<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" 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>H\u00e4r \u00e4r l\u00e4nken till videon av detta f\u00f6redrag \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\">Bildspel<\/a><\/noindex><\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/0073390d6dbcc6ab3e5305907ac6a229.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>L\u00e5t mig ber\u00e4tta lite om mig sj\u00e4lv. Jag heter Alexandr Valjalkin. H\u00e4r \u00e4r <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/valyala\">min GitHub-profil<\/a><\/noindex>. Jag \u00e4r intresserad av Go och prestandaoptimering. Jag har skrivit m\u00e5nga olika anv\u00e4ndbara och mindre anv\u00e4ndbara bibliotek. De b\u00f6rjar antingen med <code>fast<\/code>, eller med <code>quick<\/code> prefix. <\/p>\n<p><\/p>\n<p>F\u00f6r n\u00e4rvarande arbetar jag med VictoriaMetrics. Vad \u00e4r det och vad g\u00f6r jag d\u00e4r? Det kommer jag att ber\u00e4tta om i denna presentation. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/c9194bc3fee1f615d838979bec12f7d0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>F\u00f6redragets plan ser ut s\u00e5 h\u00e4r:<\/p>\n<p><\/p>\n<ul>\n<li>F\u00f6rst kommer jag att ber\u00e4tta vad VictoriaMetrics \u00e4r. <\/li>\n<li>Sedan kommer jag att f\u00f6rklara vad tidsserier \u00e4r. <\/li>\n<li>D\u00e4refter kommer jag att f\u00f6rklara hur en tidsseriedatabas fungerar.<\/li>\n<li>Vidare kommer jag att ber\u00e4tta om databasens arkitektur: hur den \u00e4r uppbyggd.<\/li>\n<li>Och tillslut kommer vi att diskutera optimeringarna som finns i VictoriaMetrics. Det handlar om optimering av den inverterade indexen och optimering f\u00f6r bitset-implementationen p\u00e5 Go.<\/li>\n<\/ul>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/3e0244b6b6fe952f660e4a094778921a.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Vet n\u00e5gon i publiken vad VictoriaMetrics \u00e4r? Oj, m\u00e5nga k\u00e4nner redan till den. Det \u00e4r goda nyheter. F\u00f6r dem som inte vet \u2013 det \u00e4r en databas f\u00f6r tidsserier. Den \u00e4r baserad p\u00e5 ClickHouse-arkitekturen, med vissa detaljer fr\u00e5n ClickHouse-implementationen. Till exempel aspekter som: MergeTree, parallell ber\u00e4kning p\u00e5 alla tillg\u00e4ngliga processor k\u00e4rnor och prestandaoptimering genom att arbeta med datablokkar som placeras i processor-cache. <\/p>\n<p><\/p>\n<p>VictoriaMetrics erbjuder b\u00e4ttre datakompression j\u00e4mf\u00f6rt med andra databaser f\u00f6r tidsserier. <\/p>\n<p><\/p>\n<p>Den kan skalas vertikalt \u2013 det vill s\u00e4ga att du kan l\u00e4gga till fler processorer, mer RAM-minne p\u00e5 en maskin. VictoriaMetrics kommer att utnyttja dessa tillg\u00e4ngliga resurser effektivt och \u00f6ka den linj\u00e4ra prestandan.<\/p>\n<p><\/p>\n<p>VictoriaMetrics kan \u00e4ven skalas horisontellt \u2013 det vill s\u00e4ga att du kan l\u00e4gga till fler noder i VictoriaMetrics-klustret, och dess prestanda kommer att \u00f6ka n\u00e4stan linj\u00e4rt.<\/p>\n<p><\/p>\n<p>Som ni kanske har gissat \u00e4r VictoriaMetrics en snabb databas, eftersom jag inte kan skriva om andra. Den \u00e4r skriven i Go, s\u00e5 d\u00e4rf\u00f6r pratar jag om den p\u00e5 detta meet-up.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/0121c9baead722eb1fe22fb6f900d4ad.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Vem vet vad en tidsserie \u00e4r? M\u00e5nga m\u00e4nniskor k\u00e4nner ocks\u00e5 till det. En tidsserie \u00e4r en sekvens av par <code>(timestamp, v\u00e4rde)<\/code>, d\u00e4r dessa par \u00e4r sorterade efter tid. V\u00e4rdet utg\u00f6rs av ett flyttal \u2013 float64.<\/p>\n<p><\/p>\n<p>Varje tidsserie identifieras unikt av en nyckel. Vad best\u00e5r denna nyckel av? Den best\u00e5r av en icke-tom upps\u00e4ttning av par nyckel-v\u00e4rde. <\/p>\n<p><\/p>\n<p>H\u00e4r \u00e4r ett exempel p\u00e5 en tidsserie. Nyckeln till denna serie \u00e4r en lista av par: <code>__name__=\"cpu_usage\"<\/code> \u2013 det \u00e4r namnet p\u00e5 metriket, <code>instance=\"my-server\"<\/code> \u2013 det \u00e4r datorn d\u00e4r denna metrik samlades in, <code>datacenter=\"us-east\"<\/code> \u2013 det \u00e4r datacentret d\u00e4r denna dator finns.<\/p>\n<p><\/p>\n<p>Vi har f\u00e5tt namnet p\u00e5 tidsserien, best\u00e5ende av tre par nyckel-v\u00e4rde. Denna nyckel motsvarar en lista av par <code>(timestamp, value)<\/code>. <code>t1, t2, t3, ..., tN<\/code> \u2013 dessa \u00e4r timestamps, <code>10, 20, 12, ..., 15<\/code> \u2013 med motsvarande v\u00e4rden. Detta \u00e4r cpu-anv\u00e4ndning just nu f\u00f6r den h\u00e4r serien.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/76cab9db0263318d355ac607ceb7e0f6.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Var kan tidsserier anv\u00e4ndas? Har n\u00e5gon n\u00e5gra id\u00e9er? <\/p>\n<p><\/p>\n<ul>\n<li>Inom DevOps kan man m\u00e4ta belastningen p\u00e5 CPU, RAM, n\u00e4tverk, rps, antal fel, etc. <\/li>\n<li>IoT \u2013 vi kan m\u00e4ta temperatur, tryck, geografiska koordinater och mer.<\/li>\n<li>\u00c4ven inom finans \u2013 vi kan \u00f6vervaka priser p\u00e5 olika aktier och valutor. <\/li>\n<li>Dessutom kan tidsserier anv\u00e4ndas f\u00f6r att \u00f6vervaka produktionsprocesser p\u00e5 fabriker. Vi har anv\u00e4ndare som anv\u00e4nder VictoriaMetrics f\u00f6r att \u00f6vervaka vindkraftverk och robotar.<\/li>\n<li>Tidsserier \u00e4r ocks\u00e5 anv\u00e4ndbara f\u00f6r att samla in information fr\u00e5n sensorer p\u00e5 olika enheter. Till exempel f\u00f6r en motor; f\u00f6r att m\u00e4ta trycket i d\u00e4ck; f\u00f6r att m\u00e4ta hastighet, avst\u00e5nd; f\u00f6r att m\u00e4ta br\u00e4nslef\u00f6rbrukning, etc.<\/li>\n<li>Tidsserier kan ocks\u00e5 anv\u00e4ndas f\u00f6r att \u00f6vervaka flygplan. Varje flygplan har en svart l\u00e5da som samlar in tidsserier om olika h\u00e4lsoparametrar f\u00f6r flygplanet. Tidsserier anv\u00e4nds ocks\u00e5 inom rymdindustrin. <\/li>\n<li>H\u00e4lsov\u00e5rd \u2013 det handlar om blodtryck, puls, etc.<\/li>\n<\/ul>\n<p><\/p>\n<p>Kanske finns det fler till\u00e4mpningar som jag har gl\u00f6mt, men jag hoppas att ni f\u00f6rst\u00e5r att tidsserier anv\u00e4nds aktivt i den moderna v\u00e4rlden. Och m\u00e4ngden av deras anv\u00e4ndning v\u00e4xer f\u00f6r varje \u00e5r.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/f1da29d372163751268b6a8f86836d04.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Vad beh\u00f6vs en databas f\u00f6r tidsserier? Varf\u00f6r kan man inte anv\u00e4nda en vanlig relationsdatabas f\u00f6r att lagra tidsserier?<\/p>\n<p><\/p>\n<p>F\u00f6r att tidsserier vanligtvis inneh\u00e5ller en stor m\u00e4ngd information som \u00e4r sv\u00e5r att lagra och bearbeta i vanliga databaser. D\u00e4rf\u00f6r har det uppst\u00e5tt specialiserade databaser f\u00f6r tidsserier. Dessa databaser lagrar effektivt punkter <code>(timestamp, value)<\/code> med en given nyckel. De erbjuder ett API f\u00f6r att l\u00e4sa de sparade uppgifterna efter nyckel, efter ett par nyckel-v\u00e4rde, flera s\u00e5dana par eller efter regexp. Till exempel, om du vill hitta CPU-belastningen f\u00f6r alla dina tj\u00e4nster i datacentret i Amerika, s\u00e5 beh\u00f6vs en s\u00e5dan pseudofr\u00e5ga.<\/p>\n<p><\/p>\n<p>Vanligtvis har databaser f\u00f6r tidsserier specialiserade fr\u00e5gespr\u00e5k, eftersom SQL inte passar s\u00e4rskilt bra f\u00f6r tidsserier. \u00c4ven om det finns databaser som st\u00f6djer SQL s\u00e5 passar det inte s\u00e4rskilt bra. Mer l\u00e4mpliga fr\u00e5gespr\u00e5k \u00e4r <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>. Jag hoppas att n\u00e5gon har h\u00f6rt talas om minst ett av dessa spr\u00e5k. Om PromQL har nog m\u00e5nga h\u00f6rt talas om.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/59e5b6d82a2f7861077bc5fe34519ee4.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>S\u00e5 h\u00e4r ser arkitekturen f\u00f6r en modern databas f\u00f6r tidsserier ut, med VictoriaMetrics som exempel.<\/p>\n<p><\/p>\n<p>Den best\u00e5r av tv\u00e5 delar. Det \u00e4r ett lagringsutrymme f\u00f6r den inverterade indexen och ett lagringsutrymme f\u00f6r v\u00e4rdena i tidsserier. Dessa lagringsutrymmen \u00e4r separerade. <\/p>\n<p><\/p>\n<p>N\u00e4r en ny post kommer in i databasen, b\u00f6rjar vi med att konsultera den inverterade indexen f\u00f6r att hitta identifieraren f\u00f6r tidsserien baserat p\u00e5 ett givet set <code>label=value<\/code> f\u00f6r denna metrik. Vi hittar denna identifierare och sparar v\u00e4rdet i data lagret.<\/p>\n<p><\/p>\n<p>N\u00e4r en fr\u00e5ga om att h\u00e4mta data fr\u00e5n TSDB kommer in, g\u00e5r vi f\u00f6rst till den inverterade indexen. Vi h\u00e4mtar alla <code>timeseries_ids<\/code> poster som motsvarar det angivna upps\u00e4ttningen. <code>label=value<\/code>Och sedan h\u00e4mtar vi all n\u00f6dv\u00e4ndig data fr\u00e5n data lagret, indexerat efter <code>timeseries_ids<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/eb9b36fa3c6de2188b97b56ad3839b2c.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>L\u00e5t oss titta p\u00e5 ett exempel p\u00e5 hur en databas f\u00f6r tidsserier behandlar en inkommande select-fr\u00e5ga.<\/p>\n<p><\/p>\n<ul>\n<li>F\u00f6rst h\u00e4mtar den alla <code>timeseries_ids<\/code> fr\u00e5n den inverterade indexen som inneh\u00e5ller de angivna paren <code>label=value<\/code>, eller som uppfyller det angivna regulj\u00e4ra uttrycket.<\/li>\n<li>Sedan h\u00e4mtar den alla data points fr\u00e5n lagringsutrymmet f\u00f6r den angivna tidsperioden f\u00f6r de hittade <code>timeseries_ids<\/code>.<\/li>\n<li>Efter detta utf\u00f6r databasen vissa ber\u00e4kningar p\u00e5 dessa datapunkter enligt anv\u00e4ndarens beg\u00e4ran. Och efter det returnerar den ett svar.<\/li>\n<\/ul>\n<p><\/p>\n<p>I denna presentation kommer jag att ber\u00e4tta om den f\u00f6rsta delen. Det handlar om s\u00f6kning <code>timeseries_ids<\/code> i det inverterade indexet. Den andra och tredje delen kan ni se senare <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/VictoriaMetrics\/VictoriaMetrics\">k\u00e4llkoderna f\u00f6r VictoriaMetrics<\/a><\/noindex>, eller v\u00e4nta tills jag f\u00f6rbereder fler f\u00f6redrag \ud83d\ude42<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/3a7dab707dd1defb9bc674a342052a03.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>L\u00e5t oss g\u00e5 vidare till det inverterade indexet. M\u00e5nga kan tycka att detta \u00e4r enkelt. Vem vet vad ett inverterat index \u00e4r och hur det fungerar? \u00c5h, det \u00e4r inte s\u00e5 m\u00e5nga l\u00e4ngre. L\u00e5t oss f\u00f6rs\u00f6ka f\u00f6rst\u00e5 vad det handlar om. <\/p>\n<p><\/p>\n<p>I sj\u00e4lva verket \u00e4r allt enkelt. Det \u00e4r bara en ordbok som mappar nyckeln till v\u00e4rdet. Vad \u00e4r en nyckel? Det \u00e4r paret <code>label=value<\/code>, d\u00e4r <code>label<\/code> och <code>value<\/code> \u2014 det \u00e4r str\u00e4ngar. Och v\u00e4rdena \u00e4r en upps\u00e4ttning <code>timeseries_ids<\/code>, som inkluderar det angivna paret <code>label=value<\/code>.<\/p>\n<p><\/p>\n<p>Det inverterade indexet m\u00f6jligg\u00f6r snabb s\u00f6kning av alla <code>timeseries_ids<\/code>, som har de angivna <code>label=value<\/code>.<\/p>\n<p><\/p>\n<p>Och det m\u00f6jligg\u00f6r ocks\u00e5 snabb s\u00f6kning av <code>timeseries_ids<\/code> tidsserier f\u00f6r flera par <code>label=value<\/code>, eller f\u00f6r paren <code>label=regexp<\/code>. Hur fungerar det? Genom att hitta snittet av m\u00e4ngden <code>timeseries_ids<\/code> f\u00f6r varje par <code>label=value<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/837a15a078e8c9422c721f3f072c0c09.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>L\u00e5t oss titta p\u00e5 olika implementeringar av det inverterade indexet. Vi b\u00f6rjar med den enklaste naiva implementeringen. Den ser ut s\u00e5 h\u00e4r. <\/p>\n<p><\/p>\n<p>Funktionen <code>getMetricIDs<\/code> h\u00e4mtar en lista med str\u00e4ngar. Varje str\u00e4ng inneh\u00e5ller <code>label=value<\/code>. Denna funktion returnerar en lista <code>metricIDs<\/code>.<\/p>\n<p><\/p>\n<p>Hur fungerar det? H\u00e4r har vi en global variabel som heter <code>invertedIndex<\/code>. Det \u00e4r en vanlig ordbok (<code>map<\/code>), som mappar str\u00e4ngen till en slice av heltal. Str\u00e4ngen inneh\u00e5ller <code>label=value<\/code>.<\/p>\n<p><\/p>\n<p>Implementeringen av funktionen: vi f\u00e5r tag p\u00e5 <code>metricIDs<\/code> f\u00f6r den f\u00f6rsta <code>label=value<\/code>, sedan g\u00e5r vi igenom alla andra <code>label=value<\/code>, tar tag p\u00e5 <code>metricIDs<\/code> f\u00f6r dem. Och vi kallar funktionen <code>intersectInts<\/code>, som kommer att diskuteras senare. Och denna funktion returnerar snittet av dessa listor.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/709beca79884a9693098781974c26f4d.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Som ni ser \u00e4r implementeringen av det inverterade indexet inte s\u00e4rskilt komplicerad. Men det \u00e4r en naiv implementation. Vilka nackdelar har den? Huvud nackdelen med den naiva implementeringen \u00e4r att detta inverterade index lagras i arbetsminnet. Efter omstart av applikationen f\u00f6rlorar vi detta index. Det finns ingen lagring av detta index p\u00e5 disken. F\u00f6r en databas \u00e4r det inverterade indexet sv\u00e5rt att anv\u00e4nda.<\/p>\n<p><\/p>\n<p>Den andra nackdelen handlar ocks\u00e5 om minne. Den inverterade indexen m\u00e5ste placeras i RAM. Om den \u00f6verstiger storleken p\u00e5 RAM, f\u00e5r vi uppenbarligen \u2013 out of memory error. Och programmet kommer inte att fungera.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/495394168ac03aa8cbcf56ccad1cb2b8.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Detta problem kan l\u00f6sas med hj\u00e4lp av f\u00e4rdiga l\u00f6sningar som <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/google\/leveldb\">LevelDB<\/a><\/noindex>, eller <noindex><a rel=\"nofollow\" href=\"https:\/\/rocksdb.org\/\">RocksDB<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>Kort sagt beh\u00f6ver vi en databas som snabbt kan utf\u00f6ra tre operationer. <\/p>\n<p><\/p>\n<ul>\n<li>Den f\u00f6rsta operationen \u00e4r att skriva <code>nyckel-v\u00e4rde<\/code> till denna databas. Detta g\u00f6rs v\u00e4ldigt snabbt, d\u00e4r <code>nyckel-v\u00e4rde<\/code> \u00e4r godtyckliga str\u00e4ngar. <\/li>\n<li>Den andra operationen \u00e4r en snabb s\u00f6kning av v\u00e4rdet f\u00f6r en given nyckel.<\/li>\n<li>Och den tredje operationen \u00e4r en snabb s\u00f6kning av alla v\u00e4rden efter ett givet prefix. <\/li>\n<\/ul>\n<p><\/p>\n<p>LevelDB och RocksDB \u2013 dessa databaser har utvecklats av Google och Facebook. F\u00f6rst kom LevelDB. Sedan tog Facebook-teamet LevelDB och b\u00f6rjade f\u00f6rb\u00e4ttra den och skapade RocksDB. Nu anv\u00e4nder n\u00e4stan alla interna databaser p\u00e5 Facebook RocksDB, dessutom har de \u00e4ven migrerat MySQL till RocksDB. De kallade det <noindex><a rel=\"nofollow\" href=\"http:\/\/myrocks.io\/\">MyRocks<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>Den inverterade indexen kan implementeras med hj\u00e4lp av LevelDB. Hur g\u00f6r vi detta? Vi sparar som nyckel <code>label=value<\/code>. Och som v\u00e4rde \u2013 identifieraren f\u00f6r tidsserien d\u00e4r paret finns <code>label=value<\/code>.<\/p>\n<p><\/p>\n<p>Om vi har m\u00e5nga tidsserier med detta par <code>label=value<\/code>, kommer det att finnas m\u00e5nga rader i denna databas med samma nyckel men olika <code>timeseries_ids<\/code>. F\u00f6r att f\u00e5 en lista \u00f6ver alla <code>timeseries_ids<\/code>, som b\u00f6rjar med detta <code>label=prefix<\/code>, g\u00f6r vi en range scan, f\u00f6r vilket denna databas \u00e4r optimerad. Det vill s\u00e4ga, vi v\u00e4ljer alla rader som b\u00f6rjar med <code>label=prefix<\/code> och f\u00e5r de n\u00f6dv\u00e4ndiga <code>timeseries_ids<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/a40877ecb758ac3bc1979bcd568d7e83.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>H\u00e4r \u00e4r en ungef\u00e4rlig implementation hur den skulle se ut i Go. Vi har en inverterad index. Det \u00e4r LevelDB.<\/p>\n<p><\/p>\n<p>Funktionen \u00e4r densamma som f\u00f6r den naiva implementationen. Den upprepar n\u00e4stan rad f\u00f6r rad den naiva implementationen. Den enda skillnaden \u00e4r att ist\u00e4llet f\u00f6r att referera till <code>map<\/code> refererar vi till den inverterade indexen. Vi h\u00e4mtar alla v\u00e4rden f\u00f6r den f\u00f6rsta <code>label=value<\/code>. Sedan g\u00e5r vi igenom alla kvarst\u00e5ende par <code>label=value<\/code> och h\u00e4mtar motsvarande upps\u00e4ttningar av metricIDs f\u00f6r dem. D\u00e4refter hittar vi snittet. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/de11b15286816ada04b22727ff78e43d.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Det verkar bra, men det finns nackdelar med denna l\u00f6sning. VictoriaMetrics implementerade f\u00f6rst den inverterade indexen baserad p\u00e5 LevelDB. Men till slut var de tvungna att \u00f6verge den.<\/p>\n<p><\/p>\n<p>Varf\u00f6r? Eftersom LevelDB \u00e4r l\u00e5ngsammare \u00e4n den naiva implementationen. I den naiva implementationen f\u00e5r vi direkt hela skivm\u00e5tt f\u00f6r en given nyckel. <code>metricIDs<\/code>. Denna mycket snabba operation - hela slice \u00e4r redo att anv\u00e4ndas.<\/p>\n<p><\/p>\n<p>I LevelDB m\u00e5ste man vid varje anrop av funktionen <code>GetValues<\/code> g\u00e5 igenom alla rader som b\u00f6rjar med <code>label=value<\/code>. Och f\u00f6r varje rad h\u00e4mta v\u00e4rdet <code>timeseries_ids<\/code>. Av s\u00e5dana <code>timeseries_ids<\/code> samla slice av dessa <code>timeseries_ids<\/code>. Det \u00e4r uppenbart att detta \u00e4r mycket l\u00e5ngsammare \u00e4n att bara h\u00e4nvisa till en vanlig map med nyckel.<\/p>\n<p><\/p>\n<p>Den andra nackdelen \u00e4r att LevelDB \u00e4r skrivet i C. Anrop till C-funktioner fr\u00e5n Go \u00e4r inte s\u00e4rskilt snabbt. Det tar hundratals nanosekunder. Det \u00e4r inte s\u00e5 snabbt, eftersom j\u00e4mf\u00f6rt med ett vanligt funktionsanrop, skrivet i Go, som tar 1-5 nanosekunder, blir prestandaskillnaden tiotals g\u00e5nger. F\u00f6r VictoriaMetrics var detta en d\u00f6dlig nackdel \ud83d\ude42<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/47cace825807b7b062064c2e790dc9d3.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>D\u00e4rf\u00f6r har jag skrivit en egen implementation av det inverterade indexet. Jag kallade den <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/VictoriaMetrics\/VictoriaMetrics\/tree\/master\/lib\/mergeset\">mergeset<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>Mergeset \u00e4r baserat p\u00e5 datakonstruktionen MergeTree. Denna datakonstruktion \u00e4r l\u00e5nad fr\u00e5n ClickHouse. Uppenbarligen b\u00f6r mergeset optimeras f\u00f6r snabb s\u00f6kning <code>timeseries_ids<\/code> efter en given nyckel. Mergeset \u00e4r helt skrivet i Go. Du kan titta p\u00e5 <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/VictoriaMetrics\/VictoriaMetrics\">k\u00e4llkoden till VictoriaMetrics p\u00e5 GitHub<\/a><\/noindex>. Implementeringen av mergeset ligger i mappen <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/VictoriaMetrics\/VictoriaMetrics\/tree\/master\/lib\/mergeset\">\/lib\/mergeset<\/a><\/noindex>. Du kan f\u00f6rs\u00f6ka ta reda p\u00e5 vad som h\u00e4nder d\u00e4r.<\/p>\n<p><\/p>\n<p>API:et f\u00f6r mergeset liknar mycket LevelDB och RocksDB. Det vill s\u00e4ga, det till\u00e5ter att snabbt spara nya postningar och snabbt h\u00e4mta postningar efter en given prefix.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/845704ed36f70beb468907d22701af59.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Vi kommer att prata om nackdelarna med mergeset senare. Just nu ska vi diskutera vilka problem som uppstod med VictoriaMetrics i produktion vid implementeringen av det inverterade indexet.<\/p>\n<p><\/p>\n<p>Varf\u00f6r uppstod de?<\/p>\n<p><\/p>\n<p>Den f\u00f6rsta anledningen \u00e4r den h\u00f6ga churn rate. P\u00e5 svenska betyder detta - ofta byte av tidsserier. Detta intr\u00e4ffar n\u00e4r en tidsserie slutar och en ny b\u00f6rjar, eller m\u00e5nga nya tidsserier b\u00f6rjar. Och detta h\u00e4nder ofta.<\/p>\n<p><\/p>\n<p>Den andra anledningen \u00e4r det stora antalet tidsserier. I b\u00f6rjan, n\u00e4r \u00f6vervakningen blev popul\u00e4r, var antalet tidsserier litet. Till exempel, f\u00f6r varje dator m\u00e5ste CPU, minne, n\u00e4tverk och disk \u00f6vervakas. 4 tidsserier per dator. Om du har, s\u00e4g, 100 datorer och 400 tidsserier. Det \u00e4r mycket lite. <\/p>\n<p><\/p>\n<p>Med tiden har m\u00e4nniskor kommit p\u00e5 att man kan m\u00e4ta mer detaljerad information. Till exempel, att m\u00e4ta belastningen inte f\u00f6r hela processorn, utan separat f\u00f6r varje processor k\u00e4rna. Om du har 40 processor k\u00e4rnor, f\u00e5r du d\u00e4rmed 40 g\u00e5nger fler tidsserier f\u00f6r att m\u00e4ta processorbelastningen. <\/p>\n<p><\/p>\n<p>Men det \u00e4r inte allt. Varje processor k\u00e4rna kan ha flera tillst\u00e5nd som idle, n\u00e4r den \u00e4r inaktiv. Dessutom finns det arbete i user space, arbete i kernel space och andra tillst\u00e5nd. Och varje s\u00e5dant tillst\u00e5nd kan ocks\u00e5 m\u00e4tas som en separat tidsserie. Detta \u00f6kar antalet serier ytterligare 7-8 g\u00e5nger.<\/p>\n<p><\/p>\n<p>Fr\u00e5n en enda metrik har vi f\u00e5tt 40 x 8 = 320 metrikker bara f\u00f6r en dator. Multiplicerat med 100 ger det oss 32 000 ist\u00e4llet f\u00f6r 400. <\/p>\n<p><\/p>\n<p>Sedan kom Kubernetes. Och det f\u00f6rv\u00e4rrade saken ytterligare, eftersom m\u00e5nga olika tj\u00e4nster kan hostas i Kubernetes. Varje tj\u00e4nst i Kubernetes best\u00e5r av m\u00e5nga pods. Allt detta m\u00e5ste \u00f6vervakas. Dessutom har vi st\u00e4ndiga deployment av nya versioner av dina tj\u00e4nster. F\u00f6r varje ny version m\u00e5ste nya tidsserier skapas. Slutresultatet \u00e4r att antalet tidsserier v\u00e4xer exponentiellt och vi st\u00e5r inf\u00f6r problemet med ett stort antal tidsserier, som kallas high-cardinality. VictoriaMetrics hanterar detta framg\u00e5ngsrikt j\u00e4mf\u00f6rt med andra tidsserie-databaser. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/c11b7ce6d3294ae420243f72636af4b9.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>L\u00e5t oss titta n\u00e4rmare p\u00e5 high churn rate. Vad orsakar high churn rate i produktion? Eftersom vissa v\u00e4rden p\u00e5 etiketter och taggar st\u00e4ndigt f\u00f6r\u00e4ndras.<\/p>\n<p><\/p>\n<p>Till exempel, l\u00e5t oss ta Kubernetes, d\u00e4r begreppet <code>deployment<\/code>, det vill s\u00e4ga n\u00e4r en ny version av din applikation rullas ut. Utvecklarna av Kubernetes har av n\u00e5gon anledning valt att l\u00e4gga till id:et f\u00f6r deployment i etiketten.<\/p>\n<p><\/p>\n<p>Vad ledde detta till? Att vid varje ny deployment avbryts alla gamla tidsserier, och ist\u00e4llet b\u00f6rjar nya tidsserier med ett nytt v\u00e4rde i etiketten. <code>deployment_id<\/code>. S\u00e5dana serier kan vara hundratusentals eller till och med miljoner.<\/p>\n<p><\/p>\n<p>En viktig aspekt av allt detta \u00e4r att det totala antalet tidsserier v\u00e4xer, men antalet tidsserier som f\u00f6r n\u00e4rvarande \u00e4r aktiva, som f\u00e5r data, f\u00f6rblir konstant. Detta tillst\u00e5nd kallas high churn rate.<\/p>\n<p><\/p>\n<p>Huvudproblemet med h\u00f6g churn rate \u00e4r att s\u00e4kerst\u00e4lla en konstant s\u00f6khastighet f\u00f6r alla tidsserier \u00f6ver en given upps\u00e4ttning etiketter under en viss tidsperiod. Vanligtvis handlar det om tidsperioden den senaste timmen eller den senaste dagen. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/ac18482b87cc37624d163b30c68cf7be.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Hur kan vi l\u00f6sa detta problem? H\u00e4r \u00e4r den f\u00f6rsta l\u00f6sningen. Vi delar den inverterade indexen i oberoende delar baserat p\u00e5 tid. Det vill s\u00e4ga, n\u00e4r en tidsperiod har passerat, avslutar vi arbetet med den nuvarande inverterade indexen. Och vi skapar en ny inverterad index. N\u00e4r ytterligare en tidsperiod passerar, skapar vi en till och en till. <\/p>\n<p><\/p>\n<p>Och vid urvalet fr\u00e5n dessa inverterade index hittar vi en upps\u00e4ttning inverterade index som faller inom den angivna tidsperioden. Och, i enlighet d\u00e4rmed, v\u00e4ljer vi id:n f\u00f6r tidsserierna d\u00e4rifr\u00e5n. <\/p>\n<p><\/p>\n<p>Detta sparar resurser eftersom vi inte beh\u00f6ver granska delar som inte faller inom den angivna tidsperioden. Det vill s\u00e4ga, om vi vanligtvis v\u00e4ljer data fr\u00e5n den senaste timmen, s\u00e5 hoppar vi \u00f6ver f\u00f6rfr\u00e5gningar fr\u00e5n tidigare tidsperioder. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/1afa3a88caa7423941ae3494b9cec706.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Det finns ytterligare en l\u00f6sning p\u00e5 detta problem. Det \u00e4r att f\u00f6r varje dag lagra en separat lista med id:n f\u00f6r tidsserier som f\u00f6rekommit den dagen.<\/p>\n<p><\/p>\n<p>F\u00f6rdelen med denna l\u00f6sning j\u00e4mf\u00f6rt med den tidigare l\u00f6sningen \u00e4r att vi inte duplicerar information om tidsserier som inte f\u00f6rsvinner \u00f6ver tid. De \u00e4r konstant n\u00e4rvarande och f\u00f6r\u00e4ndras inte. <\/p>\n<p><\/p>\n<p>Nackdelen \u00e4r att en s\u00e5dan l\u00f6sning \u00e4r mer komplicerad att implementera och sv\u00e5rare att debugga. Och VictoriaMetrics har valt denna l\u00f6sning. Det har historiskt sett blivit s\u00e5. Denna l\u00f6sning visar sig ocks\u00e5 fungera ganska bra, j\u00e4mf\u00f6rt med den tidigare. Eftersom denna l\u00f6sning inte implementerades p\u00e5 grund av att det kr\u00e4ver att vi duplicerar data i varje partition f\u00f6r tidsserier som inte f\u00f6r\u00e4ndras, det vill s\u00e4ga de som inte f\u00f6rsvinner \u00f6ver tid. VictoriaMetrics har i f\u00f6rsta hand optimerats f\u00f6r diskutrymmesanv\u00e4ndning, och den tidigare implementationen f\u00f6rs\u00e4mrade diskutrymmesanv\u00e4ndningen. Men denna implementation passar b\u00e4ttre f\u00f6r att minimera diskutrymmesanv\u00e4ndningen, vilket \u00e4r varf\u00f6r den valdes. <\/p>\n<p><\/p>\n<p>Vi fick k\u00e4mpa emot den. Kampen bestod i att i denna implementation m\u00e5ste vi fortfarande v\u00e4lja ett betydligt st\u00f6rre antal <code>timeseries_ids<\/code> f\u00f6r data \u00e4n n\u00e4r den inverterade indexen \u00e4r delad efter tid.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/5b10e61aaa803428ed3bfe31815f09d3.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Hur l\u00f6ste vi detta problem? Vi l\u00f6ste det p\u00e5 ett unikt s\u00e4tt \u2013 genom att spara flera identifierare f\u00f6r tidsserier i varje post i den inverterade indexen ist\u00e4llet f\u00f6r en enda identifierare. Det vill s\u00e4ga, vi har en nyckel <code>label=value<\/code>, som f\u00f6rekommer i varje tidsserie. Och nu sparar vi flera <code>timeseries_ids<\/code> i en post.<\/p>\n<p><\/p>\n<p>H\u00e4r \u00e4r ett exempel. Tidigare hade vi N poster, nu har vi en post vars prefix \u00e4r detsamma som alla andra. Den tidigare posten inneh\u00f6ll alla id:n f\u00f6r tidsserier. <\/p>\n<p><\/p>\n<p>Detta har \u00f6kat hastigheten f\u00f6r att skanna en s\u00e5dan inverterad index med upp till 10 g\u00e5nger. Och det har minskat minnesf\u00f6rbrukningen f\u00f6r cachen, eftersom vi nu sparar str\u00e4ngen <code>label=value<\/code> bara en g\u00e5ng i cachen ist\u00e4llet f\u00f6r N g\u00e5nger. Och denna str\u00e4ng kan vara stor, om du har l\u00e5nga str\u00e4ngar i taggar och etiketter som Kubernetes g\u00e4rna skjuter in d\u00e4r.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/ae4f38a440203bad2d03cf2abd1faafe.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Ett annat alternativ f\u00f6r att snabba upp s\u00f6kningarna i den inverterade indexen \u00e4r sharding. Att skapa flera inverterade index ist\u00e4llet f\u00f6r ett och shardera data mellan dem efter nyckel. Det \u00e4r ett set av <code>key=value<\/code> par. Det vill s\u00e4ga, vi f\u00e5r flera oberoende inverterade index som vi kan fr\u00e5ga parallellt p\u00e5 flera processorer. Tidigare implementationer till\u00e4t endast att arbeta i en enskild processor, det vill s\u00e4ga att skanna data endast p\u00e5 ett k\u00e4rna. Denna l\u00f6sning m\u00f6jligg\u00f6r att skanna data p\u00e5 flera k\u00e4rnor samtidigt, vilket ClickHouse g\u00e4rna g\u00f6r. Det \u00e4r vad vi planerar att implementera.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/5ced6446efbf8ded8d7fb91694a999b0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Och nu tillbaka till v\u00e5ra f\u00e5r \u2013 till funktionen f\u00f6r intersection <code>timeseries_ids<\/code>. L\u00e5t oss \u00f6verv\u00e4ga vilka implementationer som kan finnas. Denna funktion g\u00f6r det m\u00f6jligt att hitta <code>timeseries_ids<\/code> f\u00f6r ett givet set <code>label=value<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/073a155018a91120c6996c324772f9af.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Det f\u00f6rsta alternativet \u2013 det \u00e4r den na\u00efva implementationen. Tv\u00e5 n\u00e4stlade loopar. H\u00e4r f\u00e5r vi tv\u00e5 slices som indata till funktionen <code>intersectInts<\/code> \u2013 <code>a<\/code> och <code>b<\/code>. Utg\u00e5ngen ska ge oss intersection av dessa slices.<\/p>\n<p><\/p>\n<p>Den na\u00efva implementationen ser ut s\u00e5 h\u00e4r. Vi g\u00e5r igenom alla v\u00e4rden fr\u00e5n slice <code>a<\/code>, inom denna loop g\u00e5r vi igenom alla v\u00e4rden fr\u00e5n slice <code>b<\/code>. Och vi j\u00e4mf\u00f6r dem. Om de matchar, betyder det att vi har hittat intersection. Och vi sparar det i <code>result<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/b89dcbb4f6d73377f9cf4f50169d6dd7.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Vilka nackdelar finns det? Den kvadratiska komplexiteten \u2013 detta \u00e4r dess fr\u00e4msta nackdel. Till exempel, om du har storlekar p\u00e5 slice <code>a<\/code> och <code>b<\/code> Om du har en miljon, kommer den h\u00e4r funktionen aldrig att ge dig ett svar. Eftersom den m\u00e5ste g\u00f6ra en biljon iterationer, vilket \u00e4r mycket \u00e4ven f\u00f6r moderna datorer.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/bad26b0092a2fd7fc813bcb79a9138ce.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Den andra implementationen baseras p\u00e5 map. Vi skapar en map. Vi l\u00e4gger alla v\u00e4rden fr\u00e5n slice i denna map. <code>a<\/code>Sedan g\u00e5r vi igenom slice med en separat loop. <code>b<\/code>Och vi kontrollerar \u2013 finns det detta v\u00e4rde fr\u00e5n slice <code>b<\/code> i mappen. Om det finns, l\u00e4gger vi till det i resultatet. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/1e610acf641a9cfe4c80aac1fe26044c.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Vilka f\u00f6rdelar finns det? F\u00f6rdelen \u00e4r att h\u00e4r \u00e4r det endast linj\u00e4r komplexitet. Det vill s\u00e4ga, funktionen kommer att utf\u00f6ras mycket snabbare f\u00f6r stora storlekar av slices. F\u00f6r en slice av storlek en miljon kommer denna funktion att utf\u00f6ras p\u00e5 2 miljoner iterationer, till skillnad fr\u00e5n en biljon iterationer som i den tidigare funktionen.<\/p>\n<p><\/p>\n<p>Nackdelen \u00e4r att denna funktion kr\u00e4ver mer minne f\u00f6r att skapa denna map.<\/p>\n<p><\/p>\n<p>Den andra nackdelen \u00e4r den stora overheaden f\u00f6r hashing. Denna nackdel \u00e4r inte s\u00e4rskilt uppenbar. Och f\u00f6r oss var den inte heller s\u00e4rskilt uppenbar, d\u00e4rf\u00f6r var den initiala implementationen av intersection i VictoriaMetrics genom map. Men sedan visade profileringsresultaten att den st\u00f6rsta processor tiden gick \u00e5t till att skriva till mappen och kontrollera om det finns ett v\u00e4rde i denna map.<\/p>\n<p><\/p>\n<p>Varf\u00f6r g\u00e5r processor tiden \u00e5t p\u00e5 dessa st\u00e4llen? F\u00f6r att i dessa rader utf\u00f6r Go en hash operation. Det vill s\u00e4ga att den ber\u00e4knar hash av nyckeln f\u00f6r att sedan kunna komma \u00e5t det angivna indexet i HashMap. Hashber\u00e4kningen utf\u00f6rs p\u00e5 tiotals nanosekunder. Det \u00e4r l\u00e5ngsamt f\u00f6r VictoriaMetrics.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/969f10d875d88998e958894ca28b1181.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Jag beslutade att implementera en bitset, s\u00e4rskilt optimerad f\u00f6r detta fall. S\u00e5 h\u00e4r ser nu intersection av tv\u00e5 slices ut. H\u00e4r skapar vi en bitset. Vi l\u00e4gger till elementen fr\u00e5n den f\u00f6rsta slice. Sedan kontrollerar vi n\u00e4rvaron av dessa element i den andra slice. Och vi l\u00e4gger till dem i resultatet. Det vill s\u00e4ga, det skiljer sig knappt fr\u00e5n det tidigare exemplet. Det enda som vi har bytt h\u00e4r \u00e4r att vi har ersatt anropet till mappen med specialfunktioner. <code>add<\/code> och <code>has<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/b4ec0753d30d9684868a031f605632c0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Vid f\u00f6rsta anblicken verkar det som om detta borde fungera l\u00e5ngsammare, om standardmappen anv\u00e4ndes tidigare och h\u00e4r anropas det ytterligare funktioner, men profileringsresultaten visar att denna sak fungerar 10 g\u00e5nger snabbare \u00e4n standardmappen f\u00f6r fallet med VictoriaMetrics.<\/p>\n<p><\/p>\n<p>Dessutom anv\u00e4nder den mycket mindre minne j\u00e4mf\u00f6rt med implementationen med map. F\u00f6r vi lagrar bitar h\u00e4r ist\u00e4llet f\u00f6r \u00e5ttabyte v\u00e4rden.<\/p>\n<p><\/p>\n<p>En nackdel med denna implementation \u00e4r att den inte \u00e4r s\u00e5 uppenbar, inte trivial. <\/p>\n<p><\/p>\n<p>En annan nackdel som m\u00e5nga kanske inte m\u00e4rker \u2013 \u00e4r att denna implementation kan fungera d\u00e5ligt i vissa fall. Det vill s\u00e4ga, den \u00e4r optimerad f\u00f6r ett specifikt fall, f\u00f6r detta fall av \u00f6verlappande ids av tidsserier i VictoriaMetrics. Det betyder inte att den passar alla situationer. Om den anv\u00e4nds felaktigt, kan vi f\u00e5 inte bara s\u00e4mre prestanda utan ocks\u00e5 minnesfel och en minskning av prestanda. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/94bd174afe43e67b3cf279dc7a1be17c.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>L\u00e5t oss titta p\u00e5 implementationen av denna struktur. Om du vill se den, s\u00e5 finns den i k\u00e4llkoden till VictoriaMetrics, i mappen <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/VictoriaMetrics\/VictoriaMetrics\/tree\/master\/lib\/uint64set\">lib\/uint64set<\/a><\/noindex>. Den \u00e4r optimerad just f\u00f6r fallet VictoriaMetrics, d\u00e4r <code>timeseries_id<\/code> \u00e4r ett 64-bitarsv\u00e4rde, d\u00e4r de f\u00f6rsta 32 bitarna \u00e4r konstant och endast de sista 32 bitarna \u00e4ndras.<\/p>\n<p><\/p>\n<p>Denna datastruktur lagras inte p\u00e5 disken, den fungerar endast i minnet. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/e24ca65f860e2b04037e073f7acae0c1.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>H\u00e4r \u00e4r dess API. Det \u00e4r inte s\u00e4rskilt komplicerat. API:t \u00e4r anpassat exakt f\u00f6r det specifika exempel som anv\u00e4nds av VictoriaMetrics. Det vill s\u00e4ga, det finns inga on\u00f6diga funktioner h\u00e4r. H\u00e4r finns funktioner som VictoriaMetrics tydligt anv\u00e4nder.<\/p>\n<p><\/p>\n<p>Det finns funktioner <code>add<\/code>, som l\u00e4gger till nya v\u00e4rden. Det finns en funktion <code>has<\/code>, som kontrollerar nya v\u00e4rden. Och det finns en funktion <code>del<\/code>, som tar bort v\u00e4rden. Det finns ocks\u00e5 en hj\u00e4lpfunktion <code>len<\/code>, som returnerar storleken p\u00e5 m\u00e4ngden. Funktionen <code>clone<\/code> klonar m\u00e4ngden. Och funktionen <code>appendto<\/code> omvandlar denna set till en slice <code>timeseries_ids<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/874b04042d13f342d39f8f393e7fb78c.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>S\u00e5 h\u00e4r ser implementationen av denna datastruktur ut. I set finns det tv\u00e5 element:<\/p>\n<p><\/p>\n<ul>\n<li>\n<p><code>ItemsCount<\/code> \u2013 detta \u00e4r ett hj\u00e4lpf\u00e4lt, f\u00f6r att snabbt returnera antalet element i set. Man skulle kunna klara sig utan det h\u00e4r hj\u00e4lpf\u00e4ltet, men det lades till h\u00e4r eftersom VictoriaMetrics ofta fr\u00e5gar om l\u00e4ngden p\u00e5 bitset i sina algoritmer.<\/p>\n<p>\n<\/li>\n<li>\n<p>Det andra f\u00e4ltet \u00e4r <code>buckets<\/code>. Detta \u00e4r en slice av strukturen <code>bucket32<\/code>. I varje struktur lagras <code>hi<\/code> f\u00e4lt. Detta \u00e4r de \u00f6versta 32 bitarna. Och tv\u00e5 slices \u2014 <code>b16his<\/code> och <code>buckets<\/code> fr\u00e5n <code>bucket16<\/code> strukturer. <\/p>\n<p>\n<\/li>\n<\/ul>\n<p><\/p>\n<p>H\u00e4r lagras de \u00f6versta 16 bitarna av den andra delen av den 64-bitars strukturen. Och h\u00e4r lagras bitsets f\u00f6r de l\u00e4gre 16 bitarna av varje byte. <\/p>\n<p><\/p>\n<p><code>Bucket64<\/code> best\u00e5r av en array <code>uint64<\/code>. L\u00e4ngden ber\u00e4knas med hj\u00e4lp av dessa konstanter. I en <code>bucket16<\/code> kan maximalt lagras <code>2^16=65536<\/code> bitar. Om vi delar detta med 8, f\u00e5r vi 8 kilobyte. Om vi delar en g\u00e5ng till med 8, s\u00e5 \u00e4r det 1000 <code>uint64<\/code> v\u00e4rden. Det vill s\u00e4ga, <code>Bucket16<\/code> \u2013 \u00e4r en 8-kilobyte struktur. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/1cca4e6bfafa3408a3c95e0118e26d80.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>L\u00e5t oss titta p\u00e5 hur en av metoderna f\u00f6r denna struktur f\u00f6r att l\u00e4gga till ett nytt v\u00e4rde \u00e4r implementerad. <\/p>\n<p><\/p>\n<p>Allt b\u00f6rjar med <code>uint64<\/code> v\u00e4rdet. Vi ber\u00e4knar de \u00f6vre 32 bitarna, ber\u00e4knar de nedre 32 bitarna. Vi g\u00e5r igenom alla <code>buckets<\/code>. Vi j\u00e4mf\u00f6r de \u00f6vre 32 bitarna i varje bucket med det tillagda v\u00e4rdet. Och om de matchar, anropar vi funktionen <code>add<\/code> i strukturen b32 <code>buckets<\/code>. Och l\u00e4gger till de nedre 32 bitarna d\u00e4r. Om detta returnerade <code>true<\/code>, betyder det att vi har lagt till ett s\u00e5dant v\u00e4rde d\u00e4r och att vi inte hade ett s\u00e5dant v\u00e4rde tidigare. Om det returnerar <code>false<\/code>, s\u00e5 fanns ett s\u00e5dant v\u00e4rde redan. Sedan \u00f6kar vi antalet element i strukturen. <\/p>\n<p><\/p>\n<p>Om vi inte hittade den n\u00f6dv\u00e4ndiga <code>bucket<\/code> med det n\u00f6dv\u00e4ndiga hi-v\u00e4rdet, d\u00e5 anropar vi funktionen <code>addAlloc<\/code>, som tilldelar en ny <code>bucket<\/code>, genom att l\u00e4gga till den i bucket-strukturen.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/c5d1e5de767f47c7c40e01e12b6d0617.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Detta \u00e4r implementeringen av funktionen <code>b32.add<\/code>. Den liknar den tidigare implementationen. Vi ber\u00e4knar de \u00f6versta 16 bitarna, de nedersta 16 bitarna.<\/p>\n<p><\/p>\n<p>Sedan g\u00e5r vi igenom alla \u00f6vre 16 bitar. Vi hittar matchningar. Och vid en match anropar vi metoden add, som vi kommer att g\u00e5 igenom p\u00e5 n\u00e4sta sida f\u00f6r <code>bucket16<\/code>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/e98c4ef316f3a894fd7019c3c85696f7.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Och h\u00e4r \u00e4r den nedersta niv\u00e5n, som m\u00e5ste vara s\u00e5 optimal som m\u00f6jligt. Vi ber\u00e4knar f\u00f6r <code>uint64<\/code> id-v\u00e4rdet i slice bit, samt <code>bitmask<\/code>. Detta \u00e4r en mask f\u00f6r detta 64-bitars v\u00e4rde, genom vilken vi kan kontrollera om denna bit finns, eller s\u00e4tta den. Vi kontrollerar om denna bit \u00e4r inst\u00e4lld och s\u00e4tter den, och returnerar dess tillst\u00e5nd. S\u00e5 h\u00e4r har vi en implementation som har gjort det m\u00f6jligt att p\u00e5skynda operationen f\u00f6r att korsa ids f\u00f6r tidsserier med 10 g\u00e5nger j\u00e4mf\u00f6rt med vanliga maps.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/4dfa5a6142829a3551be22d6c9c3ab92.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>I VictoriaMetrics finns det m\u00e5nga andra optimeringar f\u00f6rutom denna. De flesta av dessa optimeringar har lagts till av en anledning, efter profilering av koden i produktion.<\/p>\n<p><\/p>\n<p>Detta \u00e4r den grundl\u00e4ggande regeln f\u00f6r optimering \u2013 l\u00e4gg inte till en optimering, antar att det kommer att vara en flaskhals h\u00e4r, f\u00f6r det kan visa sig att det inte finns n\u00e5gon flaskhals d\u00e4r. Optimering f\u00f6rs\u00e4mrar vanligtvis kodens kvalitet. D\u00e4rf\u00f6r b\u00f6r man optimera endast efter profilering och helst i produktion, s\u00e5 att det handlar om verkliga data. Om du \u00e4r intresserad kan du titta p\u00e5 k\u00e4llkoden f\u00f6r VictoriaMetrics och studera andra optimeringar som finns d\u00e4r.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Go-optimeringar i VictoriaMetrics. Alexander Valjalkin\" src=\"\/wp-content\/uploads\/2020\/05\/7585c4dc782627bac5b649e6a55e2ffb.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><em>Jag har en fr\u00e5ga om bitset. Mycket liknar implementeringen av C++ vector, optimerad bitset. Tog ni implementeringen d\u00e4rifr\u00e5n?<\/em><\/p>\n<p><\/p>\n<p>Nej, det \u00e4r inte d\u00e4rifr\u00e5n. Vid implementeringen av detta bitset utgick jag fr\u00e5n kunskapen om strukturen hos dessa ids tidsserier, som anv\u00e4nds i VictoriaMetrics. Deras struktur \u00e4r s\u00e5dan att de \u00f6vre 32 bitarna vanligtvis \u00e4r konstanta. De undre 32 bitarna kan f\u00f6r\u00e4ndras. Ju l\u00e4gre bit, desto oftare kan den f\u00f6r\u00e4ndras. D\u00e4rf\u00f6r \u00e4r denna implementering specifikt optimerad f\u00f6r denna databasstruktur. C++-implementeringen, s\u00e5 vitt jag vet, \u00e4r optimerad f\u00f6r allm\u00e4nna fall. Att g\u00f6ra en optimering f\u00f6r allm\u00e4nna fall inneb\u00e4r att den inte kommer att vara den mest optimala f\u00f6r det specifika fallet.<\/p>\n<p><\/p>\n<p>Jag rekommenderar att du ocks\u00e5 kollar p\u00e5 f\u00f6redraget av Alexej Milovid. Han pratade f\u00f6r ungef\u00e4r en m\u00e5nad sedan om optimeringar i ClickHouse f\u00f6r specifika specialiseringar. Han n\u00e4mner just att i allm\u00e4nhet \u00e4r C++-implementeringen eller n\u00e5gon annan implementation anpassad f\u00f6r att fungera bra i genomsnitt. Den kan fungera s\u00e4mre \u00e4n en specialiserad implementering f\u00f6r specifik kunskap, s\u00e5som v\u00e5r, n\u00e4r vi vet att de \u00f6vre 32 bitarna huvudsakligen \u00e4r konstanta.<\/p>\n<p><\/p>\n<p><em>Jag har en andra fr\u00e5ga. Vad \u00e4r den grundl\u00e4ggande skillnaden fr\u00e5n InfluxDB?<\/em><\/p>\n<p><\/p>\n<p>Det finns m\u00e5nga grundl\u00e4ggande skillnader. N\u00e4r det g\u00e4ller prestanda och minnesanv\u00e4ndning visar InfluxDB i tester 10 g\u00e5nger h\u00f6gre minnesanv\u00e4ndning f\u00f6r h\u00f6g kardinalitet tidsserier, n\u00e4r ni har m\u00e5nga av dem, till exempel miljoner. Till exempel anv\u00e4nder VictoriaMetrics 1 GB f\u00f6r en miljon aktiva serier, medan InfluxDB samtidigt anv\u00e4nder 10 GB. Och det \u00e4r en stor skillnad. <\/p>\n<p><\/p>\n<p>Den andra grundl\u00e4ggande skillnaden \u00e4r att InfluxDB har konstiga fr\u00e5gespr\u00e5k \u2013 Flux och InfluxQL. De \u00e4r inte s\u00e5 bekv\u00e4ma f\u00f6r att arbeta med tidsserier i j\u00e4mf\u00f6relse med <noindex><a rel=\"nofollow\" href=\"https:\/\/medium.com\/@valyala\/promql-tutorial-for-beginners-9ab455142085\">PromQL<\/a><\/noindex>, som st\u00f6ds i VictoriaMetrics. PromQL \u00e4r fr\u00e5gespr\u00e5ket fr\u00e5n Prometheus.<\/p>\n<p><\/p>\n<p>En annan skillnad \u00e4r att InfluxDB har en lite m\u00e4rklig datamodell, d\u00e4r varje rad kan inneh\u00e5lla flera f\u00e4lt med olika upps\u00e4ttningar av taggar. Dessa rader delas ocks\u00e5 in i olika tabeller. Dessa ytterligare komplikationer f\u00f6rsv\u00e5rar det fortsatta arbetet med databasen. Det \u00e4r sv\u00e5rt att underh\u00e5lla och f\u00f6rst\u00e5.<\/p>\n<p><\/p>\n<p>I VictoriaMetrics \u00e4r allt mycket enklare. D\u00e4r representerar varje tidsserie ett nyckel-v\u00e4rde-par. V\u00e4rdet \u00e4r en upps\u00e4ttning punkter \u2013 <code>(timestamp, value)<\/code>, och nyckeln \u00e4r en upps\u00e4ttning <code>label=value<\/code>Det finns ingen uppdelning i fields och measurements. Detta g\u00f6r att du kan v\u00e4lja vilken data som helst och sedan kombinera, addera, subtrahera, multiplicera och dividera, till skillnad fr\u00e5n InfluxDB d\u00e4r ber\u00e4kningar mellan olika rader fortfarande inte \u00e4r implementerade, s\u00e5vitt jag vet. \u00c4ven om de skulle vara det, \u00e4r det komplicerat och kr\u00e4ver massor av kod. <\/p>\n<p><\/p>\n<p><em>Jag har en uppf\u00f6ljande fr\u00e5ga. F\u00f6rstod jag r\u00e4tt att det fanns n\u00e5got problem som ni n\u00e4mnde, att denna inverterade index inte f\u00e5r plats i minnet, och d\u00e4rf\u00f6r g\u00e5r partitioneringen till?<\/em><\/p>\n<p><\/p>\n<p>Inledningsvis visade jag en naiv implementering av ett inverterat index p\u00e5 en standard Go-map. En s\u00e5dan implementation \u00e4r inte l\u00e4mplig f\u00f6r databaser, eftersom detta inverterade index inte sparas p\u00e5 disk, och databasen m\u00e5ste spara till disk f\u00f6r att dessa data ska f\u00f6rbli tillg\u00e4ngliga efter en omstart. I denna implementation kommer det inverterade indexet att f\u00f6rsvinna vid omstart av applikationen. Och du kommer att f\u00f6rlora \u00e5tkomst till alla data, eftersom du inte kommer att kunna hitta dem. <\/p>\n<p><\/p>\n<p><em>Hej! Tack f\u00f6r presentationen! Jag heter Pavel. Jag kommer fr\u00e5n f\u00f6retaget Wildberries. Jag har n\u00e5gra fr\u00e5gor till dig. F\u00f6rsta fr\u00e5gan. Vad tror du, om du hade valt en annan princip f\u00f6r att bygga arkitekturen av din applikation och partitionerat data efter tid, skulle du d\u00e5 kanske kunna g\u00f6ra datakorsningar vid s\u00f6kning, baserat p\u00e5 att data i en partition ligger f\u00f6r en viss tidsperiod, dvs. f\u00f6r ett visst tidsintervall, och du skulle inte beh\u00f6va bekymra dig om att du har bitar som \u00e4r spridda p\u00e5 olika s\u00e4tt? Fr\u00e5ga nummer 2 - eftersom ni implementerar en s\u00e5dan algoritm med bitset och allt annat, har ni kanske provat att anv\u00e4nda processorinstruktioner? Kanske har ni provat s\u00e5dana optimeringar?<\/em><\/p>\n<p><\/p>\n<p>Jag svarar direkt p\u00e5 den andra. Vi har \u00e4nnu inte kommit dit. Men om det beh\u00f6vs, g\u00f6r vi det. Och den f\u00f6rsta, vad var fr\u00e5gan?<\/p>\n<p><\/p>\n<p><em>Ni diskuterade tv\u00e5 scenarier. Och sa att ni valde den andra med en mer komplicerad implementation. Och ni f\u00f6redrog inte den f\u00f6rsta d\u00e4r data \u00e4r partitionerade efter tid.<\/em> <\/p>\n<p><\/p>\n<p>Ja. I det f\u00f6rsta fallet skulle den totala m\u00e4ngden av indexet vara st\u00f6rre, eftersom vi i varje partition skulle beh\u00f6va lagra dubbletter av data f\u00f6r de tidsserier som str\u00e4cker sig \u00f6ver dessa alla partitioner. Och om er churn rate f\u00f6r tidsserierna \u00e4r l\u00e5g, dvs. om st\u00e4ndigt samma serier anv\u00e4nds, s\u00e5 skulle vi i det f\u00f6rsta fallet f\u00f6rlora mycket mer utrymme j\u00e4mf\u00f6rt med det andra fallet.<\/p>\n<p><\/p>\n<p>Och ja, tidspartitionering \u00e4r ett bra alternativ. Det anv\u00e4nds av Prometheus. Men Prometheus har en annan nackdel. Vid sammanslagning av dessa datadelar beh\u00f6ver den h\u00e5lla metadata om alla labels och tidsserier i minnet. D\u00e4rf\u00f6r, om de datadelar som den sl\u00e5r ihop \u00e4r stora, \u00f6kar minnesanv\u00e4ndningen kraftigt under sammanslagningen, till skillnad fr\u00e5n VictoriaMetrics. Vid sammanslagningen f\u00f6rbrukar VictoriaMetrics \u00f6verhuvud taget inget minne, d\u00e4r anv\u00e4nds bara n\u00e5gra kilobyte, oavsett storleken p\u00e5 de sammanfogade datadelarna.<\/p>\n<p><\/p>\n<p><em>Algoritmen som ni anv\u00e4nder, den anv\u00e4nder minne. I minnet markeras tidsseriemetrik d\u00e4r det finns v\u00e4rden. P\u00e5 s\u00e5 s\u00e4tt kontrollerar ni om det finns parvis existens i en datam\u00e4ngd och i en annan. Och f\u00f6rst\u00e5r \u2013 har en intersection skett eller inte. Vanligtvis implementeras det i databaser med hj\u00e4lp av pekare, iteratorer som lagrar sitt nuvarande tillst\u00e5nd och l\u00f6per genom sorterad data vilket ger en enkel komplexitet f\u00f6r dessa operationer.<\/em> <\/p>\n<p><\/p>\n<p>Varf\u00f6r anv\u00e4nder vi inte pekare f\u00f6r att korsa data?<\/p>\n<p><\/p>\n<p><em>Ja.<\/em> <\/p>\n<p><\/p>\n<p>I v\u00e5r LevelDB eller i mergeset lagras just sorterade rader. Vi kan anv\u00e4nda en pekare f\u00f6r att g\u00e5 igenom och hitta korsningen. Men varf\u00f6r anv\u00e4nder vi inte det? F\u00f6r att det \u00e4r l\u00e5ngsamt. F\u00f6r att pekare inneb\u00e4r att f\u00f6r varje rad m\u00e5ste en funktion anropas. Anrop av en funktion tar 5 nanosekunder. Och om ni har 100 000 000 rader, s\u00e5 tar det en halv sekund bara f\u00f6r funktionsanropet.<\/p>\n<p><\/p>\n<p><em>S\u00e5dana problem finns, ja. Och min sista fr\u00e5ga. Fr\u00e5gan kanske l\u00e5ter lite konstig. Varf\u00f6r kan vi inte ber\u00e4kna alla n\u00f6dv\u00e4ndiga aggregat vid datainmatning och spara dem i \u00f6nskat format? Varf\u00f6r spara stora m\u00e4ngder i system som VictoriaMetrics, ClickHouse osv., f\u00f6r att sedan spendera s\u00e5 mycket tid p\u00e5 dem?<\/em><\/p>\n<p><\/p>\n<p><em>L\u00e5t mig ge ett exempel f\u00f6r att g\u00f6ra det tydligare. Anta att hur en liten leksakshastighetsm\u00e4tare fungerar? Den registrerar avst\u00e5ndet du har k\u00f6rt, hela tiden adderande det till en figur, och i en annan \u2013 tiden. Och delar. Och f\u00e5r den genomsnittliga hastigheten. Man kan g\u00f6ra n\u00e5got liknande. Samla ihop alla n\u00f6dv\u00e4ndiga fakta.<\/em><\/p>\n<p><\/p>\n<p>Okej, jag f\u00f6rst\u00e5r fr\u00e5gan. Ditt exempel har sin plats. Om du vet vilka aggregationer du beh\u00f6ver, s\u00e5 \u00e4r det den b\u00e4sta implementeringen. Men problemet \u00e4r att folk sparar dessa metrikor, vissa data i ClickHouse och de vet \u00e4nnu inte hur de kommer att aggregera och filtrera dem i framtiden, s\u00e5 de m\u00e5ste spara alla r\u00e5data. Men om du vet att du beh\u00f6ver r\u00e4kna n\u00e5got genomsnittligt, varf\u00f6r r\u00e4knar du inte det ist\u00e4llet f\u00f6r att spara en massa r\u00e5a v\u00e4rden d\u00e4r? Men det \u00e4r bara om du exakt vet vad du beh\u00f6ver.<\/p>\n<p><\/p>\n<p>F\u00f6rresten, databaser f\u00f6r lagring av tidsserier st\u00f6djer ber\u00e4kning av aggregationer. Till exempel, Prometheus st\u00f6djer <noindex><a rel=\"nofollow\" href=\"https:\/\/prometheus.io\/docs\/prometheus\/latest\/configuration\/recording_rules\/\">inspelning regler<\/a><\/noindex>. Det vill s\u00e4ga, detta kan g\u00f6ras om du vet vilka aggregationer du kommer att beh\u00f6va. I VictoriaMetrics finns det inte \u00e4n, men vanligtvis placeras Prometheus framf\u00f6r den, d\u00e4r detta kan g\u00f6ras i inspelningsreglerna.<\/p>\n<p><\/p>\n<p>Till exempel, p\u00e5 mitt tidigare jobb beh\u00f6vde vi r\u00e4kna antal h\u00e4ndelser i ett glidande f\u00f6nster under den senaste timmen. Problemet \u00e4r att jag var tvungen att g\u00f6ra en anpassad implementation i Go, det vill s\u00e4ga en tj\u00e4nst f\u00f6r att r\u00e4kna denna sak. Denna tj\u00e4nst blev slutligen ganska komplex eftersom det \u00e4r sv\u00e5rt att r\u00e4kna. Implementeringen kan vara enkel om du beh\u00f6ver r\u00e4kna n\u00e5gra aggregationer vid fasta tidsintervall. Men om du vill r\u00e4kna h\u00e4ndelser i ett glidande f\u00f6nster, \u00e4r det inte s\u00e5 enkelt som det verkar. Jag tror att det fortfarande inte har implementerats i ClickHouse eller i tidsserie-databaser, eftersom det \u00e4r sv\u00e5rt att implementera.<\/p>\n<p><\/p>\n<p><em>Och en sista fr\u00e5ga. Vi har just pratat om att r\u00e4kna genomsnitt, och jag kom ih\u00e5g att det en g\u00e5ng fanns n\u00e5got som Graphite med backend Carbon. Den kunde tunna ut gamla data, det vill s\u00e4ga l\u00e4mna en punkt per minut, en punkt per timme, osv. I princip \u00e4r det ganska bekv\u00e4mt om vi beh\u00f6ver r\u00e5data, s\u00e5 att s\u00e4ga, under en m\u00e5nad, och allt annat kan tunas ut. Men Prometheus, VictoriaMetrics st\u00f6djer inte denna funktionalitet. Planerar ni att st\u00f6dja det? Om inte, varf\u00f6r?<\/em><\/p>\n<p><\/p>\n<p>Tack f\u00f6r din fr\u00e5ga. V\u00e5ra anv\u00e4ndare st\u00e4ller den ibland. De undrar n\u00e4r vi kommer att l\u00e4gga till st\u00f6d f\u00f6r nedsk\u00e4rning (downsampling). H\u00e4r finns det flera problem. F\u00f6r det f\u00f6rsta, vad varje anv\u00e4ndare menar med <code>nedsk\u00e4rning<\/code> \u00e4r olika: n\u00e5gon vill f\u00e5 en godtycklig punkt inom ett angivet intervall, n\u00e5gon annan vill ha maximala, minimala, eller genomsnittliga v\u00e4rden. Om m\u00e5nga system skriver data till din databas kan du inte bara behandla dem som om de vore samma. Det kan visa sig att varje system beh\u00f6ver anv\u00e4nda olika nedsk\u00e4rningar. Och det \u00e4r komplicerat att implementera.<\/p>\n<p><\/p>\n<p>F\u00f6r det andra, VictoriaMetrics, precis som ClickHouse, \u00e4r optimerad f\u00f6r att arbeta med stora m\u00e4ngder r\u00e5data, s\u00e5 den kan bearbeta en miljard rader p\u00e5 mindre \u00e4n en sekund om du har m\u00e5nga k\u00e4rnor i ditt system. Skanning av tidsserie-punkter i VictoriaMetrics \u2013 50 000 000 punkter per sekund per k\u00e4rna. Och denna prestanda skalas med tillg\u00e4ngliga k\u00e4rnor. Om du har 20 k\u00e4rnor, till exempel, s\u00e5 blir det en miljard punkter per sekund. Och denna egenskap hos VictoriaMetrics och ClickHouse minskar behovet av nedsk\u00e4rning.<\/p>\n<p><\/p>\n<p>En annan egenskap \u00e4r att VictoriaMetrics effektivt komprimerar dessa data. Kompressionen ligger i genomsnitt p\u00e5 0,4 till 0,8 byte per punkt i produktion. Varje punkt best\u00e5r av en tidsst\u00e4mpel + ett v\u00e4rde. Och den komprimeras till mindre \u00e4n en byte i genomsnitt. <\/p>\n<p><\/p>\n<p><em>Sergej. Jag har en fr\u00e5ga. Vad \u00e4r den minimi tidskvant f\u00f6r registrering?<\/em><\/p>\n<p><\/p>\n<p>En millisekund. Nyligen hade vi ett samtal med andra utvecklare av tidsserie-databaser. Hos dem \u00e4r den minimi tidskvanten en sekund. I Graphite, till exempel, \u00e4r det ocks\u00e5 en sekund. I OpenTSDB \u00e4r det ocks\u00e5 en sekund. I InfluxDB \u2013 nanosekundprecision. I VictoriaMetrics \u00e4r det en millisekund, eftersom det i Prometheus \u00e4r en millisekund. Och VictoriaMetrics utvecklades ursprungligen som fj\u00e4rrlagring f\u00f6r Prometheus. Men nu kan den ocks\u00e5 spara data fr\u00e5n andra system. <\/p>\n<p><\/p>\n<p>M\u00e4nniskan jag pratade med s\u00e4ger att de har sekundprecision \u2013 det r\u00e4cker f\u00f6r dem, eftersom det beror p\u00e5 vilken typ av data som lagras i tidsseriedatabasen. Om det \u00e4r DevOps-data eller data fr\u00e5n infrastruktur, d\u00e4r du samlar in dem med intervaller p\u00e5 30 sekunder eller en minut, s\u00e5 r\u00e4cker sekundprecision. Mindre beh\u00f6vs inte. Men om du samlar in dessa data fr\u00e5n h\u00f6gfrekventa handelssystem, d\u00e5 beh\u00f6vs nanosekundprecision.<\/p>\n<p><\/p>\n<p>Millisekundnog noga i precision i VictoriaMetrics passar b\u00e5de f\u00f6r DevOps-fall och kan passa de flesta av de fall jag n\u00e4mnde i b\u00f6rjan av presentationen. Det enda den kanske inte \u00e4r l\u00e4mplig f\u00f6r \u00e4r h\u00f6gfrekventa handelssystem.<\/p>\n<p><\/p>\n<p><em>Tack! Och en fr\u00e5ga till. Vad \u00e4r kompatibiliteten i PromQL?<\/em><\/p>\n<p><\/p>\n<p>Full bak\u00e5tkompatibilitet. VictoriaMetrics st\u00f6djer helt och h\u00e5llet PromQL. Ut\u00f6ver detta l\u00e4gger den till ytterligare ut\u00f6kad funktionalitet p\u00e5 PromQL, vilket kallas <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/VictoriaMetrics\/VictoriaMetrics\/wiki\/MetricsQL\">MetricsQL<\/a><\/noindex>. Ang\u00e5ende denna ut\u00f6kade funktionalitet finns det en presentation p\u00e5 YouTube. Jag pratade p\u00e5 Monitoring Meetup i v\u00e5ras i Petersburg.<\/p>\n<p><\/p>\n<p>Telegram-kanal <noindex><a rel=\"nofollow\" href=\"https:\/\/t.me\/VictoriaMetrics_ru1\">VictoriaMetrics<\/a><\/noindex>.<\/p>\n<p class=\"for_users_only_msg\">Endast registrerade anv\u00e4ndare kan delta i omr\u00f6stningen. <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/auth\/login\/\">Logga in<\/a><\/noindex>, tack.<\/p>\n<h2 class=\"default-block__polling-title\">Vad hindrar dig fr\u00e5n att byta till VictoriaMetrics som en l\u00e5ngsiktig lagring f\u00f6r Prometheus? (Skriv i kommentarerna, jag l\u00e4gger till det i enk\u00e4ten))<\/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>Anv\u00e4nder inte Prometheus5<\/p>\n<\/li>\n<li class=\"poll-result__item\">\n<p>                <strong class=\"poll-result__data-percent\">28,6%<\/strong>Visste inte om VictoriaMetrics2<\/p>\n<\/li>\n<\/ul>\n<p>    7 anv\u00e4ndare r\u00f6stade. 12 anv\u00e4ndare avstod.<br \/>\n<br \/>K\u00e4lla: <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.4 - 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\/sv\/blog\/administrirovanie\/go-optimizations-in-victoriametrics-aleksandr-valyalkin\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.4\" \/>\n\t\t<meta property=\"og:locale\" content=\"sv_SE\" \/>\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\/sv\/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-optimeringar i VictoriaMetrics. Alexander Valjalkin | ProHoster","description":"Jag vill g\u00e4rna dela med mig av en sammanfattning av Alexandr Valjalkins f\u00f6redrag fr\u00e5n slutet av 2019 \"Go-optimeringar i VictoriaMetrics\"","canonical_url":"https:\/\/prohoster.info\/sv\/blog\/administrirovanie\/go-optimizations-in-victoriametrics-aleksandr-valyalkin","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"sv_SE","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\/sv\/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\/sv\/wp-json\/wp\/v2\/posts\/80733","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/comments?post=80733"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/posts\/80733\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/media\/80734"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/media?parent=80733"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/categories?post=80733"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/tags?post=80733"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}