{"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\/hr\/blog\/administrirovanie\/parallelnye-zaprosy-v-postgresql","title":{"rendered":"Paralelni upiti u PostgreSQL-u","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"Paralelni upiti u PostgreSQL-u\" src=\"\/wp-content\/uploads\/2019\/04\/bbb4b4d3f8714bbdb9b26ada2f2253b5.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nU moderne procesore se ugra\u0111uje mnogo jezgri. Godinama su aplikacije slale upite u baze podataka paralelno. Ako je to upit za izvje\u0161tavanje o velikom broju redaka u tablici, izvodi se br\u017ee kada se koristi vi\u0161e procesora, a u PostgreSQL-u je to mogu\u0107e od verzije 9.6.<\/p>\n<p><\/p>\n<p>Bilo je potrebno 3 godine da se implementira funkcija paralelnih upita \u2014 kod je morao biti prepravljen u raznim fazama izvr\u0161avanja upita. U PostgreSQL 9.6 uvedena je infrastruktura za daljnje pobolj\u0161anje koda. U kasnijim verzijama, i drugi tipovi upita se izvode paralelno.<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h3 id=\"ogranicheniya\">Ograni\u010denja<\/h3>\n<p><\/p>\n<ul>\n<li>Ne uklju\u010dujte paralelno izvr\u0161avanje ako su sve jezgre ve\u0107 zauzete, ina\u010de \u0107e drugi upiti biti usporeni.<\/li>\n<li>Najva\u017enije, paralelna obrada s visokim vrijednostima WORK_MEM koristi puno memorije \u2014 svaka hash veza ili sortiranje zauzimaju memoriju u obujmu work_mem.<\/li>\n<li>OLTP upiti s niskim ka\u0161njenjem ne mogu se ubrzati paralelnim izvr\u0161avanjem. Ako upit vra\u0107a jedan redak, paralelna obrada \u0107e ga samo usporiti.<\/li>\n<li>Programeri vole koristiti TPC-H benchmark. Mo\u017eda imate sli\u010dne upite za idealno paralelno izvr\u0161avanje.<\/li>\n<li>Samo SELECT upiti bez predikativne blokade se izvode paralelno.<\/li>\n<li>Ponekad je pravilna indeksacija bolja od sekvencijalnog skeniranja tablice u paralelnom na\u010dinu.<\/li>\n<li>Suspended queries and cursors are not supported.<\/li>\n<li>Window functions and aggregate functions of ordered sets are not parallel.<\/li>\n<li>You gain nothing in I\/O workload.<\/li>\n<li>There are no parallel sorting algorithms. However, queries with sorting can be executed in parallel in some aspects.<\/li>\n<li>Replace CTE (WITH ...) with a nested SELECT to enable parallel processing.<\/li>\n<li>Third-party data wrappers do not currently support parallel processing (though they could!)<\/li>\n<li>FULL OUTER JOIN is not supported.<\/li>\n<li>max_rows disables parallel processing.<\/li>\n<li>If there is a function in the query that is not marked as PARALLEL SAFE, it will be single-threaded.<\/li>\n<li>The SERIALIZABLE transaction isolation level disables parallel processing.<\/li>\n<\/ul>\n<p><\/p>\n<h3 id=\"testovaya-sreda\">Test environment<\/h3>\n<p><\/p>\n<p>PostgreSQL developers aimed to cut the response times of TPC-H benchmark queries. Download the benchmark and <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/tvondra\/pg_tpch\">adapt it to PostgreSQL<\/a><\/noindex>. Ovo je neformalna upotreba TPC-H benchmarka - ne za usporedbu baza podataka ili hardvera.<\/p>\n<p><\/p>\n<ol>\n<li>Preuzmite TPC-H_Tools_v2.17.3.zip (ili noviju verziju) <noindex><a rel=\"nofollow\" href=\"http:\/\/www.tpc.org\/tpc_documents_current_versions\/current_specifications.asp\">s web stranice TPC<\/a><\/noindex>.<\/li>\n<li>Preimenujte makefile.suite u Makefile i izmijenite ga kao \u0161to je opisano ovdje: <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/tvondra\/pg_tpch\">https:\/\/github.com\/tvondra\/pg_tpch <\/a><\/noindex>. Kompajlirajte kod naredbom make.<\/li>\n<li>Generirajte podatke: <code>.\\\/dbgen -s 10<\/code> stvara bazu podataka od 23 GB. To je dovoljno da se vidi razlika u performansama paralelnih i neparalelnih upita.<\/li>\n<li>Konvertirajte datoteke <code>tbl<\/code> u <code>csv s for<\/code> i <code>sed<\/code>.<\/li>\n<li>Kloni repozitorij <code>pg_tpch<\/code> i kopirajte datoteke <code>csv<\/code> u <code>pg_tpch\\\/dss\\\/data<\/code>.<\/li>\n<li>Kreirajte upite naredbom <code>qgen<\/code>.<\/li>\n<li>U\u010ditajte podatke u bazu naredbom <code>.\\\/tpch.sh<\/code>.<\/li>\n<\/ol>\n<p><\/p>\n<h3 id=\"parallelnoe-posledovatelnoe-skanirovanie\">Paralelno sekvencijsko skeniranje<\/h3>\n<p><\/p>\n<p>Ono mo\u017ee biti br\u017ee ne zbog paralelnog \u010ditanja, ve\u0107 zato \u0161to su podaci raspr\u0161eni po mnogim CPU jezgrama. U modernim OS, datoteke podataka PostgreSQL se dobro ke\u0161iraju. Sa pred-\u010ditanjem mo\u017eete dobiti iz spremi\u0161ta blok ve\u0107i od onoga \u0161to tra\u017ei PG demon. Stoga performanse upita nisu ograni\u010dene na ulazno-izlazne operacije diska. Tro\u0161i CPU cikluse za:<\/p>\n<p><\/p>\n<ul>\n<li>\u010ditati redove jedan po jedan sa stranica tablice;<\/li>\n<li>uspore\u0111ivati vrijednosti redova i uvjete <code>GDJE<\/code>.<\/li>\n<\/ul>\n<p><\/p>\n<p>Izvr\u0161imo jednostavan upit <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>Sekvencijsko skeniranje daje previ\u0161e redova bez agregacije, tako da se upit izvr\u0161ava jednim CPU jezgrom.<\/p>\n<p><\/p>\n<p>Ako dodate <code>SUM()<\/code>, vidljivo je da \u0107e dva radna procesa pomo\u0107i ubrzati upit:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">explain analyze select sum(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----------------------------------------------------------------------------------------------------------------------------------------------------\nFinalize Aggregate (cost=1589702.14..1589702.15 rows=1 width=32) (actual time=8553.365..8553.365 rows=1 loops=1)\n-&gt; 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 Seq Scan on 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>\u010cvor &#171;Parallel Seq Scan&#187; generira retke za djelomi\u010dnu agregaciju. \u010cvor &#171;Partial Aggregate&#187; skra\u0107uje te retke pomo\u0107u <code>SUM()<\/code>. Na kraju, broja\u010d SUM iz svakog radnog procesa sakuplja \u010dvor &#171;Gather&#187;.<\/p>\n<p><\/p>\n<p>Zavr\u0161ni rezultat izra\u010dunava \u010dvor &#171;Finalize Aggregate&#187;. Ako imate svoje funkcije agregacije, ne zaboravite ih ozna\u010diti kao &#171;parallel safe&#187;.<\/p>\n<p><\/p>\n<h3 id=\"kolichestvo-rabochih-processov\">Broj radnih procesa<\/h3>\n<p><\/p>\n<p>Broj radnih procesa mo\u017ee se pove\u0107ati bez ponovnog pokretanja servera:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">explain analyze select sum(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----------------------------------------------------------------------------------------------------------------------------------------------------\nFinalize Aggregate (cost=1589702.14..1589702.15 rows=1 width=32) (actual time=8553.365..8553.365 rows=1 loops=1)\n-&gt; 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 Seq Scan on 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>\u0160to se ovdje doga\u0111a? Radnih procesa je dvostruko vi\u0161e, a upit je postao samo 1,6599 puta br\u017ei. Izra\u010duni su zanimljivi. Imali smo 2 radna procesa i 1 vo\u0111u. Nakon promjene sad imamo 4+1.<\/p>\n<p><\/p>\n<p>Na\u0161e maksimalno ubrzanje od paralelnog obrade: 5\/3 = 1,66(6) puta.<\/p>\n<p><\/p>\n<h2 id=\"kak-eto-rabotaet\">Kako to radi?<\/h2>\n<p><\/p>\n<h3 id=\"processy\">Procesi<\/h3>\n<p><\/p>\n<p>Izvr\u0161enje upita uvijek po\u010dinje s vode\u0107im procesom. Vo\u0111a radi sve neparalelno i dio paralelnog obrade. Ostali procesi koji izvr\u0161avaju iste upite nazivaju se radnim procesima. Paralelna obrada koristi infrastrukturu <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/11\/bgworker.html\">dinami\u010dkih pozadinskih radnih procesa<\/a><\/noindex> (od verzije 9.4). Kako druge komponente PostgreSQL koriste procese, a ne niti, upit s 3 radna procesa mogao bi biti 4 puta br\u017ei od tradicionalne obrade.<\/p>\n<p><\/p>\n<h3 id=\"vzaimodeystvie\">Interakcija<\/h3>\n<p><\/p>\n<p>Radni procesi komuniciraju s vo\u0111om putem reda poruka (na bazi zajedni\u010dke memorije). Svaki proces ima 2 reda: za gre\u0161ke i za tuple.<\/p>\n<p><\/p>\n<h3 id=\"skolko-nuzhno-rabochih-processov\">Koliko radnih procesa je potrebno?<\/h3>\n<p><\/p>\n<p>Minimalno ograni\u010denje postavlja parametar <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>. Nakon toga izvr\u0161itelj upita uzima radne procese iz bazena, ograni\u010denog parametrom <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>. Posljednje ograni\u010denje 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>, tj. ukupni broj pozadinskih procesa.<\/p>\n<p><\/p>\n<p>Ako nije mogu\u0107e dodijeliti radni proces, obrada \u0107e biti jednoprosjedna.<\/p>\n<p><\/p>\n<p>Planer upita mo\u017ee smanjiti radne procese ovisno o veli\u010dini tablice ili indeksa. Za to postoje 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> i <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 tablica =&gt; 1 radnik\n24MB tablica =&gt; 2 radnika\n72MB tablica =&gt; 3 radnika\nx =&gt; log(x \/ min_parallel_table_scan_size) \/ log(3) + 1 radnik<\/code><\/pre>\n<p><\/p>\n<p>Svaki put kada je tablica 3 puta ve\u0107a od <code>min_parallel_(index|table)_scan_size<\/code>, Postgres dodaje radni proces. Broj radnih procesa nije temeljen na tro\u0161kovima. Cikli\u010dna ovisnost ote\u017eava slo\u017eene implementacije. Umjesto toga, planer koristi jednostavna pravila.<\/p>\n<p><\/p>\n<p>U praksi ova pravila nisu uvijek primjenjiva za produkciju, pa je mogu\u0107e promijeniti broj radnih procesa za odre\u0111enu tablicu: ALTER TABLE \u2026 SET (<code>parallel_workers = N<\/code>).<\/p>\n<p><\/p>\n<h3 id=\"pochemu-parallelnaya-obrabotka-ne-ispolzuetsya\">Za\u0161to se ne koristi paralelna obrada?<\/h3>\n<p><\/p>\n<p>Osim dugog popisa ograni\u010denja, postoje i provjere tro\u0161kova:<\/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 kako bi se izbjegla paralelna obrada kratkih upita. Ovaj parametar procjenjuje vrijeme potrebno za pripremu memorije, pokretanje procesa i inicijalnu razmjenu podataka.<\/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 vo\u0111e s radnicima mo\u017ee se produ\u017eiti proporcionalno broju tuplova od radnih procesa. Ovaj parametar izra\u010dunava tro\u0161kove razmjene podataka.<\/p>\n<p><\/p>\n<h3 id=\"soedineniya-vlozhennyh-ciklov--nested-loop-join\">Spajanje ugnije\u017edenih petlji \u2014 Nested Loop Join<\/h3>\n<p><\/p>\n<pre><code class=\"plaintext\">PostgreSQL 9.6+ mo\u017ee izvr\u0161avati ugne\u017edene petlje paralelno \u2014 \u0161to 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 Workers Planned: 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>Sakupljanje se odvija u posljednjoj fazi, tako da je Nested Loop Left Join \u2014 paralelna operacija. Parallel Index Only Scan pojavio se tek u verziji 10. Radi analogno paralelnom sekvencijalnom skeniranju. Uvjet <code>c_custkey = o_custkey<\/code> \u010ditati jedan nalog za svaki klijentski redak. Dakle, nije paralelan.<\/p>\n<p><\/p>\n<h3 id=\"hesh-soedinenie--hash-join\">Hash join \u2014 Hash Join<\/h3>\n<p><\/p>\n<p>Svaki radni proces stvara svoju hash-tablicu do PostgreSQL 11. A ako je tih procesa vi\u0161e od \u010detiri, performanse se ne\u0107e pobolj\u0161ati. U novoj verziji hash-tablica je zajedni\u010dka. Svaki radni proces mo\u017ee koristiti WORK_MEM za stvaranje hash-tablice.<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">odabir\n        l_shipmode,\n        zbroj(case\n                kada o_orderpriority = '1-URGENT'\n                        ili o_orderpriority = '2-HIGH'\n                        onda 1\n                ina\u010de 0\n        kraj) kao high_line_count,\n        zbroj(case\n                kada o_orderpriority  '1-URGENT'\n                        i o_orderpriority  '2-HIGH'\n                        onda 1\n                ina\u010de 0\n        kraj) kao low_line_count\niz\n        narud\u017ebe,\n        stavka\ngdje\n        o_orderkey = l_orderkey\n        i l_shipmode u ('MAIL', 'AIR')\n        i l_commitdate &lt; l_receiptdate\n        i l_shipdate = datum '1996-01-01'\n        i l_receiptdate &lt; datum &#039;1996-01-01&#039; + interval &#039;1&#039; godina\ngrupa po\n        l_shipmode\nnaru\u010dite po\n        l_shipmode\nLIMIT 1;\n                                                                                                                                    PLAN UPITA\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n Ograni\u010denje  (tro\u0161ak=1964755.66..1964961.44 redaka=1 \u0161irina=27) (stvarno vrijeme=7579.592..7922.997 redaka=1 petlje=1)\n   -&gt;  Finalizacija GrupaAgregacija  (tro\u0161ak=1964755.66..1966196.11 redaka=7 \u0161irina=27) (stvarno vrijeme=7579.590..7579.591 redaka=1 petlje=1)\n         Klju\u010d grupe: lineitem.l_shipmode\n         -&gt;  Zbirka Merge  (tro\u0161ak=1964755.66..1966195.83 redaka=28 \u0161irina=27) (stvarno vrijeme=7559.593..7922.319 redaka=6 petlje=1)\n               Planned Workers: 4\n               Pokrenuti radnici: 4\n               -&gt;  Djelomi\u010dna GrupaAgregacija  (tro\u0161ak=1963755.61..1965192.44 redaka=7 \u0161irina=27) (stvarno vrijeme=7548.103..7564.592 redaka=2 petlje=5)\n                     Klju\u010d grupe: lineitem.l_shipmode\n                     -&gt;  Sort  (tro\u0161ak=1963755.61..1963935.20 redaka=71838 \u0161irina=27) (stvarno vrijeme=7530.280..7539.688 redaka=62519 petlje=5)\n                           Klju\u010d sortiranja: lineitem.l_shipmode\n                           Metoda sortiranja: vanjsko spajanje  Disk: 2304kB\n                           Radnik 0:  Metoda sortiranja: vanjsko spajanje  Disk: 2064kB\n                           Radnik 1:  Metoda sortiranja: vanjsko spajanje  Disk: 2384kB\n                           Radnik 2:  Metoda sortiranja: vanjsko spajanje  Disk: 2264kB\n                           Radnik 3:  Metoda sortiranja: vanjsko spajanje  Disk: 2336kB\n                           -&gt;  Paralelno Hash Spajanje  (tro\u0161ak=382571.01..1957960.99 redaka=71838 \u0161irina=27) (stvarno vrijeme=7036.917..7499.692 redaka=62519 petlje=5)\n                                 Hash Cond: (lineitem.l_orderkey = orders.o_orderkey)\n                                 -&gt;  Paralelno Sekvencijalno Skeniranje na lineitem  (tro\u0161ak=0.00..1552386.40 redaka=71838 \u0161irina=19) (stvarno vrijeme=0.583..4901.063 redaka=62519 petlje=5)\n                                       Filter: ((l_shipmode = BILO (&#039;{MAIL,AIR}&#039;::bpchar[])) I (l_commitdate &lt; l_receiptdate) I (l_shipdate = '1996-01-01'::datum) I (l_receiptdate &lt; &#039;1997-01-01 00:00:00&#039;::timestamp bez vremenske zone))\n                                       Redci uklonjeni filtriranjem: 11934691\n                                 -&gt;  Paralelno Hash  (tro\u0161ak=313722.45..313722.45 redaka=3750045 \u0161irina=20) (stvarno vrijeme=2011.518..2011.518 redaka=3000000 petlje=5)\n                                       Kante: 65536  Serije: 256  Kori\u0161tenje memorije: 3840kB\n                                       -&gt;  Paralelno Sekvencijalno Skeniranje na narud\u017ebama  (tro\u0161ak=0.00..313722.45 redaka=3750045 \u0161irina=20) (stvarno vrijeme=0.029..995.948 redaka=3000000 petlje=5)\n Vrijeme planiranja: 0.977 ms\n Vrijeme izvo\u0111enja: 7923.770 ms<\/code><\/pre>\n<p><\/p>\n<p>Upit 12 iz TPC-H jasno prikazuje paralelno hash povezivanje. Svaka radna jedinica sudjeluje u stvaranju zajedni\u010dke hash tablice.<\/p>\n<p><\/p>\n<h3 id=\"soedinenie-sliyaniem--merge-join\">Spojivanje preko spajanja \u2014 Merge Join<\/h3>\n<p><\/p>\n<p>Spojivanje preko spajanja nije paralelno po svojoj prirodi. Ne brinite ako je to posljednja faza upita, \u2014 mo\u017ee se i dalje izvr\u0161avati paralelno.<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">-- Upit 2 iz TPC-H\nobja&scaron;njenje (tro&scaron;kovi isklju\u010deni) odaberi s_acctbal, s_name, n_name, p_partkey, p_mfgr, s_address, s_phone, s_comment\niz    part, supplier, partsupp, nation, region\ngdje\n        p_partkey = ps_partkey\n        i s_suppkey = ps_suppkey\n        i p_size = 36\n        i p_type poput &#039;%BRASS&#039;\n        i s_nationkey = n_nationkey\n        i n_regionkey = r_regionkey\n        i r_name = &#039;AMERIKA&#039;\n        i ps_supplycost = (\n                odaberi\n                        min(ps_supplycost)\n                iz    partsupp, supplier, nation, region\n                gdje\n                        p_partkey = ps_partkey\n                        i s_suppkey = ps_suppkey\n                        i s_nationkey = n_nationkey\n                        i n_regionkey = r_regionkey\n                        i r_name = &#039;AMERIKA&#039;\n        )\nredom po s_acctbal opadaju\u0107e, n_name, s_name, p_partkey\nLIMIT 100;\n                                                PLAN UPITA\n----------------------------------------------------------------------------------------------------------\n Limit\n   -&amp;gt;  Sort\n         Klju\u010d sortiranja: supplier.s_acctbal DESC, nation.n_name, supplier.s_name, part.p_partkey\n         -&amp;gt;  Merge Join\n               Spajanje uvjeta: (part.p_partkey = partsupp.ps_partkey)\n               Uvjet za spajanje: (partsupp.ps_supplycost = (SubPlan 1))\n               -&amp;gt;  Gather Merge\n                     Planirano radnika: 4\n                     -&amp;gt;  Paralelno pretra\u017eivanje indeksa koriste\u0107i &lt;strong&gt;part_pkey&lt;\/strong&gt; na dijelu\n                           Filter: (((p_type)::text ~~ &#039;%BRASS&#039;::text) I (p_size = 36))\n               -&amp;gt;  Materijalizirati\n                     -&amp;gt;  Sort\n                           Klju\u010d sortiranja: partsupp.ps_partkey\n                           -&amp;gt;  Ugnije\u017e\u0111ena petlja\n                                 -&amp;gt;  Ugnije\u017e\u0111ena petlja\n                                       Uvjet za spajanje: (nation.n_regionkey = region.r_regionkey)\n                                       -&amp;gt;  Seq Scan na region\n                                             Filter: (r_name = &#039;AMERIKA&#039;::bpchar)\n                                       -&amp;gt;  Hash Join\n                                             Hash uvjet: (supplier.s_nationkey = nation.n_nationkey)\n                                             -&amp;gt;  Seq Scan na supplier\n                                             -&amp;gt;  Hash\n                                                   -&amp;gt;  Seq Scan na nation\n                                 -&amp;gt;  Pretra\u017eivanje indeksa koriste\u0107i idx_partsupp_suppkey na partsupp\n                                       Uvjet indeksa: (ps_suppkey = supplier.s_suppkey)\n               SubPlan 1\n                 -&amp;gt;  Agregat\n                       -&amp;gt;  Ugnije\u017e\u0111ena petlja\n                             Uvjet za spajanje: (nation_1.n_regionkey = region_1.r_regionkey)\n                             -&amp;gt;  Seq Scan na region region_1\n                                   Filter: (r_name = &#039;AMERIKA&#039;::bpchar)\n                             -&amp;gt;  Ugnije\u017e\u0111ena petlja\n                                   -&amp;gt;  Ugnije\u017e\u0111ena petlja\n                                         -&amp;gt;  Pretra\u017eivanje indeksa koriste\u0107i idx_partsupp_partkey na partsupp partsupp_1\n                                               Uvjet indeksa: (part.p_partkey = ps_partkey)\n                                         -&amp;gt;  Pretra\u017eivanje indeksa koriste\u0107i supplier_pkey na supplier supplier_1\n                                               Uvjet indeksa: (s_suppkey = partsupp_1.ps_suppkey)\n                                   -&amp;gt;  Pretra\u017eivanje indeksa koriste\u0107i nation_pkey na nation nation_1\n                                         Uvjet indeksa: (n_nationkey = supplier_1.s_nationkey)<\/code><\/pre>\n<p><\/p>\n<p>\u010cvor &#171;Merge Join&#187; nalazi se iznad &#171;Gather Merge&#187;. Tako da spajanje ne koristi paralelnu obradu. No, \u010dvor &#171;Parallel Index Scan&#187; i dalje poma\u017ee s dijelom. <code>part_pkey<\/code>.<\/p>\n<p><\/p>\n<h3 id=\"soedinenie-po-sekciyam\">Spojivanje po sekcijama<\/h3>\n<p><\/p>\n<p>U PostgreSQL 11 <noindex><a rel=\"nofollow\" href=\"http:\/\/ashutoshpg.blogspot.com\/2017\/12\/partition-wise-joins-divide-and-conquer.html\">spojanje po sekcijama<\/a><\/noindex> onemogu\u0107eno prema zadanim postavkama: ima vrlo skupo planiranje. Tablice sa sli\u010dnim sekcioniranjem mogu se spajati sekcija po sekciju. Tako \u0107e Postgres koristiti manje hash-tablice. Svako spajanje sekcija mo\u017ee biti paralelno.<\/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                    PLAN UPITA\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                        PLAN UPITA\n-----------------------------------------------------------\n Gather\n   Planirani radnici: 4\n   -&gt;  Paralelno dodavanje\n         -&gt;  Paralelno hash spajanje\n               Hash Cond: (t2_1.b = t1_1.a)\n               -&gt;  Paralelno Seq Scan on prt2_p2 t2_1\n                     Filter: ((b &gt;= 0) AND (b &lt;= 10000))\n               -&gt;  Paralelni Hash\n                     -&gt;  Paralelno Seq Scan on prt1_p2 t1_1\n                           Filter: (b = 0)\n         -&gt;  Paralelno hash spajanje\n               Hash Cond: (t2.b = t1.a)\n               -&gt;  Paralelno Seq Scan on prt2_p1 t2\n                     Filter: ((b &gt;= 0) AND (b &lt;= 10000))\n               -&gt;  Paralelni Hash\n                     -&gt;  Paralelno Seq Scan on prt1_p1 t1\n                           Filter: (b = 0)<\/code><\/pre>\n<p><\/p>\n<p>Glavno je da spojavanje po sekcijama mo\u017ee biti paralelno, samo ako su te sekcije dovoljno velike.<\/p>\n<p><\/p>\n<h3 id=\"parallelnoe-dopolnenie--parallel-append\">Paralelno dodavanje \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\">Paralelno dodavanje<\/a><\/noindex> mo\u017ee se koristiti umjesto razli\u010ditih blokova u razli\u010ditim radnim procesima. Obi\u010dno se to doga\u0111a s upitima UNION ALL. Nedostatak \u2014 manje paralelizma, jer svaki radni proces obra\u0111uje samo 1 upit.<\/p>\n<p><\/p>\n<p>Ovdje su pokrenuta 2 radna procesa, iako je uklju\u010deno 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 &lt;= date &#039;2000-12-01&#039; - interval &#039;105&#039; day;\n                                           QUERY PLAN\n------------------------------------------------------------------------------------------------\n Gather\n   Workers Planned: 2\n   -&gt;  Parallel Append\n         -&gt;  Aggregate\n               -&gt;  Seq Scan on lineitem\n                     Filter: (l_shipdate &lt;= &#039;2000-08-18 00:00:00&#039;::timestamp without time zone)\n         -&gt;  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\">Najva\u017enije varijable<\/h3>\n<p><\/p>\n<ul>\n<li>WORK_MEM ograni\u010dava koli\u010dinu memorije za svaki proces, ne samo za upite: work_mem <em> procesi <\/em> veze = jako puno memorije.<\/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 \u0107e radnih procesa program koristiti za paralelnu obradu prema planu.<\/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 prilago\u0111ava ukupni broj radnih procesa prema broju CPU jezgri na poslu\u017eitelju.<\/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 isto, ali za paralelne radne procese.<\/li>\n<\/ul>\n<p><\/p>\n<h3 id=\"itogi\">Zaklju\u010dci<\/h3>\n<p><\/p>\n<p>Po\u010dev\u0161i od verzije 9.6, paralelna obrada mo\u017ee zna\u010dajno pobolj\u0161ati performanse slo\u017eenih upita koji skeniraju mnogo redaka ili indeksa. U PostgreSQL 10, paralelno procesiranje je uklju\u010deno po defaultu. Ne zaboravite ga onemogu\u0107iti na poslu\u017eiteljima s velikim OLTP optere\u0107enjem. Sekvencijalna skeniranja ili skeniranja indeksa tro\u0161e jako puno resursa. Ako ne izvodite izvje\u0161taj na cijelom skupu podataka, mo\u017eete u\u010diniti upite u\u010dinkovitijima jednostavno dodavanjem nedostaju\u0107ih indeksa ili kori\u0161tenjem pravog sekcioniranja.<\/p>\n<p><\/p>\n<h3 id=\"ssylki\">Linkovi<\/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\">Paralelizam u 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\/hr\/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=\"hr_HR\" \/>\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\/hr\/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\udd47Paralelni upiti u PostgreSQL | ProHoster","description":"U modernim CPU-ima ima jako puno jezgri.","canonical_url":"https:\/\/prohoster.info\/hr\/blog\/administrirovanie\/parallelnye-zaprosy-v-postgresql","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"hr_HR","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\/hr\/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\/hr\/wp-json\/wp\/v2\/posts\/30900","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/comments?post=30900"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/posts\/30900\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/media\/22879"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/media?parent=30900"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/categories?post=30900"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/tags?post=30900"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}