{"id":79467,"date":"2020-04-27T07:42:23","date_gmt":"2020-04-27T05:42:23","guid":{"rendered":"https:\/\/prohoster.info\/blog\/administrirovanie\/operativnaya-analitika-v-mikroservisnoj-arhitekture-p%cc%b6o%cc%b6n%cc%b6ya%cc%b6t%cc%b6%cc%b6-%cc%b6i%cc%b6-%cc%b6p%cc%b6r%cc%b6o%cc%b6s%cc%b6t%cc%b6i%cc%b6t%cc%b6%cc%b6-pomoch-i-podskazat-postgres"},"modified":"2020-04-27T07:42:23","modified_gmt":"2020-04-27T05:42:23","slug":"operativnaya-analitika-v-mikroservisnoj-arhitekture-p%cc%b6o%cc%b6n%cc%b6ya%cc%b6t%cc%b6%cc%b6-%cc%b6i%cc%b6-%cc%b6p%cc%b6r%cc%b6o%cc%b6s%cc%b6t%cc%b6i%cc%b6t%cc%b6%cc%b6-pomoch-i-podskazat-postgres","status":"publish","type":"post","link":"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/operativnaya-analitika-v-mikroservisnoj-arhitekture-p%cc%b6o%cc%b6n%cc%b6ya%cc%b6t%cc%b6%cc%b6-%cc%b6i%cc%b6-%cc%b6p%cc%b6r%cc%b6o%cc%b6s%cc%b6t%cc%b6i%cc%b6t%cc%b6%cc%b6-pomoch-i-podskazat-postgres","title":{"rendered":"Analityka operacyjna w architekturze mikroserwisowej: z\u0336a\u0336r\u0336o\u0336z\u0336y\u0336 \u0336i\u0336 \u0336u\u0336l\u0336o\u0336g\u0336i\u0336c\u0336z\u0336y\u0336 \u0336pomaga i wskazuje Postgres FDW","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Architektura mikroserwisowa, podobnie jak wszystko w tym \u015bwiecie, ma swoje zalety i wady. Niekt\u00f3re procesy staj\u0105 si\u0119 prostsze, inne \u2014 bardziej skomplikowane. W imi\u0119 szybko\u015bci zmian i lepszej skalowalno\u015bci trzeba ponosi\u0107 pewne ofiary. Jedn\u0105 z nich jest skomplikowanie analityki. Je\u015bli w monolicie ca\u0142\u0105 operacyjn\u0105 analityk\u0119 mo\u017cna sprowadzi\u0107 do zapyta\u0144 SQL do repliki analitycznej, to w architekturze wieloserwisowej ka\u017cda us\u0142uga ma swoj\u0105 baz\u0119 i wydaje si\u0119, \u017ce jednym zapytaniem si\u0119 nie obejdzie (a mo\u017ce si\u0119 obejdzie?). Dla tych, kt\u00f3rzy s\u0105 ciekawi, jak rozwi\u0105zali\u015bmy problem operacyjnej analityki w naszej firmie i jak nauczyli\u015bmy si\u0119 \u017cy\u0107 z tym rozwi\u0105zaniem \u2014 zapraszamy.<\/p>\n<p><img decoding=\"async\" alt=\"Analityka operacyjna w architekturze mikroserwisowej: z\u0336a\u0336r\u0336o\u0336z\u0336y\u0336 \u0336i\u0336 \u0336u\u0336l\u0336o\u0336g\u0336i\u0336c\u0336z\u0336y\u0336 \u0336pomaga i wskazuje Postgres FDW\" src=\"\/wp-content\/uploads\/2020\/04\/4296389f06d488999cc023dcaa3027f7.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nNazywam si\u0119 Pawe\u0142 Siwa\u015b, w DomKlick pracuj\u0119 w zespole, kt\u00f3ry odpowiada za utrzymanie analitycznego magazynu danych. Nasz\u0105 dzia\u0142alno\u015b\u0107 mo\u017cna w du\u017cej mierze klasyfikowa\u0107 jako in\u017cynieri\u0119 danych, ale tak naprawd\u0119 zakres zada\u0144 jest znacznie szerszy. Obejmuje standardowe dla in\u017cynierii danych ETL\/ELT, wsparcie i adaptacj\u0119 narz\u0119dzi analitycznych oraz rozw\u00f3j w\u0142asnych narz\u0119dzi. W szczeg\u00f3lno\u015bci, dla raportowania operacyjnego postanowili\u015bmy \u00abudawa\u0107\u00bb, \u017ce mamy monolit i da\u0107 analitykom jedn\u0105 baz\u0119, w kt\u00f3rej b\u0119d\u0105 zawarte wszystkie niezb\u0119dne im dane. <noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<p>Generalnie rozwa\u017cali\u015bmy r\u00f3\u017cne opcje. Mo\u017cna by\u0142o stworzy\u0107 pe\u0142noprawne magazyn \u2014 nawet pr\u00f3bowali\u015bmy, ale szczerze m\u00f3wi\u0105c, nie uda\u0142o nam si\u0119 zsynchronizowa\u0107 do\u015b\u0107 cz\u0119stych zmian w logice z do\u015b\u0107 wolnym procesem budowania magazynu i wprowadzania w nim zmian (je\u015bli komu\u015b si\u0119 uda\u0142o, prosz\u0119 napisa\u0107 w komentarzach jak). Mo\u017cna by\u0142o powiedzie\u0107 analitykom: \u201eCh\u0142opaki, uczcie si\u0119 Pythona i przechod\u017acie do replik analitycznych\u201d, ale to dodatkowy warunek przy rekrutacji, kt\u00f3rego chcieli\u015bmy unikn\u0105\u0107, je\u015bli to mo\u017cliwe. Postanowili\u015bmy spr\u00f3bowa\u0107 zastosowa\u0107 technologi\u0119 FDW (Foreign Data Wrapper): w zasadzie, to standardowy dblink, kt\u00f3ry znajduje si\u0119 w standardzie SQL, ale z znacznie bardziej przyjaznym interfejsem. Na jego podstawie stworzyli\u015bmy rozwi\u0105zanie, kt\u00f3re ostatecznie si\u0119 sprawdzi\u0142o i na nim si\u0119 zatrzymali\u015bmy. Szczeg\u00f3\u0142y s\u0105 tematem osobnego artyku\u0142u, a mo\u017ce nawet nie jednego, poniewa\u017c chcia\u0142oby si\u0119 opowiedzie\u0107 o wielu rzeczach: od synchronizacji schemat\u00f3w baz do zarz\u0105dzania dost\u0119pem i anonimizacji danych osobowych. Nale\u017cy r\u00f3wnie\u017c zaznaczy\u0107, \u017ce to rozwi\u0105zanie nie jest zamiennikiem realnych baz analitycznych i magazyn\u00f3w, a jedynie rozwi\u0105zuje konkretne zadanie.<\/p>\n<p>Na wysokim poziomie wygl\u0105da to tak:<\/p>\n<p><img decoding=\"async\" alt=\"Analityka operacyjna w architekturze mikroserwisowej: z\u0336a\u0336r\u0336o\u0336z\u0336y\u0336 \u0336i\u0336 \u0336u\u0336l\u0336o\u0336g\u0336i\u0336c\u0336z\u0336y\u0336 \u0336pomaga i wskazuje Postgres FDW\" src=\"\/wp-content\/uploads\/2020\/04\/574da29dfdb40706afe9e817e789a61b.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nJest baza PostgreSQL, gdzie u\u017cytkownicy mog\u0105 przechowywa\u0107 swoje dane robocze, a co najwa\u017cniejsze \u2014 do tej bazy przez FDW pod\u0142\u0105czone s\u0105 analityczne repliki wszystkich us\u0142ug. To pozwala napisa\u0107 zapytanie do kilku baz, niezale\u017cnie od tego, czy s\u0105 to: PostgreSQL, MySQL, MongoDB, czy co\u015b innego (plik, API, a je\u015bli nie ma odpowiedniego wrappera, mo\u017cna napisa\u0107 sw\u00f3j). No, to wszystko, super! Rozchodzimy si\u0119?<\/p>\n<p>Gdyby wszystko ko\u0144czy\u0142o si\u0119 tak szybko i prosto, to pewnie nie by\u0142oby artyku\u0142u.<\/p>\n<p>Wa\u017cy uwzgl\u0119dni\u0107, jak PostgreSQL przetwarza zapytania do zdalnych serwer\u00f3w. Wydaje si\u0119 to logiczne, jednak cz\u0119sto nie zwraca si\u0119 na to uwagi: PostgreSQL dzieli zapytanie na cz\u0119\u015bci, kt\u00f3re s\u0105 wykonywane na zdalnych serwerach niezale\u017cnie, zbiera te dane, a finalne obliczenia prowadzi sam, dlatego szybko\u015b\u0107 wykonania zapytania b\u0119dzie w du\u017cej mierze zale\u017cna od tego, jak jest napisane. Nale\u017cy tak\u017ce zauwa\u017cy\u0107, \u017ce gdy dane przychodz\u0105 z zdalnego serwera, nie maj\u0105 ju\u017c indeks\u00f3w, nie ma nic, co pomog\u0142oby planistowi, wi\u0119c tylko my mo\u017cemy mu pom\u00f3c i podpowiedzie\u0107. I o tym w\u0142a\u015bnie chcia\u0142bym opowiedzie\u0107 szczeg\u00f3\u0142owo.<\/p>\n<h1>Prosty zapytanie i plan z nim<\/h1>\n<p>\nAby pokaza\u0107, jak Postgres wykonuje zapytanie do tabeli zawieraj\u0105cej 6 milion\u00f3w wierszy na zdalnym serwerze, <a class=\"wpil_keyword_link\" href=\"https:\/\/prohoster.info\/server\/dts-dronten\/\"   title=\"serwerze\" data-wpil-keyword-link=\"linked\"  data-wpil-monitor-id=\"2589\">serwerze<\/a>, przyjrzyjmy si\u0119 prostemu planowi.<\/p>\n<pre><code class=\"sql\">wyja\u015bnij analiz\u0119 szczeg\u00f3\u0142owo  \nWYBIERZ count(1)\nZ fdw_schema.tabeli;\n\nAgregat  (koszt=418383.23..418383.24 wierszy=1 szeroko\u015b\u0107=8) (rzeczywisty czas=3857.198..3857.198 wierszy=1 p\u0119tle=1)\n  Wynik: count(1)\n  -&gt;  zdalne skanowanie w fdw_schema.&quot;tabela&quot;  (koszt=100.00..402376.14 wierszy=6402838 szeroko\u015b\u0107=0) (rzeczywisty czas=4.874..3256.511 wierszy=6406868 p\u0119tle=1)\n        Wynik: &quot;tabela&quot;.id, &quot;tabela&quot;.is_active, &quot;tabela&quot;.meta, &quot;tabela&quot;.created_dt\n        Zdalne SQL: WYBIERZ NULL Z fdw_schema.tabeli\nCzas planowania: 0.986 ms\nCzas wykonania: 3857.436 ms<\/code><\/pre>\n<p>\nU\u017cycie instrukcji VERBOSE pozwala zobaczy\u0107 zapytanie, kt\u00f3re zostanie wys\u0142ane do zdalnego serwera oraz wyniki, kt\u00f3re otrzymamy do dalszego przetwarzania (linia RemoteSQL).<\/p>\n<p>Zr\u00f3bmy krok dalej i dodajmy do naszego zapytania kilka filtr\u00f3w: jeden wed\u0142ug <b>boolean<\/b> pola, jeden wed\u0142ug wyst\u0105pienia <b>timestamp<\/b> w interwale i jeden wed\u0142ug <b>jsonb<\/b>.<\/p>\n<pre><code class=\"sql\">wyja\u015bnij analiz\u0119 szczeg\u00f3\u0142owo\nWYBIERZ count(1)\nZ fdw_schema.tabeli \nGDZIE is_active jest Prawda\nI created_dt POMI\u0118DZY OBECNA_DATA - INTERWA\u0141 '7 miesi\u0119cy' \nI OBECNA_DATA - INTERWA\u0141 '6 miesi\u0119cy'\nI meta-&gt;&gt;'source' = 'test';\n\nAgregat  (koszt=577487.69..577487.70 wierszy=1 szeroko\u015b\u0107=8) (rzeczywisty czas=27473.818..25473.819 wierszy=1 p\u0119tle=1)\n  Wynik: count(1)\n  -&gt;  zdalne skanowanie w fdw_schema.&quot;tabela&quot;  (koszt=100.00..577469.21 wierszy=7390 szeroko\u015b\u0107=0) (rzeczywisty czas=31.369..25372.466 wierszy=1360025 p\u0119tle=1)\n        Wynik: &quot;tabela&quot;.id, &quot;tabela&quot;.is_active, &quot;tabela&quot;.meta, &quot;tabela&quot;.created_dt\n        Filtr: ((&quot;tabela&quot;.is_active JEST PRAWDA) I ((&quot;tabela&quot;.meta -&gt;&gt; 'source'::tekst) = 'test'::tekst) I (&quot;tabela&quot;.created_dt &gt;= (('teraz'::cstring)::data - '7 miesi\u0119cy'::interwa\u0142)) I (&quot;tabela&quot;.created_dt &lt;= ((('teraz'::cstring)::data)::znacznik czasowy ze stref\u0105 czasow\u0105 - '6 miesi\u0119cy'::interwa\u0142)))\n        Usuni\u0119te wiersze z powodu filtra: 5046843\n        Zdalne SQL: WYBIERZ created_dt, is_active, meta Z fdw_schema.tabeli\nCzas planowania: 0.665 ms\nCzas wykonania: 27474.118 ms<\/code><\/pre>\n<p>\nTo w\u0142a\u015bnie tutaj kryje si\u0119 moment, na kt\u00f3ry nale\u017cy zwr\u00f3ci\u0107 uwag\u0119 przy pisaniu zapyta\u0144. Filtry nie zosta\u0142y przekazane na zdalny serwer, co oznacza, \u017ce Postgres pobiera wszystkie 6 milion\u00f3w wierszy, aby p\u00f3\u017aniej lokalnie odfiltrowa\u0107 (linia Filter) i wykona\u0107 agregacj\u0119. Kluczem do sukcesu jest napisanie zapytania w taki spos\u00f3b, aby filtry by\u0142y przekazywane do zdalnej maszyny, a my otrzymywali\u015bmy i agregowali tylko istotne wiersze. <\/p>\n<h1>To jaki\u015b bzdurny boolean<\/h1>\n<p>\nZ polami boolean jest prosto. W pierwotnym zapytaniu problem pojawi\u0142 si\u0119 z powodu operatora <b>is<\/b>. Je\u015bli zamienimy go na <b>=<\/b>, otrzymamy nast\u0119puj\u0105cy wynik:<\/p>\n<pre><code class=\"sql\">wyja\u015bnij analiz\u0119 szczeg\u00f3\u0142owo\nWYBIERZ count(1)\nZ fdw_schema.tabeli\nGDZIE is_active = Prawda\nI created_dt POMI\u0118DZY OBECNA_DATA - INTERWA\u0141 '7 miesi\u0119cy' \nI OBECNA_DATA - INTERWA\u0141 '6 miesi\u0119cy'\nI meta-&gt;&gt;'source' = 'test';\n\nAgregat  (koszt=508010.14..508010.15 wierszy=1 szeroko\u015b\u0107=8) (rzeczywisty czas=19064.314..19064.314 wierszy=1 p\u0119tle=1)\n  Wynik: count(1)\n  -&gt;  zdalne skanowanie w fdw_schema.&quot;tabela&quot;  (koszt=100.00..507988.44 wierszy=8679 szeroko\u015b\u0107=0) (rzeczywisty czas=33.035..18951.278 wierszy=1360025 p\u0119tle=1)\n        Wynik: &quot;tabela&quot;.id, &quot;tabela&quot;.is_active, &quot;tabela&quot;.meta, &quot;tabela&quot;.created_dt\n        Filtr: (((&quot;tabela&quot;.meta -&gt;&gt; 'source'::tekst) = 'test'::tekst) I (&quot;tabela&quot;.created_dt &gt;= (('teraz'::cstring)::data - '7 miesi\u0119cy'::interwa\u0142)) I (&quot;tabela&quot;.created_dt &lt;= ((('teraz'::cstring)::data)::znacznik czasowy ze stref\u0105 czasow\u0105 - '6 miesi\u0119cy'::interwa\u0142)))\n        Usuni\u0119te wiersze z powodu filtra: 3567989\n        Zdalne SQL: WYBIERZ created_dt, meta Z fdw_schema.tabeli GDZIE (is_active)\nCzas planowania: 0.834 ms\nCzas wykonania: 19064.534 ms<\/code><\/pre>\n<p>\nJak widzicie, filtr przeszed\u0142 na zdalny serwer, a czas wykonania skr\u00f3ci\u0142 si\u0119 z 27 do 19 sekund. <\/p>\n<p>Warto zauwa\u017cy\u0107, \u017ce operator <b>is<\/b> r\u00f3\u017cni si\u0119 od operatora <b>=<\/b> tym, \u017ce potrafi pracowa\u0107 z warto\u015bci\u0105 Null. Oznacza to, \u017ce <b>is not True<\/b> w filtrze pozostawi warto\u015bci False i Null, podczas gdy <b>!= True<\/b> zostawi tylko warto\u015bci False. Dlatego przy zamianie operatora <b>is not<\/b> nale\u017cy przekaza\u0107 do filtru dwa warunki z operatorem OR, na przyk\u0142ad, <b>WHERE (col != True) OR (col is null)<\/b>.<\/p>\n<p>Ze zmienn\u0105 boolean si\u0119 uporali\u015bmy, przechodzimy dalej. A tymczasem przywr\u00f3\u0107my filtr po warto\u015bci logicznej do pierwotnej formy, aby niezale\u017cnie rozwa\u017cy\u0107 efekt innych zmian.<\/p>\n<h1>timestamptz? hz<\/h1>\n<p>\nCz\u0119sto trzeba eksperymentowa\u0107 z tym, jak prawid\u0142owo napisa\u0107 zapytanie, w kt\u00f3rym bior\u0105 udzia\u0142 zdalne serwery, a dopiero potem szuka\u0107 wyja\u015bnienia, dlaczego tak si\u0119 dzieje. W Internecie mo\u017cna znale\u017a\u0107 bardzo ma\u0142o informacji na ten temat. W trakcie eksperyment\u00f3w odkryli\u015bmy, \u017ce filtr po sta\u0142ej dacie dzia\u0142a bez problemu na zdalnym serwerze, ale gdy chcemy ustawi\u0107 dat\u0119 dynamicznie, na przyk\u0142ad now() lub OBECNA_DATA, to ju\u017c tak si\u0119 nie dzieje. W naszym przyk\u0142adzie dodali\u015bmy taki filtr, aby kolumna created_at zawiera\u0142a dane z dok\u0142adnie sprzed 1 miesi\u0105ca (POMI\u0118DZY OBECNA_DATA \u2014 INTERWA\u0141 '7 miesi\u0119cy' I OBECNA_DATA \u2014 INTERWA\u0141 '6 miesi\u0119cy'). Co zrobili\u015bmy w tym przypadku?<\/p>\n<pre><code class=\"sql\">wyja\u015bnij analiz\u0119 szczeg\u00f3\u0142ow\u0105\nSELECT count(1)\nFROM fdw_schema.table \nWHERE is_active is True\nAND created_dt &gt;= (SELECT CURRENT_DATE::timestamptz - INTERVAL '7 miesi\u0105ce') \nAND created_dt &gt;'source' = 'test';\n\nAgregat  (koszt=306875.17..306875.18 wierszy=1 szeroko\u015b\u0107=8) (czas rzeczywisty=4789.114..4789.115 wierszy=1 p\u0119tli=1)\n  Wyj\u015bcie: count(1)\n  InitPlan 1 (zwraca $0)\n    -&gt;  Wynik  (koszt=0.00..0.02 wierszy=1 szeroko\u015b\u0107=8) (czas rzeczywisty=0.007..0.008 wierszy=1 p\u0119tli=1)\n          Wyj\u015bcie: ((('now'::cstring)::date)::timestamp with time zone - '7 mons'::interval)\n  InitPlan 2 (zwraca $1)\n    -&gt;  Wynik  (koszt=0.00..0.02 wierszy=1 szeroko\u015b\u0107=8) (czas rzeczywisty=0.002..0.002 wierszy=1 p\u0119tli=1)\n          Wyj\u015bcie: ((('now'::cstring)::date)::timestamp with time zone - '6 mons'::interval)\n  -&gt;  Skanowanie zdalne na fdw_schema.\"table\"  (koszt=100.02..306874.86 wierszy=105 szeroko\u015b\u0107=0) (czas rzeczywisty=23.475..4681.419 wierszy=1360025 p\u0119tli=1)\n        Wyj\u015bcie: \"table\".id, \"table\".is_active, \"table\".meta, \"table\".created_dt\n        Filtr: ((\"table\".is_active IS TRUE) AND ((\"table\".meta -&gt;&gt; 'source'::text) = 'test'::text))\n        Wiersze usuni\u0119te przez filtr: 76934\n        Zdalne SQL: SELECT is_active, meta FROM fdw_schema.table WHERE ((created_dt &gt;= $1::timestamp with time zone)) AND ((created_dt &lt; $2::timestamp with time zone))\nCzas planowania: 0.703 ms\nCzas wykonania: 4789.379 ms<\/code><\/pre>\n<p>\nSugerowali\u015bmy planowaniu wst\u0119pnie obliczy\u0107 dat\u0119 w podzapytaniu i przekaza\u0107 gotow\u0105 zmienn\u0105 do filtru. Ta sugestia przynios\u0142a nam wspania\u0142y rezultat, zapytanie sta\u0142o si\u0119 szybsze prawie 6 razy!<\/p>\n<p>Ponownie, wa\u017cne jest, aby by\u0107 ostro\u017cnym: typ danych w podzapytaniu musi by\u0107 taki sam, co pole, wed\u0142ug kt\u00f3rego filtrujemy, w przeciwnym razie planista zdecyduje, \u017ce typy s\u0105 r\u00f3\u017cne i konieczne jest najpierw pobranie wszystkich danych, a nast\u0119pnie lokalne filtrowanie.<\/p>\n<p>Przywr\u00f3\u0107my filtr daty do pierwotnej warto\u015bci.<\/p>\n<h1>Freddy vs. Jsonb<\/h1>\n<p>\nW sumie, pola logiczne i daty ju\u017c wystarczaj\u0105co przyspieszy\u0142y nasze zapytanie, jednak pozostawa\u0142 jeszcze jeden typ danych. Walka z filtrowaniem wed\u0142ug niego, szczerze m\u00f3wi\u0105c, nadal trwa, chocia\u017c tutaj s\u0105 ju\u017c pewne sukcesy. Tak wi\u0119c, oto jak uda\u0142o nam si\u0119 przekaza\u0107 filtr wed\u0142ug <b>jsonb<\/b> pola na zdalny serwer.<\/p>\n<pre><code class=\"sql\">wyja\u015bnij analiz\u0119 szczeg\u00f3\u0142ow\u0105\nSELECT count(1)\nFROM fdw_schema.table \nWHERE is_active is True\nAND created_dt BETWEEN CURRENT_DATE - INTERVAL '7 miesi\u0119cy' \nAND CURRENT_DATE - INTERVAL '6 miesi\u0119cy'\nAND meta @&gt; '{\"source\":\"test\"}'::jsonb;\n\nAgregat  (koszt=245463.60..245463.61 wierszy=1 szeroko\u015b\u0107=8) (czas rzeczywisty=6727.589..6727.590 wierszy=1 p\u0119tli=1)\n  Wyj\u015bcie: count(1)\n  -&gt;  Skanowanie zdalne na fdw_schema.\"table\"  (koszt=1100.00..245459.90 wierszy=1478 szeroko\u015b\u0107=0) (czas rzeczywisty=16.213..6634.794 wierszy=1360025 p\u0119tli=1)\n        Wyj\u015bcie: \"table\".id, \"table\".is_active, \"table\".meta, \"table\".created_dt\n        Filtr: ((\"table\".is_active IS TRUE) AND (\"table\".created_dt &gt;= (('now'::cstring)::date - '7 mons'::interval)) AND (\"table\".created_dt  '{\"source\": \"test\"}'::jsonb))\nCzas planowania: 0.747 ms\nCzas wykonania: 6727.815 ms<\/code><\/pre>\n<p>\nZamiast operator\u00f3w filtrowania nale\u017cy u\u017cywa\u0107 operatora is present <b>jsonb<\/b> w innym. 7 sekund zamiast pierwotnych 29. To jak na razie jedyny udany spos\u00f3b przesy\u0142ania filtr\u00f3w przez <b>jsonb<\/b> na zdalny serwer, ale wa\u017cne jest, aby uwzgl\u0119dni\u0107 jedno ograniczenie: u\u017cywamy wersji bazy 9.6, jednak do ko\u0144ca kwietnia planujemy zako\u0144czy\u0107 ostatnie testy i przej\u015b\u0107 na wersj\u0119 12. Gdy si\u0119 zaktualizujemy, napiszemy, jak to wp\u0142yn\u0119\u0142o, poniewa\u017c zmian, na kt\u00f3re wielu liczy, jest ca\u0142kiem sporo: json_path, nowe zachowanie CTE, push down (istniej\u0105ce od wersji 10). Naprawd\u0119 chcemy to jak najszybciej przetestowa\u0107.<\/p>\n<h1>Zako\u0144cz go<\/h1>\n<p>\nSprawdzili\u015bmy, jak ka\u017cda zmiana wp\u0142ywa na szybko\u015b\u0107 zapytania indywidualnie. Teraz przyjrzyjmy si\u0119, co si\u0119 stanie, gdy wszystkie trzy filtry b\u0119d\u0105 napisane poprawnie.<\/p>\n<pre><code class=\"sql\">wyja\u015bnij analizuj szczeg\u00f3\u0142owo\nSELECT count(1)\nFROM fdw_schema.table \nWHERE is_active = True\nAND created_dt &gt;= (SELECT CURRENT_DATE::timestamptz - INTERVAL '7 miesi\u0105ce') \nAND created_dt  '{\"source\":\"test\"}'::jsonb;\n\nAgregat  (koszt=322041.51..322041.52 wierszy=1 szeroko\u015b\u0107=8) (czas rzeczywisty=2278.867..2278.867 wierszy=1 p\u0119tli=1)\n  Wynik: count(1)\n  InitPlan 1 (zwraca $0)\n    -&gt;  Wynik  (koszt=0.00..0.02 wierszy=1 szeroko\u015b\u0107=8) (czas rzeczywisty=0.010..0.010 wierszy=1 p\u0119tli=1)\n          Wynik: ((('teraz'::cstring)::date)::timestamp with time zone - '7 miesi\u0119cy'::interval)\n  InitPlan 2 (zwraca $1)\n    -&gt;  Wynik  (koszt=0.00..0.02 wierszy=1 szeroko\u015b\u0107=8) (czas rzeczywisty=0.003..0.003 wierszy=1 p\u0119tli=1)\n          Wynik: ((('teraz'::cstring)::date)::timestamp with time zone - '6 miesi\u0119cy'::interval)\n  -&gt;  Zdalne skanowanie w fdw_schema.\"table\"  (koszt=100.02..322041.41 wierszy=25 szeroko\u015b\u0107=0) (czas rzeczywisty=8.597..2153.809 wierszy=1360025 p\u0119tli=1)\n        Wynik: \"table\".id, \"table\".is_active, \"table\".meta, \"table\".created_dt\n        Zdalne SQL: SELECT NULL FROM fdw_schema.table WHERE (is_active) AND ((created_dt &gt;= $1::timestamp with time zone)) AND ((created_dt  '{\"source\": \"test\"}'::jsonb))\nCzas planowania: 0.820 ms\nCzas wykonania: 2279.087 ms<\/code><\/pre>\n<p>\nTak, zapytanie wygl\u0105da na bardziej skomplikowane, to wymuszona cena, ale czas wykonania wynosi 2 sekundy, co jest ponad 10 razy szybciej! A m\u00f3wimy o prostym zapytaniu do relatywnie niewielkiego zbioru danych. W przypadku rzeczywistych zapyta\u0144 uzyskiwali\u015bmy przyrosty rz\u0119du setek razy.<\/p>\n<p>Podsumowuj\u0105c: je\u015bli u\u017cywasz PostgreSQL z FDW, zawsze sprawdzaj, czy wszystkie filtry s\u0105 przesy\u0142ane na zdalny serwer, a b\u0119dziesz szcz\u0119\u015bliwy... Przynajmniej dop\u00f3ki nie dojdziesz do join\u00f3w mi\u0119dzy tabelami z r\u00f3\u017cnych <a class=\"wpil_keyword_link\" href=\"https:\/\/prohoster.info\/server\/\"   title=\"serwer\u00f3w\" data-wpil-keyword-link=\"linked\"  data-wpil-monitor-id=\"1482\">serwer\u00f3w<\/a>. Ale to ju\u017c historia na kolejny artyku\u0142.<\/p>\n<p>Dzi\u0119kuj\u0119 za uwag\u0119! B\u0119d\u0119 wdzi\u0119czny za pytania, komentarze oraz historie o twoim do\u015bwiadczeniu w komentarzach.<br \/>\n<br \/>\u0179r\u00f3d\u0142o: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/domclick\/blog\/498018\/\">habr.com<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u041c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043d\u0430\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430, \u043a\u0430\u043a \u0438 \u0432\u0441\u0435 \u0432 \u044d\u0442\u043e\u043c \u043c\u0438\u0440\u0435, \u0438\u043c\u0435\u0435\u0442 \u0441\u0432\u043e\u0438 \u043f\u043b\u044e\u0441\u044b \u0438 \u0441\u0432\u043e\u0438 \u043c\u0438\u043d\u0443\u0441\u044b. \u041e\u0434\u043d\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b \u0441 \u043d\u0435\u0439 \u0441\u0442\u0430\u043d\u043e\u0432\u044f\u0442\u0441\u044f \u043f\u0440\u043e\u0449\u0435, \u0434\u0440\u0443\u0433\u0438\u0435 \u2014 \u0441\u043b\u043e\u0436\u043d\u0435\u0435. \u0418 \u0432 \u0443\u0433\u043e\u0434\u0443 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 \u0438 \u043b\u0443\u0447\u0448\u0435\u0439 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u0443\u0435\u043c\u043e\u0441\u0442\u0438 \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u0438\u043d\u043e\u0441\u0438\u0442\u044c \u0441\u0432\u043e\u0438 \u0436\u0435\u0440\u0442\u0432\u044b. \u041e\u0434\u043d\u0430 \u0438\u0437 \u043d\u0438\u0445 \u2014 \u0443\u0441\u043b\u043e\u0436\u043d\u0435\u043d\u0438\u0435 \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0438. \u0415\u0441\u043b\u0438 \u0432 \u043c\u043e\u043d\u043e\u043b\u0438\u0442\u0435 \u0432\u0441\u044e \u043e\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u0443\u044e \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0443 \u043c\u043e\u0436\u043d\u043e \u0441\u0432\u0435\u0441\u0442\u0438 \u043a SQL \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u043c \u043a \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0440\u0435\u043f\u043b\u0438\u043a\u0435, [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":79468,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-79467","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.1.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u041c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043d\u0430\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430, \u043a\u0430\u043a \u0438 \u0432\u0441\u0435 \u0432 \u044d\u0442\u043e\u043c \u043c\u0438\u0440\u0435, \u0438\u043c\u0435\u0435\u0442 \u0441\u0432\u043e\u0438 \u043f\u043b\u044e\u0441\u044b \u0438 \u0441\u0432\u043e\u0438 \u043c\u0438\u043d\u0443\u0441\u044b. \u041e\u0434\u043d\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b \u0441 \u043d\u0435\u0439 \u0441\u0442\u0430\u043d\u043e\u0432\u044f\u0442\u0441\u044f \u043f\u0440\u043e\u0449\u0435, \u0434\u0440\u0443\u0433\u0438\u0435 \u2014 \u0441\u043b\u043e\u0436\u043d\u0435\u0435.\" \/>\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\/pl\/blog\/administrirovanie\/operativnaya-analitika-v-mikroservisnoj-arhitekture-p%cc%b6o%cc%b6n%cc%b6ya%cc%b6t%cc%b6%cc%b6-%cc%b6i%cc%b6-%cc%b6p%cc%b6r%cc%b6o%cc%b6s%cc%b6t%cc%b6i%cc%b6t%cc%b6%cc%b6-pomoch-i-podskazat-postgres\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"pl_PL\" \/>\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\udd47\u041e\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u0430\u044f \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0430 \u0432 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0435: \u043f\u0336\u043e\u0336\u043d\u0336\u044f\u0336\u0442\u0336\u044c\u0336 \u0336\u0438\u0336 \u0336\u043f\u0336\u0440\u0336\u043e\u0336\u0441\u0336\u0442\u0336\u0438\u0336\u0442\u0336\u044c\u0336 \u043f\u043e\u043c\u043e\u0447\u044c \u0438 \u043f\u043e\u0434\u0441\u043a\u0430\u0437\u0430\u0442\u044c Postgres FDW | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u041c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043d\u0430\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430, \u043a\u0430\u043a \u0438 \u0432\u0441\u0435 \u0432 \u044d\u0442\u043e\u043c \u043c\u0438\u0440\u0435, \u0438\u043c\u0435\u0435\u0442 \u0441\u0432\u043e\u0438 \u043f\u043b\u044e\u0441\u044b \u0438 \u0441\u0432\u043e\u0438 \u043c\u0438\u043d\u0443\u0441\u044b. \u041e\u0434\u043d\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b \u0441 \u043d\u0435\u0439 \u0441\u0442\u0430\u043d\u043e\u0432\u044f\u0442\u0441\u044f \u043f\u0440\u043e\u0449\u0435, \u0434\u0440\u0443\u0433\u0438\u0435 \u2014 \u0441\u043b\u043e\u0436\u043d\u0435\u0435.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/operativnaya-analitika-v-mikroservisnoj-arhitekture-p%cc%b6o%cc%b6n%cc%b6ya%cc%b6t%cc%b6%cc%b6-%cc%b6i%cc%b6-%cc%b6p%cc%b6r%cc%b6o%cc%b6s%cc%b6t%cc%b6i%cc%b6t%cc%b6%cc%b6-pomoch-i-podskazat-postgres\" \/>\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-04-27T05:42:23+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-04-27T05:42:23+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\udd47Analiza operacyjna w architekturze mikroserwisowej: zrozumie\u0107 i upro\u015bci\u0107 pomoc oraz wskaz\u00f3wki Postgres FDW | ProHoster","description":"Architektura mikroserwis\u00f3w, podobnie jak wszystko w tym \u015bwiecie, ma swoje plusy i minusy. Niekt\u00f3re procesy staj\u0105 si\u0119 z ni\u0105 prostsze, inne \u2014 bardziej skomplikowane.","canonical_url":"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/operativnaya-analitika-v-mikroservisnoj-arhitekture-p%cc%b6o%cc%b6n%cc%b6ya%cc%b6t%cc%b6%cc%b6-%cc%b6i%cc%b6-%cc%b6p%cc%b6r%cc%b6o%cc%b6s%cc%b6t%cc%b6i%cc%b6t%cc%b6%cc%b6-pomoch-i-podskazat-postgres","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"pl_PL","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\udd47\u041e\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u0430\u044f \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0430 \u0432 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0435: \u043f\u0336\u043e\u0336\u043d\u0336\u044f\u0336\u0442\u0336\u044c\u0336 \u0336\u0438\u0336 \u0336\u043f\u0336\u0440\u0336\u043e\u0336\u0441\u0336\u0442\u0336\u0438\u0336\u0442\u0336\u044c\u0336 \u043f\u043e\u043c\u043e\u0447\u044c \u0438 \u043f\u043e\u0434\u0441\u043a\u0430\u0437\u0430\u0442\u044c Postgres FDW | ProHoster","og:description":"\u041c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043d\u0430\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430, \u043a\u0430\u043a \u0438 \u0432\u0441\u0435 \u0432 \u044d\u0442\u043e\u043c \u043c\u0438\u0440\u0435, \u0438\u043c\u0435\u0435\u0442 \u0441\u0432\u043e\u0438 \u043f\u043b\u044e\u0441\u044b \u0438 \u0441\u0432\u043e\u0438 \u043c\u0438\u043d\u0443\u0441\u044b. \u041e\u0434\u043d\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b \u0441 \u043d\u0435\u0439 \u0441\u0442\u0430\u043d\u043e\u0432\u044f\u0442\u0441\u044f \u043f\u0440\u043e\u0449\u0435, \u0434\u0440\u0443\u0433\u0438\u0435 \u2014 \u0441\u043b\u043e\u0436\u043d\u0435\u0435.","og:url":"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/operativnaya-analitika-v-mikroservisnoj-arhitekture-p%cc%b6o%cc%b6n%cc%b6ya%cc%b6t%cc%b6%cc%b6-%cc%b6i%cc%b6-%cc%b6p%cc%b6r%cc%b6o%cc%b6s%cc%b6t%cc%b6i%cc%b6t%cc%b6%cc%b6-pomoch-i-podskazat-postgres","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-04-27T05:42:23+00:00","article:modified_time":"2020-04-27T05:42:23+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"79467","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:37:24","updated":"2026-02-09 21:38:01","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts\/79467","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/comments?post=79467"}],"version-history":[{"count":2,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts\/79467\/revisions"}],"predecessor-version":[{"id":159871,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts\/79467\/revisions\/159871"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/media\/79468"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/media?parent=79467"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/categories?post=79467"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/tags?post=79467"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}