{"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\/es\/blog\/administrirovanie\/parallelnye-zaprosy-v-postgresql","title":{"rendered":"Consultas paralelas en PostgreSQL","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"Consultas paralelas en PostgreSQL\" src=\"\/wp-content\/uploads\/2019\/04\/bbb4b4d3f8714bbdb9b26ada2f2253b5.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nEn los procesadores modernos hay muchos n\u00facleos. Durante a\u00f1os, las aplicaciones han enviado consultas a bases de datos en paralelo. Si se trata de una consulta de informes a m\u00faltiples filas en una tabla, se ejecuta m\u00e1s r\u00e1pido cuando utiliza varios n\u00facleos de CPU, y en PostgreSQL esto es posible a partir de la versi\u00f3n 9.6.<\/p>\n<p><\/p>\n<p>Se necesit\u00f3 3 a\u00f1os para implementar la funci\u00f3n de consultas paralelas, y fue necesario reescribir el c\u00f3digo en diferentes etapas de la ejecuci\u00f3n de las consultas. PostgreSQL 9.6 introdujo la infraestructura para futuras mejoras en el c\u00f3digo. En versiones posteriores, otros tipos de consultas tambi\u00e9n se ejecutan en paralelo.<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h3 id=\"ogranicheniya\">Limitaciones<\/h3>\n<p><\/p>\n<ul>\n<li>No active la ejecuci\u00f3n paralela si todos los n\u00facleos ya est\u00e1n ocupados, de lo contrario, otras consultas se ralentizar\u00e1n.<\/li>\n<li>Lo m\u00e1s importante es que el procesamiento paralelo con altos valores de WORK_MEM utiliza mucha memoria: cada conexi\u00f3n hash o cada ordenaci\u00f3n consume memoria en la cantidad de work_mem.<\/li>\n<li>Las consultas OLTP con baja latencia no pueden acelerarse mediante la ejecuci\u00f3n paralela. Y si una consulta devuelve una sola fila, el procesamiento paralelo solo la ralentizar\u00e1.<\/li>\n<li>A los desarrolladores les gusta usar el benchmark TPC-H. Quiz\u00e1s tenga consultas similares para una ejecuci\u00f3n paralela \u00f3ptima.<\/li>\n<li>Solo las consultas SELECT sin bloqueo de predicado se ejecutan en paralelo.<\/li>\n<li>A veces, una correcta indexaci\u00f3n es mejor que el escaneo secuencial de una tabla en modo paralelo.<\/li>\n<li>Las pausas en las consultas y los cursores no son compatibles.<\/li>\n<li>Las funciones de ventana y las funciones agregadas de conjuntos ordenados no son paralelas.<\/li>\n<li>No obtienes ninguna ventaja en la carga de trabajo de entrada\/salida.<\/li>\n<li>No existen algoritmos de ordenaci\u00f3n paralelos. Pero las consultas con ordenaciones pueden ejecutarse en paralelo en ciertos aspectos.<\/li>\n<li>Reemplace CTE (WITH \u2026) por un SELECT anidado para habilitar el procesamiento paralelo.<\/li>\n<li>Las envolturas de datos de terceros a\u00fan no admiten el procesamiento paralelo (\u00a1y podr\u00edan hacerlo!).<\/li>\n<li>FULL OUTER JOIN no es compatible.<\/li>\n<li>max_rows desactiva el procesamiento paralelo.<\/li>\n<li>Si hay una funci\u00f3n en la consulta que no est\u00e1 marcada como PARALLEL SAFE, ser\u00e1 de un solo hilo.<\/li>\n<li>El nivel de aislamiento de la transacci\u00f3n SERIALIZABLE desactiva el procesamiento paralelo.<\/li>\n<\/ul>\n<p><\/p>\n<h3 id=\"testovaya-sreda\">Entorno de prueba<\/h3>\n<p><\/p>\n<p>Los desarrolladores de PostgreSQL intentaron reducir el tiempo de respuesta de las consultas del benchmark TPC-H. Descargue el benchmark y <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/tvondra\/pg_tpch\">ad\u00e1ptelo a PostgreSQL<\/a><\/noindex>. Este uso no oficial del benchmark TPC-H no es para comparar bases de datos o hardware.<\/p>\n<p><\/p>\n<ol>\n<li>Descargue TPC-H_Tools_v2.17.3.zip (o una versi\u00f3n m\u00e1s nueva) <noindex><a rel=\"nofollow\" href=\"http:\/\/www.tpc.org\/tpc_documents_current_versions\/current_specifications.asp\">desde el sitio oficial de TPC<\/a><\/noindex>.<\/li>\n<li>Renombre makefile.suite a Makefile y modifique seg\u00fan se describe aqu\u00ed: <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/tvondra\/pg_tpch\">https:\/\/github.com\/tvondra\/pg_tpch <\/a><\/noindex>. Compile el c\u00f3digo con el comando make.<\/li>\n<li>Genere los datos: <code>.\/dbgen -s 10<\/code> crea una base de datos de 23 GB. Esto es suficiente para observar la diferencia en el rendimiento de consultas paralelas y no paralelas.<\/li>\n<li>Convierta los archivos <code>tbl<\/code> en <code>csv con for<\/code> y <code>sed<\/code>.<\/li>\n<li>Clone el repositorio <code>pg_tpch<\/code> y copie los archivos <code>csv<\/code> en <code>pg_tpch\/dss\/data<\/code>.<\/li>\n<li>Cree consultas con el comando <code>qgen<\/code>.<\/li>\n<li>Cargue los datos en la base con el comando <code>.\/tpch.sh<\/code>.<\/li>\n<\/ol>\n<p><\/p>\n<h3 id=\"parallelnoe-posledovatelnoe-skanirovanie\">Escaneo secuencial paralelo<\/h3>\n<p><\/p>\n<p>Puede ser m\u00e1s r\u00e1pido no debido a la lectura paralela, sino porque los datos est\u00e1n distribuidos entre muchos n\u00facleos de la CPU. En los sistemas operativos modernos, los archivos de datos de PostgreSQL se almacenan en cach\u00e9 de manera eficiente. Con la lectura anticipada, se puede obtener del almacenamiento un bloque mayor de lo que solicita el demonio PG. Por lo tanto, el rendimiento de la consulta no est\u00e1 limitado por la entrada y salida del disco. Consume ciclos de CPU para:<\/p>\n<p><\/p>\n<ul>\n<li>leer filas una por una de las p\u00e1ginas de la tabla;<\/li>\n<li>comparar los valores de las filas y las condiciones <code>WHERE<\/code>.<\/li>\n<\/ul>\n<p><\/p>\n<p>Realicemos una consulta simple <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>El escaneo secuencial devuelve demasiadas filas sin agregaci\u00f3n, por lo que la consulta se ejecuta con un solo n\u00facleo de CPU.<\/p>\n<p><\/p>\n<p>Si agregamos <code>SUM()<\/code>, se puede ver que dos procesos de trabajo ayudan a acelerar la consulta:<\/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; Parallel 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\">Agregaci\u00f3n paralela<\/h3>\n<p><\/p>\n<p>El nodo \u00abEscaneo Secuencial Paralelo\u00bb genera filas para la agregaci\u00f3n parcial. El nodo \u00abAgregaci\u00f3n Parcial\u00bb recorta estas filas utilizando <code>SUM()<\/code>. Al final, el contador SUM de cada proceso de trabajo es recopilado por el nodo \u00abReunir\u00bb.<\/p>\n<p><\/p>\n<p>El resultado final es calculado por el nodo \u00abFinalizar Agregaci\u00f3n\u00bb. Si tienes tus propias funciones de agregaci\u00f3n, no olvides marcarlas como \u00abparalelamente seguras\u00bb.<\/p>\n<p><\/p>\n<h3 id=\"kolichestvo-rabochih-processov\">N\u00famero de procesos de trabajo<\/h3>\n<p><\/p>\n<p>El n\u00famero de procesos de trabajo se puede aumentar sin reiniciar el servidor:<\/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; Parallel 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>\u00bfQu\u00e9 est\u00e1 ocurriendo aqu\u00ed? El n\u00famero de procesos de trabajo se ha duplicado, y la solicitud se volvi\u00f3 solo 1,6599 veces m\u00e1s r\u00e1pida. Los c\u00e1lculos son interesantes. Ten\u00edamos 2 procesos de trabajo y 1 l\u00edder. Despu\u00e9s del cambio, ahora son 4+1.<\/p>\n<p><\/p>\n<p>Nuestra aceleraci\u00f3n m\u00e1xima por procesamiento paralelo: 5\/3 = 1,66(6) veces.<\/p>\n<p><\/p>\n<h2 id=\"kak-eto-rabotaet\">\u00bfC\u00f3mo funciona?<\/h2>\n<p><\/p>\n<h3 id=\"processy\">Procesos<\/h3>\n<p><\/p>\n<p>La ejecuci\u00f3n de una solicitud siempre comienza con el proceso l\u00edder. El l\u00edder realiza toda la parte no paralela y parte del procesamiento paralelo. Otros procesos que realizan las mismas solicitudes se llaman procesos de trabajo. El procesamiento paralelo utiliza la infraestructura <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/11\/bgworker.html\">de procesos de trabajo en segundo plano din\u00e1micos<\/a><\/noindex> (a partir de la versi\u00f3n 9.4). Dado que otras partes de PostgreSQL utilizan procesos, no hilos, una solicitud con 3 procesos de trabajo podr\u00eda ser 4 veces m\u00e1s r\u00e1pida que el procesamiento tradicional.<\/p>\n<p><\/p>\n<h3 id=\"vzaimodeystvie\">Interacci\u00f3n<\/h3>\n<p><\/p>\n<p>Los procesos de trabajo se comunican con el l\u00edder a trav\u00e9s de una cola de mensajes (basada en memoria compartida). Cada proceso tiene 2 colas: una para errores y otra para tuplas.<\/p>\n<p><\/p>\n<h3 id=\"skolko-nuzhno-rabochih-processov\">\u00bfCu\u00e1ntos procesos de trabajo se necesitan?<\/h3>\n<p><\/p>\n<p>El l\u00edmite m\u00ednimo es establecido por el par\u00e1metro <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>. Luego, el ejecutor de la consulta toma procesos de trabajo del conjunto, limitado por el par\u00e1metro <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>. El \u00faltimo l\u00edmite es <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>, es decir, el n\u00famero total de procesos en segundo plano.<\/p>\n<p><\/p>\n<p>Si no se puede asignar un proceso de trabajo, el procesamiento ser\u00e1 de un solo proceso.<\/p>\n<p><\/p>\n<p>El planificador de consultas puede reducir los procesos de trabajo dependiendo del tama\u00f1o de la tabla o \u00edndice. Para esto, hay par\u00e1metros <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> y <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 tabla =&gt; 1 trabajador\n24MB tabla =&gt; 2 trabajadores\n72MB tabla =&gt; 3 trabajadores\nx =&gt; log(x \/ min_parallel_table_scan_size) \/ log(3) + 1 trabajador<\/code><\/pre>\n<p><\/p>\n<p>Cada vez que la tabla es 3 veces m\u00e1s grande que <code>min_parallel_(index|table)_scan_size<\/code>, PostgreSQL agrega un proceso de trabajo. El n\u00famero de procesos de trabajo no se basa en costos. La dependencia c\u00edclica complica las implementaciones complejas. En su lugar, el planificador usa reglas simples.<\/p>\n<p><\/p>\n<p>En la pr\u00e1ctica, estas reglas no siempre son adecuadas para producci\u00f3n, por lo que se puede cambiar el n\u00famero de procesos de trabajo para una tabla espec\u00edfica: ALTER TABLE \u2026 SET (<code>parallel_workers = N<\/code>).<\/p>\n<p><\/p>\n<h3 id=\"pochemu-parallelnaya-obrabotka-ne-ispolzuetsya\">\u00bfPor qu\u00e9 no se utiliza el procesamiento paralelo?<\/h3>\n<p><\/p>\n<p>Adem\u00e1s de una larga lista de restricciones, hay m\u00e1s comprobaciones de costos:<\/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 para evitar el procesamiento paralelo de consultas cortas. Este par\u00e1metro estima el tiempo para la preparaci\u00f3n de la memoria, el inicio del proceso y el intercambio inicial de datos.<\/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>: la comunicaci\u00f3n del l\u00edder con los trabajadores puede prolongarse proporcionalmente al n\u00famero de tuplas de los procesos de trabajo. Este par\u00e1metro calcula los costos del intercambio de datos.<\/p>\n<p><\/p>\n<h3 id=\"soedineniya-vlozhennyh-ciklov--nested-loop-join\">Conexiones de bucles anidados \u2014 Nested Loop Join<\/h3>\n<p><\/p>\n<pre><code class=\"plaintext\">PostgreSQL 9.6+ puede ejecutar ciclos anidados en paralelo, lo que constituye una operaci\u00f3n simple.\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>La recopilaci\u00f3n ocurre en la \u00faltima etapa, por lo que el Nested Loop Left Join es una operaci\u00f3n paralela. El Parallel Index Only Scan apareci\u00f3 solo en la versi\u00f3n 10. Funciona de manera similar al escaneo secuencial paralelo. La condici\u00f3n <code>c_custkey = o_custkey<\/code> lee un pedido por cada fila del cliente. Por lo tanto, no es paralelo.<\/p>\n<p><\/p>\n<h3 id=\"hesh-soedinenie--hash-join\">Uni\u00f3n hash \u2014 Hash Join<\/h3>\n<p><\/p>\n<p>Cada proceso de trabajo crea su tabla hash hasta PostgreSQL 11. Y si hay m\u00e1s de cuatro procesos, el rendimiento no aumentar\u00e1. En la nueva versi\u00f3n, la tabla hash es compartida. Cada proceso de trabajo puede usar WORK_MEM para crear una tabla hash.<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">seleccionar\n        l_shipmode,\n        suma(caso\n                cuando o_orderpriority = '1-URGENTE'\n                        o o_orderpriority = '2-ALTO'\n                        entonces 1\n                else 0\n        fin) como high_line_count,\n        suma(caso\n                cuando o_orderpriority  '1-URGENTE'\n                        y o_orderpriority  '2-ALTO'\n                        entonces 1\n                else 0\n        fin) como low_line_count\nde\n        \u00f3rdenes,\n        l\u00ednea\ndonde\n        o_orderkey = l_orderkey\n        y l_shipmode en ('CORREO', 'A\u00c9REO')\n        y l_commitdate &lt; l_receiptdate\n        y l_shipdate = fecha '1996-01-01'\n        y l_receiptdate &lt; fecha &#039;1996-01-01&#039; + intervalo &#039;1&#039; a\u00f1o\ngrupo por\n        l_shipmode\nordenar por\n        l_shipmode\nLIMITE 1;\n                                                                                                                                    PLAN DE CONSULTA\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n L\u00edmite  (costo=1964755.66..1964961.44 filas=1 ancho=27) (tiempo real=7579.592..7922.997 filas=1 bucles=1)\n   -&gt;  Finalizar GroupAggregate  (costo=1964755.66..1966196.11 filas=7 ancho=27) (tiempo real=7579.590..7579.591 filas=1 bucles=1)\n         Clave de Grupo: l\u00ednea.l_shipmode\n         -&gt;  Reunir Junta  (costo=1964755.66..1966195.83 filas=28 ancho=27) (tiempo real=7559.593..7922.319 filas=6 bucles=1)\n               Trabajadores Planeados: 4\n               Trabajadores Lanzados: 4\n               -&gt;  Agrupar Parcial  (costo=1963755.61..1965192.44 filas=7 ancho=27) (tiempo real=7548.103..7564.592 filas=2 bucles=5)\n                     Clave de Grupo: l\u00ednea.l_shipmode\n                     -&gt;  Ordenar  (costo=1963755.61..1963935.20 filas=71838 ancho=27) (tiempo real=7530.280..7539.688 filas=62519 bucles=5)\n                           Clave de Orden: l\u00ednea.l_shipmode\n                           M\u00e9todo de Orden: fusi\u00f3n externa  Disco: 2304kB\n                           Trabajador 0:  M\u00e9todo de Orden: fusi\u00f3n externa  Disco: 2064kB\n                           Trabajador 1:  M\u00e9todo de Orden: fusi\u00f3n externa  Disco: 2384kB\n                           Trabajador 2:  M\u00e9todo de Orden: fusi\u00f3n externa  Disco: 2264kB\n                           Trabajador 3:  M\u00e9todo de Orden: fusi\u00f3n externa  Disco: 2336kB\n                           -&gt;  Uni\u00f3n Hash Paralela  (costo=382571.01..1957960.99 filas=71838 ancho=27) (tiempo real=7036.917..7499.692 filas=62519 bucles=5)\n                                 Condici\u00f3n Hash: (l\u00ednea.l_orderkey = \u00f3rdenes.o_orderkey)\n                                 -&gt;  Escaneo Secuencial Paralelo en l\u00ednea  (costo=0.00..1552386.40 filas=71838 ancho=19) (tiempo real=0.583..4901.063 filas=62519 bucles=5)\n                                       Filtro: ((l_shipmode = CUALQUIERA (&#039;{CORREO,A\u00c9REO}&#039;::bpchar[])) Y (l_commitdate &lt; l_receiptdate) Y (l_shipdate = '1996-01-01'::fecha) Y (l_receiptdate &lt; &#039;1997-01-01 00:00:00&#039;::marca de tiempo sin zona horaria))\n                                       Filas Eliminadas por Filtro: 11934691\n                                 -&gt;  Hash Paralelo  (costo=313722.45..313722.45 filas=3750045 ancho=20) (tiempo real=2011.518..2011.518 filas=3000000 bucles=5)\n                                       Cubos: 65536  Lotes: 256  Uso de Memoria: 3840kB\n                                       -&gt;  Escaneo Secuencial Paralelo en \u00f3rdenes  (costo=0.00..313722.45 filas=3750045 ancho=20) (tiempo real=0.029..995.948 filas=3000000 bucles=5)\n Tiempo de Planificaci\u00f3n: 0.977 ms\n Tiempo de Ejecuci\u00f3n: 7923.770 ms<\/code><\/pre>\n<p><\/p>\n<p>La consulta 12 de TPC-H muestra claramente la uni\u00f3n hash paralela. Cada proceso de trabajo participa en la creaci\u00f3n de una tabla hash com\u00fan.<\/p>\n<p><\/p>\n<h3 id=\"soedinenie-sliyaniem--merge-join\">Uni\u00f3n de fusi\u00f3n \u2014 Merge Join<\/h3>\n<p><\/p>\n<p>La uni\u00f3n de fusi\u00f3n no es paralela por su naturaleza. No te preocupes si esta es la \u00faltima etapa de la consulta, a\u00fan puede ejecutarse en paralelo.<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">-- Consulta 2 de TPC-H\nexplicar (costos apagados) seleccionar s_acctbal, s_name, n_name, p_partkey, p_mfgr, s_address, s_phone, s_comment\ndesde parte, proveedor, partsupp, naci&oacute;n, regi&oacute;n\ndonde\n        p_partkey = ps_partkey\n        y s_suppkey = ps_suppkey\n        y p_size = 36\n        y p_type como &#039;%BRASS&#039;\n        y s_nationkey = n_nationkey\n        y n_regionkey = r_regionkey\n        y r_name = &#039;AM&Eacute;RICA&#039;\n        y ps_supplycost = (\n                seleccionar\n                        min(ps_supplycost)\n                desde partsupp, proveedor, naci&oacute;n, regi&oacute;n\n                donde\n                        p_partkey = ps_partkey\n                        y s_suppkey = ps_suppkey\n                        y s_nationkey = n_nationkey\n                        y n_regionkey = r_regionkey\n                        y r_name = &#039;AM&Eacute;RICA&#039;\n        )\nordenar por s_acctbal desc, n_name, s_name, p_partkey\nLIMITE 100;\n                                                PLAN DE CONSULTA\n----------------------------------------------------------------------------------------------------------\n L&iacute;mite\n   -&amp;gt;  Ordenar\n         Clave de Orden: supplier.s_acctbal DESC, nation.n_name, supplier.s_name, part.p_partkey\n         -&amp;gt;  Uni&oacute;n de Fusiones\n               Condici&oacute;n de fusi&oacute;n: (part.p_partkey = partsupp.ps_partkey)\n               Filtro de uni&oacute;n: (partsupp.ps_supplycost = (Subplan 1))\n               -&amp;gt;  Reunir fusi&oacute;n\n                     Trabajadores planificados: 4\n                     -&amp;gt;  Escaneo de &iacute;ndice paralelo utilizando &lt;strong&gt;part_pkey&lt;\/strong&gt; en parte\n                           Filtro: (((p_type)::text ~~ &#039;%BRASS&#039;::text) Y (p_size = 36))\n               -&amp;gt;  Materializar\n                     -&amp;gt;  Ordenar\n                           Clave de Orden: partsupp.ps_partkey\n                           -&amp;gt;  Bucle anidado\n                                 -&amp;gt;  Bucle anidado\n                                       Filtro de uni&oacute;n: (nation.n_regionkey = region.r_regionkey)\n                                       -&amp;gt;  Escaneo secuencial en regi&oacute;n\n                                             Filtro: (r_name = &#039;AM&Eacute;RICA&#039;::bpchar)\n                                       -&amp;gt;  Uni&oacute;n hash\n                                             Condici&oacute;n hash: (supplier.s_nationkey = nation.n_nationkey)\n                                             -&amp;gt;  Escaneo secuencial en proveedor\n                                             -&amp;gt;  Hash\n                                                   -&amp;gt;  Escaneo secuencial en naci&oacute;n\n                                 -&amp;gt;  Escaneo de &iacute;ndice utilizando idx_partsupp_suppkey en partsupp\n                                       Condici&oacute;n de &iacute;ndice: (ps_suppkey = supplier.s_suppkey)\n               Subplan 1\n                 -&amp;gt;  Agregar\n                       -&amp;gt;  Bucle anidado\n                             Filtro de uni&oacute;n: (nation_1.n_regionkey = region_1.r_regionkey)\n                             -&amp;gt;  Escaneo secuencial en regi&oacute;n region_1\n                                   Filtro: (r_name = &#039;AM&Eacute;RICA&#039;::bpchar)\n                             -&amp;gt;  Bucle anidado\n                                   -&amp;gt;  Bucle anidado\n                                         -&amp;gt;  Escaneo de &iacute;ndice utilizando idx_partsupp_partkey en partsupp partsupp_1\n                                               Condici&oacute;n de &iacute;ndice: (part.p_partkey = ps_partkey)\n                                         -&amp;gt;  Escaneo de &iacute;ndice utilizando supplier_pkey en proveedor supplier_1\n                                               Condici&oacute;n de &iacute;ndice: (s_suppkey = partsupp_1.ps_suppkey)\n                                   -&amp;gt;  Escaneo de &iacute;ndice utilizando nation_pkey en naci&oacute;n naci&oacute;n_1\n                                         Condici&oacute;n de &iacute;ndice: (n_nationkey = supplier_1.s_nationkey)<\/code><\/pre>\n<p><\/p>\n<p>El nodo \u00abUni\u00f3n por Mezcla\u00bb est\u00e1 sobre el \u00abReunir Mezcla\u00bb. As\u00ed que la fusi\u00f3n no utiliza procesamiento paralelo. Pero el nodo \u00abEscaneo de \u00cdndice Paralelo\u00bb todav\u00eda ayuda con el segmento. <code>part_pkey<\/code>.<\/p>\n<p><\/p>\n<h3 id=\"soedinenie-po-sekciyam\">Uni\u00f3n por secciones<\/h3>\n<p><\/p>\n<p>En PostgreSQL 11 <noindex><a rel=\"nofollow\" href=\"http:\/\/ashutoshpg.blogspot.com\/2017\/12\/partition-wise-joins-divide-and-conquer.html\">uni\u00f3n por secciones<\/a><\/noindex> desactivada por defecto: tiene un costo de planificaci\u00f3n muy alto. Las tablas con particionamiento similar se pueden unir secci\u00f3n por secci\u00f3n. As\u00ed, Postgres utilizar\u00e1 tablas hash m\u00e1s peque\u00f1as. Cada uni\u00f3n de secciones puede ser paralela.<\/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 DE CONSULTA\n---------------------------------------------------\n Append\n   -&gt;  Hash Join\n         Condici\u00f3n Hash: (t2.b = t1.a)\n         -&gt;  Secuencia de Escaneo en prt2_p1 t2\n               Filtro: ((b &gt;= 0) AND (b &lt;= 10000))\n         -&gt;  Hash\n               -&gt;  Secuencia de Escaneo en prt1_p1 t1\n                     Filtro: (b = 0)\n   -&gt;  Hash Join\n         Condici\u00f3n Hash: (t2_1.b = t1_1.a)\n         -&gt;  Secuencia de Escaneo en prt2_p2 t2_1\n               Filtro: ((b &gt;= 0) AND (b &lt;= 10000))\n         -&gt;  Hash\n               -&gt;  Secuencia de Escaneo en prt1_p2 t1_1\n                     Filtro: (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 DE CONSULTA\n-----------------------------------------------------------\n Gather\n   Trabajadores Planificados: 4\n   -&gt;  Append Paralelo\n         -&gt;  Join Hash Paralelo\n               Condici\u00f3n Hash: (t2_1.b = t1_1.a)\n               -&gt;  Secuencia de Escaneo Paralelo en prt2_p2 t2_1\n                     Filtro: ((b &gt;= 0) AND (b &lt;= 10000))\n               -&gt;  Hash\n                     -&gt;  Secuencia de Escaneo Paralelo en prt1_p2 t1_1\n                           Filtro: (b = 0)\n         -&gt;  Join Hash Paralelo\n               Condici\u00f3n Hash: (t2.b = t1.a)\n               -&gt;  Secuencia de Escaneo Paralelo en prt2_p1 t2\n                     Filtro: ((b &gt;= 0) AND (b &lt;= 10000))\n               -&gt;  Hash\n                     -&gt;  Secuencia de Escaneo Paralelo en prt1_p1 t1\n                           Filtro: (b = 0)<\/code><\/pre>\n<p><\/p>\n<p>Lo principal es que la uni\u00f3n por secciones puede ser paralela solo si estas secciones son lo suficientemente grandes.<\/p>\n<p><\/p>\n<h3 id=\"parallelnoe-dopolnenie--parallel-append\">Adici\u00f3n paralela \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> puede ser utilizada en lugar de diferentes bloques en diferentes procesos de trabajo. Esto suele suceder con consultas UNION ALL. La desventaja es menos paralelismo, ya que cada proceso de trabajo maneja solo 1 consulta.<\/p>\n<p><\/p>\n<p>Aqu\u00ed se est\u00e1n ejecutando 2 procesos de trabajo, aunque se han habilitado 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\">Las variables m\u00e1s importantes<\/h3>\n<p><\/p>\n<ul>\n<li>WORK_MEM limita la cantidad de memoria para cada proceso, no solo para las consultas: work_mem <em> procesos <\/em> conexiones = mucha memoria.<\/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 cu\u00e1ntos procesos de trabajo utilizar\u00e1 el programa ejecutante para el procesamiento paralelo del plan.<\/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 ajusta el n\u00famero total de procesos de trabajo seg\u00fan el n\u00famero de n\u00facleos de CPU en el servidor.<\/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 lo mismo, pero para procesos de trabajo paralelos.<\/li>\n<\/ul>\n<p><\/p>\n<h3 id=\"itogi\">Resultados<\/h3>\n<p><\/p>\n<p>A partir de la versi\u00f3n 9.6, el procesamiento paralelo puede mejorar significativamente el rendimiento de consultas complejas que escanean muchas filas o \u00edndices. En PostgreSQL 10, el procesamiento paralelo est\u00e1 habilitado por defecto. Recuerde desactivarlo en servidores con alta carga de trabajo OLTP. Los escaneos secuenciales o escaneos de \u00edndices consumen muchos recursos. Si no est\u00e1 ejecutando informes sobre todo el conjunto de datos, las consultas se pueden hacer m\u00e1s eficientes simplemente agregando \u00edndices faltantes o utilizando la partici\u00f3n adecuada.<\/p>\n<p><\/p>\n<h3 id=\"ssylki\">Enlaces<\/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\">Paralelismo en PostgreSQL 11<\/a><\/noindex><\/li>\n<\/ul>\n<p>Fuente: <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.3 - 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\/es\/blog\/administrirovanie\/parallelnye-zaprosy-v-postgresql\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"es_ES\" \/>\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\/es\/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\udd47Consultas paralelas en PostgreSQL | ProHoster","description":"En los CPU modernos hay muchos n\u00facleos.","canonical_url":"https:\/\/prohoster.info\/es\/blog\/administrirovanie\/parallelnye-zaprosy-v-postgresql","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"es_ES","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\/es\/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\/es\/wp-json\/wp\/v2\/posts\/30900","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/comments?post=30900"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/posts\/30900\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/media\/22879"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/media?parent=30900"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/categories?post=30900"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/tags?post=30900"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}