PARALLEL QUERY EN POSTGRESQL

Hoy os traemos una de las funcionalidades más recientes e interesantes dentro del motor de base de datos PostgreSQL. A lo largo de las últimas versiones hemos ido viendo sucesivos parches para dotar a este motor de base de datos de lo que ampliamente se conoce como Parallel Query.

¿Qué es Parallel Query?

Es un método para mejorar el tiempo de ejecución de una consulta SQL al dividir la carga de trabajo de la consulta y ejecutarla en paralelo por múltiples procesos. Se beneficia enormemente de máquinas con múltiples cores y queries muy pesadas.

¿Cómo funciona Parallel Query en PostgreSQL?

Cuando el optimizador considera que la estrategia de parallel query es la más óptima de entre todos los planes de ejecución, este crea un plan que incluye el nodo Gather o Gather Merge. En el caso de que esta entrada esté en lo más alto del árbol del EXPLAIN toda la query se ejecutará en paralelo. En el caso de que figure en una posición inferior, solo esta parte de la query se ejecutará en paralelo.

imdb=# explain analyze select count(*) from title_basics ;
                                   QUERY PLAN 
Finalize Aggregate  (cost=101220.45..101220.46 rows=1 width=8) (actual time=563.320..563.320 rows=1 l
oops=1)
   -> Gather  (cost=101220.24..101220.45 rows=2 width=8) (actual time=561.947..563.304 rows=3 loops=1)
         Workers Planned: 2
         Workers Launched: 2
         -> Partial Aggregate  (cost=100220.24..100220.25 rows=1 width=8) (actual time=554.448..554.4
48 rows=1 loops=3)
               -> Parallel Seq Scan on title_basics  (cost=0.00..95030.59 rows=2075859 width=0) (actu
al time=3.524..374.295 rows=1660777 loops=3)
Planning time: 0.404 ms
Execution time: 563.555 ms
(8 rows)

En el cuadro se aprecia como para la ejecución de la query en cuestión se solicitan hasta dos background worker processes, que es el máximo número de procesos que pueden levantarse por nodo por defecto configurados por max_parallel_workers_per_gather  Adicionalmente, será necesario que el parámetro dynamic_shared_memory_type esté configurado a un valor diferente a none para favorecer que los procesos compartan información a través de esta memoria dinámica compartida.

Si aumentamos el valor de max_parallel_workers_per_gathera 8 y ejecutamos la misma query:

imdb=# set max_parallel_workers_per_gather = 8;
SET
imdb=# explain analyze select count(*) from title_basics;
                                   QUERY PLAN 
Finalize Aggregate  (cost=90841.36..90841.37 rows=1 width=8) (actual time=485.215..485.215 rows=1 loo
ps=1)
   -> Gather  (cost=90840.94..90841.35 rows=4 width=8) (actual time=473.937..485.206 rows=5 loops=1)
         Workers Planned: 4
         Workers Launched: 4
         -> Partial Aggregate  (cost=89840.94..89840.95 rows=1 width=8) (actual time=457.623..457.623
rows=1 loops=5)
               -> Parallel Seq Scan on title_basics  (cost=0.00..86727.15 rows=1245516 width=0) (actu
al time=0.077..286.119 rows=996466 loops=5)
Planning time: 0.161 ms
Execution time: 485.478 ms
(8 rows)

Apreciamos que el planificador de los 8 procesos que podría utilizar ha estimado que 4 es el valor óptimo y ahora en lugar de usar 2 emplea estos 4 mejorando la respuesta de la query.

Podemos influir en los costes del plan de ejecución con paralelismo para forzar el uso de esta característica. Esto se puede hacer a través de los siguientes parámetros de configuración parallel_setup_cost y el  parallel_tuple_cost. Disminuyéndolos lograremos forzar esta paralelización:

imdb=# set parallel_setup_cost = 1;
SET
imdb=# set parallel_tuple_cost = 0.001;
SET
imdb=# explain analyze select * from title_basics ;
                                    QUERY PLAN           
Gather  (cost=1.00..91710.22 rows=4982062 width=86) (actual time=1.196..4114.111 rows=4982332 loops=1)
Workers Planned: 4
Workers Launched: 4
  -> Parallel Seq Scan on title_basics  (cost=0.00..86727.15 rows=1245516 width=86) (actual time=0.138..245.636 rows=996466 loops=5)
Planning time: 0.134 ms
Execution time: 4332.653 ms
(6 rows)

Pero mucho cuidado al alterar el comportamiento del planificador porque puede que no siempre obtengamos un resultado más óptimo, como es este caso. Es un buen momento para recordar que los parámetros del planificador sólo deben ser configurados a nivel de sesión y deben ser reestablecidos tras la ejecución ya que en un comportamiento habitual sus configuraciones son las más idóneas por defecto. Aún existen otros dos parámetros que intervienen en el comportamiento del parallel query, estos son min_parallel_table_scan_size  y min_parallel_index_scan_size. Básicamente son la cantidad mínima de información de una tabla o índice que deben escaneados para que se considere evaluar un plan con paralelismo.

Existen múltiples motivos que pueden provocar que una query no se ejecute en paralelo incluso cuando el plan más óptimo así lo indica. Estos son los motivos por los que esto podría no suceder:

  • No se obtiene un backgound worker debido a la limitación de que el número máximo de estos no pueden exceder de max_worker_processess 
  • No se obtiene un background worker debido a la limitación de que el número máximo de workers en paralelo no puede exceder de max_parallel_workers 
  • Una declaración del tipò CREATE TABLE .. AS EXECUTE .. . Esta construcción convierte lo que habría sido una operación de lectura en una de lectura-escritura haciéndola ininteligible para el parallel query.
  • Cuando el nivel de aislamiento está configurado a serializable.

Al ser una de las funcionalidades más modernas y esperadas, esperamos las mejoras que irán surgiendo a lo largo de las próximas versiones. No dejen de consultar en https://www.postgresql.org/developer/roadmap/ para ver el roadmap de PostgreSQL.

Bibliografía

https://www.postgresql.org/docs/10/

https://www.postgresql.org/docs/10/static/how-parallel-query-works.html

https://www.postgresql.org/docs/10/static/when-can-parallel-query-be-used.html

https://www.imdb.com/interfaces/

 

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.