{"id":30900,"date":"2019-10-31T21:38:02","date_gmt":"2019-10-31T18:38:02","guid":{"rendered":"https:\/\/prohoster.info\/blog\/parallelnye-zaprosy-v-postgresql\/"},"modified":"2019-10-31T21:38:02","modified_gmt":"2019-10-31T18:38:02","slug":"parallelnye-zaprosy-v-postgresql","status":"publish","type":"post","link":"https:\/\/prohoster.info\/sl\/blog\/administrirovanie\/parallelnye-zaprosy-v-postgresql","title":{"rendered":"Paralelni poizvedbe v PostgreSQL","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"Paralelni poizvedbe v PostgreSQL\" src=\"\/wp-content\/uploads\/2019\/04\/bbb4b4d3f8714bbdb9b26ada2f2253b5.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nV sodobnih CPU-ijih je veliko jedr. Leta so aplikacije po\u0161iljale poizvedbe v baze podatkov hkrati. \u010ce je to poro\u010dilo, ki vklju\u010duje veliko \u0161tevilo vrstic v tabeli, se hitreje izvede, ko uporabljate ve\u010d CPU-ijev, kar je v PostgreSQL mogo\u010de od razli\u010dice 9.6 naprej.<\/p>\n<p><\/p>\n<p>Ugodno je bilo potrebno 3 leta, da se je realizirala funkcija paralelnih poizvedb \u2014 kod je bilo potrebno ponovno napisati v razli\u010dnih fazah izvajanja poizvedb. V PostgreSQL 9.6 je bila uvedena infrastruktura za nadaljnje izbolj\u0161anje kode. V kasnej\u0161ih razli\u010dicah so bile paralelno izvedene tudi druge vrste poizvedb.<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h3 id=\"ogranicheniya\">Omejitve<\/h3>\n<p><\/p>\n<ul>\n<li>Ne vklanjajte paralelnega izvajanja, \u010de so vse jedra \u017ee zasedena, sicer bodo druge poizvedbe po\u010dasne.<\/li>\n<li>Najpomembneje je, da paralelno obdelovanje z visokimi vrednostmi WORK_MEM porabi veliko pomnilnika \u2014 vsaka povezava ali sortiranje s funkcijo hash uporablja pomnilnik v obsegu work_mem.<\/li>\n<li>OLTP poizvedbe z nizko zakasnitvijo ni mogo\u010de pospe\u0161iti s paralelnim izvajanjem. \u010ce poizvedba vrne eno vrstico, jo paralelno obdelovanje samo upo\u010dasni.<\/li>\n<li>Razvijalci radi uporabljajo benchmark TPC-H. Morda imate podobne poizvedbe za idealno paralelno izvajanje.<\/li>\n<li>Samo poizvedbe SELECT brez predikatske zaklenitve se izvajajo paralelno.<\/li>\n<li>V\u010dasih je pravilna indeksacija bolj\u0161a od zaporednega skeniranja tabele v paralelnem na\u010dinu.<\/li>\n<li>Zaustavitve poizvedb in kurzorji niso podprti.<\/li>\n<li>Okenske funkcije in agregatne funkcije urejenih nizov niso paralelne.<\/li>\n<li>Ne pridobivate ni\u010desar v delovni obremenitvi vhod-izhod.<\/li>\n<li>Paralelni algoritmi za sortiranje ne obstajajo. Toda poizvedbe s sortiranjem se lahko izvajajo paralelno v nekaterih vidikih.<\/li>\n<li>Zamenjajte CTE (WITH \u2026) z vne\u0161enim SELECT, da omogo\u010dite paralelno obdelovanje.<\/li>\n<li>Ovije podatkov tretjih oseb zaenkrat ne podpirajo paralelne obdelave (kar bi lahko!).<\/li>\n<li>FULL OUTER JOIN ni podprt.<\/li>\n<li>max_rows onemogo\u010da paralelno obdelovanje.<\/li>\n<li>\u010ce je v poizvedbi funkcija, ki ni ozna\u010dena kot PARALLEL SAFE, bo enojna nit.<\/li>\n<li>Nivo izolacije transakcij SERIALIZABLE onemogo\u010da paralelno obdelovanje.<\/li>\n<\/ul>\n<p><\/p>\n<h3 id=\"testovaya-sreda\">Testno okolje<\/h3>\n<p><\/p>\n<p>Razvijalci PostgreSQL so posku\u0161ali zmanj\u0161ati odzivni \u010das poizvedb benchmarka TPC-H. Prenesite benchmark in <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/tvondra\/pg_tpch\">prilagodite ga PostgreSQL.<\/a><\/noindex>. To je neformalna uporaba benchmarka TPC-H \u2014 ni za primerjavo baz podatkov ali opreme.<\/p>\n<p><\/p>\n<ol>\n<li>Prenesite TPC-H_Tools_v2.17.3.zip (ali novej\u0161o razli\u010dico) <noindex><a rel=\"nofollow\" href=\"http:\/\/www.tpc.org\/tpc_documents_current_versions\/current_specifications.asp\">s spletne strani TPC<\/a><\/noindex>.<\/li>\n<li>Preimenujte makefile.suite v Makefile in spremenite, kot je opisano tukaj: <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/tvondra\/pg_tpch\">https:\/\/github.com\/tvondra\/pg_tpch <\/a><\/noindex>. Kodo skompajlirajte z ukazom make.<\/li>\n<li>Generirajte podatke: <code>.\\\/dbgen -s 10<\/code> ustvari bazo podatkov velikosti 23 GB. To je dovolj, da opazite razliko v zmogljivosti med paralelnimi in nepovedenimi poizvedbami.<\/li>\n<li>Pretvorite datoteke <code>tbl<\/code> u <code>csv s for<\/code> in <code>sed<\/code>.<\/li>\n<li>Klonirajte repozitorij <code>pg_tpch<\/code> in kopirajte datoteke <code>csv<\/code> u <code>pg_tpch\\\/dss\\\/data<\/code>.<\/li>\n<li>Ustvarite poizvedbe z ukazom <code>qgen<\/code>.<\/li>\n<li>Nalo\u017eite podatke v bazo z ukazom <code>.\\\/tpch.sh<\/code>.<\/li>\n<\/ol>\n<p><\/p>\n<h3 id=\"parallelnoe-posledovatelnoe-skanirovanie\">Paralelno zaporedno skeniranje<\/h3>\n<p><\/p>\n<p>Morda je hitrej\u0161e ne zaradi paralelnega branja, temve\u010d zato, ker so podatki razpr\u0161eni po ve\u010d jedrih CPU. V sodobnih operacijskih sistemih se datoteke podatkov PostgreSQL dobro predpomnijo. S predhodnim branjem lahko pridobite iz shrambi blok, ki je ve\u010dji, kot ga zahteva PG demon. Torej zmogljivost poizvedbe ni omejena z vhodno-izhodnimi operacijami diska. Porablja cikle CPU za:<\/p>\n<p><\/p>\n<ul>\n<li>branje vrstic eno po eno s strani tabele;<\/li>\n<li>primerjanje vrednosti vrstic in pogojev <code>WHERE<\/code>.<\/li>\n<\/ul>\n<p><\/p>\n<p>Izvedimo preprosto poizvedbo <code>select<\/code>:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">tpch=# explain analyze select l_quantity as sum_qty from lineitem where l_shipdate &lt;= date &#039;1998-12-01&#039; - interval &#039;105&#039; day;\nQUERY PLAN\n--------------------------------------------------------------------------------------------------------------------------\nSeq Scan on lineitem (cost=0.00..1964772.00 rows=58856235 width=5) (actual time=0.014..16951.669 rows=58839715 loops=1)\nFilter: (l_shipdate &lt;= &#039;1998-08-18 00:00:00&#039;::timestamp without time zone)\nRows Removed by Filter: 1146337\nPlanning Time: 0.203 ms\nExecution Time: 19035.100 ms<\/code><\/pre>\n<p><\/p>\n<p>Zaporedno skeniranje daje preve\u010d vrstic brez agregacije, tako da se poizvedba izvede z enim jedrom CPU.<\/p>\n<p><\/p>\n<p>\u010ce dodamo <code>SUM()<\/code>, vidimo, da dva delovna procesa pomagata pospe\u0161iti poizvedbo:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">explain analyze select sum(l_quantity) as sum_qty from lineitem where l_shipdate  Gather (cost=1589701.91..1589702.12 rows=2 width=32) (actual time=8553.241..8555.067 rows=3 loops=1)\nWorkers Planned: 2\nWorkers Launched: 2\n-&gt; Partial Aggregate (cost=1588701.91..1588701.92 rows=1 width=32) (actual time=8547.546..8547.546 rows=1 loops=3)\n-&gt; Paralelno zaporedno skeniranje na lineitem (cost=0.00..1527393.33 rows=24523431 width=5) (actual time=0.038..5998.417 rows=19613238 loops=3)\nFilter: (l_shipdate &lt;= &#039;1998-08-18 00:00:00&#039;::timestamp without time zone)\nRows Removed by Filter: 382112\nPlanning Time: 0.241 ms\nExecution Time: 8555.131 ms<\/code><\/pre>\n<p><\/p>\n<h3 id=\"parallelnaya-agregaciya\">Paralelna agregacija<\/h3>\n<p><\/p>\n<p>Noda 'Parallel Seq Scan' proizvodi retke za delimi\u010dnu agregaciju. Noda 'Partial Aggregate' skra\u0107uje te retke pomo\u0107u <code>SUM()<\/code>. Na kraju, prebrojavanje SUM iz svake radne jedinice okuplja nod 'Gather'.<\/p>\n<p><\/p>\n<p>Kona\u010dni rezultat izra\u010dunava nod 'Finalize Aggregate'. Ako imate svoje funkcije agregacije, ne zaboravite da ih ozna\u010dite kao 'parallel safe'.<\/p>\n<p><\/p>\n<h3 id=\"kolichestvo-rabochih-processov\">\u0160tevilo delovnih procesov<\/h3>\n<p><\/p>\n<p>\u0160tevilo delovnih procesov lahko pove\u010date brez ponovnega zagonu stre\u017enika:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">explain analyze select sum(l_quantity) as sum_qty from lineitem where l_shipdate  Gather (cost=1589701.91..1589702.12 rows=2 width=32) (actual time=8553.241..8555.067 rows=3 loops=1)\nWorkers Planned: 2\nWorkers Launched: 2\n-&gt; Partial Aggregate (cost=1588701.91..1588701.92 rows=1 width=32) (actual time=8547.546..8547.546 rows=1 loops=3)\n-&gt; Paralelno zaporedno skeniranje na lineitem (cost=0.00..1527393.33 rows=24523431 width=5) (actual time=0.038..5998.417 rows=19613238 loops=3)\nFilter: (l_shipdate &lt;= &#039;1998-08-18 00:00:00&#039;::timestamp without time zone)\nRows Removed by Filter: 382112\nPlanning Time: 0.241 ms\nExecution Time: 8555.131 ms<\/code><\/pre>\n<p><\/p>\n<p>Kaj se dogaja tukaj? \u0160tevilo delovnih procesov se je pove\u010dalo na 2-krat ve\u010d, medtem ko je bil zahtevek le 1,6599-krat hitrej\u0161i. Izra\u010duni so zanimivi. Imeli smo 2 delovna procesa in 1 vodilni. Po spremembi pa je bilo 4+1.<\/p>\n<p><\/p>\n<p>Na\u0161a najve\u010dja pospe\u0161itev zaradi paralelnega procesiranja: 5\/3 = 1,66(6)-krat.<\/p>\n<p><\/p>\n<h2 id=\"kak-eto-rabotaet\">Kako deluje?<\/h2>\n<p><\/p>\n<h3 id=\"processy\">Procesi<\/h3>\n<p><\/p>\n<p>Izvajanje zahteve se vedno za\u010dne z vodilnim procesom. Vodja opravi vse nepodvojeno in del dela paralelne obdelave. Ostali procesi, ki izvajajo enake zahteve, se imenujejo delovni procesi. Paralelno procesiranje uporablja infrastrukturo <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/11\/bgworker.html\">dinami\u010dnih ozadnih delovnih procesov<\/a><\/noindex> (od razli\u010dice 9.4). Ker druge komponente PostgreSQL uporabljajo procese in ne niti, lahko je zahteva s 3 delovnimi procesi do 4-krat hitrej\u0161a od tradicionalne obdelave.<\/p>\n<p><\/p>\n<h3 id=\"vzaimodeystvie\">Interakcija<\/h3>\n<p><\/p>\n<p>Delovni procesi komunicirajo z vodjo preko \u010dakalne vrste sporo\u010dil (na osnovi skupne pomnilnika). Vsak proces ima 2 \u010dakalni vrsti: za napake in za tuplje.<\/p>\n<p><\/p>\n<h3 id=\"skolko-nuzhno-rabochih-processov\">Koliko delovnih procesov potrebujemo?<\/h3>\n<p><\/p>\n<p>Minimalno omejitev dolo\u010da parameter <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/11\/runtime-config-resource.html#GUC-MAX-PARALLEL-WORKERS-PER-GATHER\"><code>max_parallel_workers_per_gather<\/code><\/a><\/noindex>. Nato izvr\u0161evalec zahtevkov vzame delovne procese iz bazena, omejenega z \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u043c <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/11\/runtime-config-resource.html#GUC-MAX-WORKER-PROCESSES\"><code>max_parallel_workers size<\/code><\/a><\/noindex>. Zadnja omejitev je <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/11\/runtime-config-resource.html#GUC-MAX-WORKER-PROCESSES\"><code>max_worker_processes<\/code><\/a><\/noindex>, to je skupno \u0161tevilo ozadnih procesov.<\/p>\n<p><\/p>\n<p>\u010ce ne uspe dodeliti delovnega procesa, bo obdelava enoprocesna.<\/p>\n<p><\/p>\n<p>Sistem za na\u010drtovanje zahtevkov lahko zmanj\u0161a \u0161tevilo delovnih procesov glede na velikost tabele ali indeksa. Za to obstajajo parametri <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/current\/runtime-config-query.html#GUC-MIN-PARALLEL-TABLE-SCAN-SIZE\"><code>min_parallel_table_scan_size<\/code><\/a><\/noindex> in <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/current\/runtime-config-query.html#GUC-MIN-PARALLEL-INDEX-SCAN-SIZE\"><code>min_parallel_index_scan_size<\/code><\/a><\/noindex>.<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">set min_parallel_table_scan_size='8MB'\n8MB tabela =&gt; 1 delovni proces\n24MB tabela =&gt; 2 delovna procesa\n72MB tabela =&gt; 3 delovne procese\nx =&gt; log(x \/ min_parallel_table_scan_size) \/ log(3) + 1 delovni proces<\/code><\/pre>\n<p><\/p>\n<p>Vsaki\u010d, ko je tabela 3-krat ve\u010dja od <code>min_parallel_(index|table)_scan_size<\/code>, Postgres doda delovni proces. \u0160tevilo delovnih procesov ni temeljeno na stro\u0161kih. Cikli\u010dna odvisnost ote\u017euje zapletene izvedbe. Namesto tega na\u010drtovalec uporablja preprosta pravila.<\/p>\n<p><\/p>\n<p>V praksi ta pravila niso vedno primerna za proizvodnjo, zato lahko spremenite \u0161tevilo delovnih procesov za dolo\u010deno tabelo: ALTER TABLE \u2026 SET (<code>parallel_workers = N<\/code>).<\/p>\n<p><\/p>\n<h3 id=\"pochemu-parallelnaya-obrabotka-ne-ispolzuetsya\">Zakaj paralelno procesiranje ni uporabljeno?<\/h3>\n<p><\/p>\n<p>Poleg dolgega seznama omejitev so \u0161e preverbe stro\u0161kov:<\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/current\/runtime-config-query.html#GUC-PARALLEL-SETUP-COST\"><code>parallel_setup_cost<\/code><\/a><\/noindex> \u2014 da se izognemo paralelnemu obdelovanju kratkih poizvedb. Ta parameter ocenjuje \u010das, potreben za pripravo pomnilnika, zagon procesa in za\u010detno izmenjavo podatkov.<\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/current\/runtime-config-query.html#GUC-PARALLEL-TUPLE-COST\"><code>parallel_tuple_cost<\/code><\/a><\/noindex>: komunikacija vodje z delavci se lahko podalj\u0161a sorazmerno s \u0161tevilom tupljev delovnih procesov. Ta parameter izra\u010dunava stro\u0161ke izmenjave podatkov.<\/p>\n<p><\/p>\n<h3 id=\"soedineniya-vlozhennyh-ciklov--nested-loop-join\">Povezave vgrajenih krogov \u2014 Nested Loop Join<\/h3>\n<p><\/p>\n<pre><code class=\"plaintext\">PostgreSQL 9.6+ mo\u017ee izvoditi ugnje\u017edene petlje paralelno \u2014 to je jednostavna operacija.\n\nexplain (costs off) select c_custkey, count(o_orderkey)\n from customer left outer join orders on\n c_custkey = o_custkey and o_comment not like '%specialposits%'\n group by c_custkey;\n QUERY PLAN\n--------------------------------------------------------------------------------------\n Finalize GroupAggregate\n Group Key: customer.c_custkey\n -&gt; Gather Merge\n Planned Workers: 4\n -&gt; Partial GroupAggregate\n Group Key: customer.c_custkey\n -&gt; Nested Loop Left Join\n -&gt; Parallel Index Only Scan using customer_pkey on customer\n -&gt; Index Scan using idx_orders_custkey on orders\n Index Cond: (customer.c_custkey = o_custkey)\n Filter: ((o_comment)::text !~~ '%specialposits%'::text)<\/code><\/pre>\n<p><\/p>\n<p>Zbiranje poteka v zadnji fazi, tako da je Nested Loop Left Join \u2014 to je paralelna operacija. Parallel Index Only Scan se je pojavil \u0161ele v razli\u010dici 10. Deluje podobno kot paralelno sequential scanning. Pogoji <code>c_custkey = o_custkey<\/code> prebere en naro\u010dilo za vsako vrstico stranke. Tako da ni paralelno.<\/p>\n<p><\/p>\n<h3 id=\"hesh-soedinenie--hash-join\">Hash Join<\/h3>\n<p><\/p>\n<p>Vsak delovni proces ustvari svojo hash tabelo do PostgreSQL 11. \u010ce pa je teh procesov ve\u010d kot \u0161tiri, zmogljivost ne bo bolj\u0161a. V novi razli\u010dici je hash tabela skupna. Vsak delovni proces lahko uporablja WORK_MEM za ustvarjanje hash tabele.<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">select\n        l_shipmode,\n        sum(case\n                when o_orderpriority = '1-URGENT'\n                        or o_orderpriority = '2-HIGH'\n                        then 1\n                else 0\n        end) as high_line_count,\n        sum(case\n                when o_orderpriority  '1-URGENT'\n                        and o_orderpriority  '2-HIGH'\n                        then 1\n                else 0\n        end) as low_line_count\nfrom\n        orders,\n        lineitem\nwhere\n        o_orderkey = l_orderkey\n        and l_shipmode in ('MAIL', 'AIR')\n        and l_commitdate &lt; l_receiptdate\n        and l_shipdate = date '1996-01-01'\n        and l_receiptdate   Finalize GroupAggregate  (cost=1964755.66..1966196.11 rows=7 width=27) (actual time=7579.590..7579.591 rows=1 loops=1)\n         Group Key: lineitem.l_shipmode\n         -&gt;  Gather Merge  (cost=1964755.66..1966195.83 rows=28 width=27) (actual time=7559.593..7922.319 rows=6 loops=1)\n               Workers Planned: 4\n               Workers Launched: 4\n               -&gt;  Partial GroupAggregate  (cost=1963755.61..1965192.44 rows=7 width=27) (actual time=7548.103..7564.592 rows=2 loops=5)\n                     Group Key: lineitem.l_shipmode\n                     -&gt;  Sort  (cost=1963755.61..1963935.20 rows=71838 width=27) (actual time=7530.280..7539.688 rows=62519 loops=5)\n                           Sort Key: lineitem.l_shipmode\n                           Sort Method: external merge  Disk: 2304kB\n                           Worker 0:  Sort Method: external merge  Disk: 2064kB\n                           Worker 1:  Sort Method: external merge  Disk: 2384kB\n                           Worker 2:  Sort Method: external merge  Disk: 2264kB\n                           Worker 3:  Sort Method: external merge  Disk: 2336kB\n                           -&gt;  Parallel Hash Join  (cost=382571.01..1957960.99 rows=71838 width=27) (actual time=7036.917..7499.692 rows=62519 loops=5)\n                                 Hash Cond: (lineitem.l_orderkey = orders.o_orderkey)\n                                 -&gt;  Parallel Seq Scan on lineitem  (cost=0.00..1552386.40 rows=71838 width=19) (actual time=0.583..4901.063 rows=62519 loops=5)\n                                       Filter: ((l_shipmode = ANY ('{MAIL,AIR}'::bpchar[])) AND (l_commitdate &lt; l_receiptdate) AND (l_shipdate = '1996-01-01'::date) AND (l_receiptdate   Parallel Hash  (cost=313722.45..313722.45 rows=3750045 width=20) (actual time=2011.518..2011.518 rows=3000000 loops=5)\n                                       Buckets: 65536  Batches: 256  Memory Usage: 3840kB\n                                       -&gt;  Parallel Seq Scan on orders  (cost=0.00..313722.45 rows=3750045 width=20) (actual time=0.029..995.948 rows=3000000 loops=5)\n Planning Time: 0.977 ms\n Execution Time: 7923.770 ms<\/code><\/pre>\n<p><\/p>\n<p>Poja\u010danje 12 iz TPC-H jasno prikazuje paralelno ha\u0161 spajanje. Svaki radni proces u\u010destvuje u kreiranju zajedni\u010dke ha\u0161 tabele.<\/p>\n<p><\/p>\n<h3 id=\"soedinenie-sliyaniem--merge-join\">Spojitev s fuzijo \u2014 Merge Join<\/h3>\n<p><\/p>\n<p>Spojitev s fuzijo je po svoji naravi nepovsem povezana. Ne skrbite, \u010de je to zadnja faza poizvedbe, \u0161e vedno se lahko izvaja vzporedno.<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">-- Poizvedba 2 iz TPC-H\nrazlo\u017ei (stro&scaron;ke izklopi) izbira s_acctbal, s_name, n_name, p_partkey, p_mfgr, s_address, s_phone, s_comment\niz    del, dobavitelj, partsupp, narod, regija\nkjer\n        p_partkey = ps_partkey\n        in s_suppkey = ps_suppkey\n        in p_size = 36\n        in p_type kot &#039;%BRASS&#039;\n        in s_nationkey = n_nationkey\n        in n_regionkey = r_regionkey\n        in r_name = &#039;AMERIKA&#039;\n        in ps_supplycost = (\n                izbira\n                        min(ps_supplycost)\n                iz    partsupp, dobavitelj, narod, regija\n                kjer\n                        p_partkey = ps_partkey\n                        in s_suppkey = ps_suppkey\n                        in s_nationkey = n_nationkey\n                        in n_regionkey = r_regionkey\n                        in r_name = &#039;AMERIKA&#039;\n        )\nuredi po s_acctbal desc, n_name, s_name, p_partkey\nLIMIT 100;\n                                                NA\u010cRT POIZVEDBE\n----------------------------------------------------------------------------------------------------------\n Limit\n   -&amp;gt;  Razvrsti\n         Klju\u010d za razvr&scaron;\u010danje: dobavitelj.s_acctbal DESC, narod.n_name, dobavitelj.s_name, del.p_partkey\n         -&amp;gt;  Zdru\u017ei Merge\n               Pogoj zdru\u017eitve: (del.p_partkey = partsupp.ps_partkey)\n               Filtriranje pri zdru\u017eevanju: (partsupp.ps_supplycost = (Podna\u010drt 1))\n               -&amp;gt;  Zbiranje za merge\n                     Na\u010drtovani delavci: 4\n                     -&amp;gt;  Paralelno iskanje po indeksu z uporabo &lt;strong&gt;del_pkey&lt;\/strong&gt; na delu\n                           Filtriranje: (((p_type)::text ~~ &#039;%BRASS&#039;::text) IN (p_size = 36))\n               -&amp;gt;  Materializiraj\n                     -&amp;gt;  Razvrsti\n                           Klju\u010d za razvr&scaron;\u010danje: partsupp.ps_partkey\n                           -&amp;gt;  Gnezdena zanka\n                                 -&amp;gt;  Gnezdena zanka\n                                       Filtriranje pri zdru\u017eevanju: (narod.n_regionkey = regija.r_regionkey)\n                                       -&amp;gt;  Sekvencijsko iskanje po regiji\n                                             Filtriranje: (r_name = &#039;AMERIKA&#039;::bpchar)\n                                       -&amp;gt;  Hash Join\n                                             Hash Pogoj: (dobavitelj.s_nationkey = narod.n_nationkey)\n                                             -&amp;gt;  Sekvencijsko iskanje po dobavitelju\n                                             -&amp;gt;  Hash\n                                                   -&amp;gt;  Sekvencijsko iskanje po narodu\n                                 -&amp;gt;  Iskanje po indeksu z uporabo idx_partsupp_suppkey na partsupp\n                                       Indeksni pogoj: (ps_suppkey = dobavitelj.s_suppkey)\n               Podna\u010drt 1\n                 -&amp;gt;  Agregat\n                       -&amp;gt;  Gnezdena zanka\n                             Filtriranje pri zdru\u017eevanju: (narod_1.n_regionkey = regija_1.r_regionkey)\n                             -&amp;gt;  Sekvencijsko iskanje po regiji regija_1\n                                   Filtriranje: (r_name = &#039;AMERIKA&#039;::bpchar)\n                             -&amp;gt;  Gnezdena zanka\n                                   -&amp;gt;  Gnezdena zanka\n                                         -&amp;gt;  Iskanje po indeksu z uporabo idx_partsupp_partkey na partsupp partsupp_1\n                                               Indeksni pogoj: (del.p_partkey = ps_partkey)\n                                         -&amp;gt;  Iskanje po indeksu z uporabo supplier_pkey na dobavitelju dobavitelj_1\n                                               Indeksni pogoj: (s_suppkey = partsupp_1.ps_suppkey)\n                                   -&amp;gt;  Iskanje po indeksu z uporabo nation_pkey na narodu narod_1\n                                         Indeksni pogoj: (n_nationkey = dobavitelj_1.s_nationkey)<\/code><\/pre>\n<p><\/p>\n<p>Noda 'Merge Join' se nalazi iznad 'Gather Merge'. Tako da spajanje ne koristi paralelnu obradu. Ali nod 'Parallel Index Scan' i dalje poma\u017ee sa segmentom. <code>del_pkey<\/code>.<\/p>\n<p><\/p>\n<h3 id=\"soedinenie-po-sekciyam\">Spojitev po sekcijah<\/h3>\n<p><\/p>\n<p>V PostgreSQL 11 <noindex><a rel=\"nofollow\" href=\"http:\/\/ashutoshpg.blogspot.com\/2017\/12\/partition-wise-joins-divide-and-conquer.html\">spojitev po sekcijah<\/a><\/noindex> privzeto je izklopljeno: ima zelo drago na\u010drtovanje. Tabele s podobnim sekcioniranjem je mogo\u010de povezati sekcija za sekcijo. Tako bo Postgres uporabil manj\u0161e hashe. Vsaka spojitev sekcij je lahko vzporedna.<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">tpch=# set enable_partitionwise_join=t;\ntpch=# explain (costs off) select * from prt1 t1, prt2 t2\nwhere t1.a = t2.b and t1.b = 0 and t2.b between 0 and 10000;\n                    QUERY PLAN\n---------------------------------------------------\n Append\n   -&gt;  Hash Join\n         Hash Cond: (t2.b = t1.a)\n         -&gt;  Seq Scan on prt2_p1 t2\n               Filter: ((b &gt;= 0) AND (b &lt;= 10000))\n         -&gt;  Hash\n               -&gt;  Seq Scan on prt1_p1 t1\n                     Filter: (b = 0)\n   -&gt;  Hash Join\n         Hash Cond: (t2_1.b = t1_1.a)\n         -&gt;  Seq Scan on prt2_p2 t2_1\n               Filter: ((b &gt;= 0) AND (b &lt;= 10000))\n         -&gt;  Hash\n               -&gt;  Seq Scan on prt1_p2 t1_1\n                     Filter: (b = 0)\ntpch=# set parallel_setup_cost = 1;\ntpch=# set parallel_tuple_cost = 0.01;\ntpch=# explain (costs off) select * from prt1 t1, prt2 t2\nwhere t1.a = t2.b and t1.b = 0 and t2.b between 0 and 10000;\n                        QUERY PLAN\n-----------------------------------------------------------\n Gather\n   Workers Planned: 4\n   -&gt;  Parallel Append\n         -&gt;  Parallel Hash Join\n               Hash Cond: (t2_1.b = t1_1.a)\n               -&gt;  Parallel Seq Scan on prt2_p2 t2_1\n                     Filter: ((b &gt;= 0) AND (b &lt;= 10000))\n               -&gt;  Parallel Hash\n                     -&gt;  Parallel Seq Scan on prt1_p2 t1_1\n                           Filter: (b = 0)\n         -&gt;  Parallel Hash Join\n               Hash Cond: (t2.b = t1.a)\n               -&gt;  Parallel Seq Scan on prt2_p1 t2\n                     Filter: ((b &gt;= 0) AND (b &lt;= 10000))\n               -&gt;  Parallel Hash\n                     -&gt;  Parallel Seq Scan on prt1_p1 t1\n                           Filter: (b = 0)<\/code><\/pre>\n<p><\/p>\n<p>Najpomembneje je, da je spojitev po sekcijah lahko vzporedna, le \u010de so te sekcije dovolj velike.<\/p>\n<p><\/p>\n<h3 id=\"parallelnoe-dopolnenie--parallel-append\">Vzporedno dopolnilo \u2014 Parallel Append<\/h3>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/11\/parallel-plans.html#PARALLEL-APPEND\">Parallel Append<\/a><\/noindex> se lahko uporablja namesto razli\u010dnih blokov v razli\u010dnih delovnih procesih. Obi\u010dajno se to zgodi pri poizvedbah UNION ALL. Pomanjkljivost je manj\u0161a vzporednost, saj vsak delovni proces obravnava le 1 poizvedbo.<\/p>\n<p><\/p>\n<p>Tukaj sta zagnana 2 delovna procesa, \u010deprav je vklopljenih 4.<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">tpch=# explain (costs off) select sum(l_quantity) as sum_qty from lineitem where l_shipdate &lt;= date &#039;1998-12-01&#039; - interval &#039;105&#039; day union all select sum(l_quantity) as sum_qty from lineitem where l_shipdate   Parallel Append\n         -&gt;  Aggregate\n               -&gt;  Seq Scan on lineitem\n                     Filter: (l_shipdate   Aggregate\n               -&gt;  Seq Scan on lineitem lineitem_1\n                     Filter: (l_shipdate &lt;= &#039;1998-08-18 00:00:00&#039;::timestamp without time zone)<\/code><\/pre>\n<p><\/p>\n<h3 id=\"samye-vazhnye-peremennye\">Najpomembnej\u0161e spremenljivke<\/h3>\n<p><\/p>\n<ul>\n<li>WORK_MEM omejuje koli\u010dino pomnilnika za vsak proces, ne le za poizvedbe: work_mem <em> procesi <\/em> povezave = zelo veliko pomnilnika.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/11\/runtime-config-resource.html#GUC-MAX-PARALLEL-WORKERS-PER-GATHER\"><code>max_parallel_workers_per_gather<\/code><\/a><\/noindex> \u2014 koliko delovnih procesov bo izvajajo\u010di program uporabil za paralelno obdelavo po na\u010drtu.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/11\/runtime-config-resource.html#GUC-MAX-WORKER-PROCESSES\"><code>max_worker_processes<\/code><\/a><\/noindex> \u2014 prilagaja skupno \u0161tevilo delovnih procesov \u0161tevilu jedrskih CPU na stre\u017eniku.<\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/11\/runtime-config-resource.html#GUC-MAX-WORKER-PROCESSES\"><code>max_parallel_workers<\/code><\/a><\/noindex> \u2014 enakega, vendar za paralelne delovne procese.<\/li>\n<\/ul>\n<p><\/p>\n<h3 id=\"itogi\">Zaklju\u010dki<\/h3>\n<p><\/p>\n<p>Za\u010dnite z razli\u010dico 9.6, paralelno obdelavo lahko znatno izbolj\u0161a uspe\u0161nost zapletenih poizvedb, ki skenirajo veliko vrstic ali indeksov. V PostgreSQL 10 je paralelna obdelava privzeto omogo\u010dena. Ne pozabite jo onemogo\u010diti na stre\u017enikih z velikim OLTP delovnim obremenitvami. Zaporedni skeni ali skeni indeksov porabijo zelo veliko virov. \u010ce ne izvajate poro\u010dila za celoten niz podatkov, lahko poizvedbe postanejo bolj u\u010dinkovite, tako da dodate manjkajo\u010de indekse ali uporabite pravilno delitev.<\/p>\n<p><\/p>\n<h3 id=\"ssylki\">Povezave<\/h3>\n<p><\/p>\n<ul>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/11\/how-parallel-query-works.html\">https:\/\/www.postgresql.org\/docs\/11\/how-parallel-query-works.html<\/a><\/noindex><\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/11\/parallel-plans.html\">https:\/\/www.postgresql.org\/docs\/11\/parallel-plans.html<\/a><\/noindex><\/li>\n<li><noindex><a rel=\"nofollow\" href=\"http:\/\/ashutoshpg.blogspot.com\/2017\/12\/partition-wise-joins-divide-and-conquer.html\">http:\/\/ashutoshpg.blogspot.com\/2017\/12\/partition-wise-joins-divide-and-conquer.html<\/a><\/noindex><\/li>\n<li><noindex><a rel=\"nofollow\" href=\"http:\/\/rhaas.blogspot.com\/2016\/04\/postgresql-96-with-parallel-query-vs.html\">http:\/\/rhaas.blogspot.com\/2016\/04\/postgresql-96-with-parallel-query-vs.html<\/a><\/noindex><\/li>\n<li><noindex><a rel=\"nofollow\" href=\"http:\/\/amitkapila16.blogspot.com\/2015\/11\/parallel-sequential-scans-in-play.html\">http:\/\/amitkapila16.blogspot.com\/2015\/11\/parallel-sequential-scans-in-play.html<\/a><\/noindex><\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/write-skew.blogspot.com\/2018\/01\/parallel-hash-for-postgresql.html\">https:\/\/write-skew.blogspot.com\/2018\/01\/parallel-hash-for-postgresql.html<\/a><\/noindex><\/li>\n<li><noindex><a rel=\"nofollow\" href=\"http:\/\/rhaas.blogspot.com\/2017\/03\/parallel-query-v2.html\">http:\/\/rhaas.blogspot.com\/2017\/03\/parallel-query-v2.html<\/a><\/noindex><\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/blog.2ndquadrant.com\/parallel-monster-benchmark\/\">https:\/\/blog.2ndquadrant.com\/parallel-monster-benchmark\/<\/a><\/noindex><\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/blog.2ndquadrant.com\/parallel-aggregate\/\">https:\/\/blog.2ndquadrant.com\/parallel-aggregate\/<\/a><\/noindex><\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/www.depesz.com\/2018\/02\/12\/waiting-for-postgresql-11-support-parallel-btree-index-builds\/\">https:\/\/www.depesz.com\/2018\/02\/12\/waiting-for-postgresql-11-support-parallel-btree-index-builds\/<\/a><\/noindex><\/li>\n<li><noindex><a rel=\"nofollow\" href=\"https:\/\/youtu.be\/jWIOZzezbb8\">Paralelizem v PostgreSQL 11<\/a><\/noindex><\/li>\n<\/ul>\n<p>Izvor: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/southbridge\/blog\/446706\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0412 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0426\u041f \u043e\u0447\u0435\u043d\u044c \u043c\u043d\u043e\u0433\u043e \u044f\u0434\u0435\u0440. \u0413\u043e\u0434\u0430\u043c\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043f\u043e\u0441\u044b\u043b\u0430\u043b\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0432 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e. \u0415\u0441\u043b\u0438 \u044d\u0442\u043e \u043e\u0442\u0447\u0435\u0442\u043d\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u043a\u043e \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u0443 \u0441\u0442\u0440\u043e\u043a \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435, \u043e\u043d \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0431\u044b\u0441\u0442\u0440\u0435\u0435, \u043a\u043e\u0433\u0434\u0430 \u0437\u0430\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0426\u041f, \u0438 \u0432 PostgreSQL \u044d\u0442\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u043d\u0430\u0447\u0438\u043d\u0430\u044f \u0441 \u0432\u0435\u0440\u0441\u0438\u0438 9.6. \u041f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u043b\u043e\u0441\u044c 3 \u0433\u043e\u0434\u0430, \u0447\u0442\u043e\u0431\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u2014 \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u0430\u0442\u044c \u043a\u043e\u0434 \u043d\u0430 \u0440\u0430\u0437\u043d\u044b\u0445 \u044d\u0442\u0430\u043f\u0430\u0445 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":22879,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-30900","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=\"\u0412 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0426\u041f \u043e\u0447\u0435\u043d\u044c \u043c\u043d\u043e\u0433\u043e \u044f\u0434\u0435\u0440.\" \/>\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\/sl\/blog\/administrirovanie\/parallelnye-zaprosy-v-postgresql\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.4\" \/>\n\t\t<meta property=\"og:locale\" content=\"sl_SI\" \/>\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\u041f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0432 PostgreSQL | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0412 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0426\u041f \u043e\u0447\u0435\u043d\u044c \u043c\u043d\u043e\u0433\u043e \u044f\u0434\u0435\u0440.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/sl\/blog\/administrirovanie\/parallelnye-zaprosy-v-postgresql\" \/>\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=\"2019-10-31T18:38:02+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2019-10-31T18:38:02+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\udd47Paralelne poizvedbe v PostgreSQL | ProHoster","description":"V sodobnih CPU-eh je zelo veliko jeder.","canonical_url":"https:\/\/prohoster.info\/sl\/blog\/administrirovanie\/parallelnye-zaprosy-v-postgresql","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"sl_SI","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\u041f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0432 PostgreSQL | ProHoster","og:description":"\u0412 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0426\u041f \u043e\u0447\u0435\u043d\u044c \u043c\u043d\u043e\u0433\u043e \u044f\u0434\u0435\u0440.","og:url":"https:\/\/prohoster.info\/sl\/blog\/administrirovanie\/parallelnye-zaprosy-v-postgresql","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":"2019-10-31T18:38:02+00:00","article:modified_time":"2019-10-31T18:38:02+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"30900","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":"2026-01-21 03:34:04","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-03-01 03:27:06","updated":"2026-01-21 03:34:04","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/posts\/30900","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/comments?post=30900"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/posts\/30900\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/media\/22879"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/media?parent=30900"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/categories?post=30900"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/tags?post=30900"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}