Chapter 6. Consejos de rendimiento

Table of Contents

6.1. Tablas pequeñas de geometrías grandes

6.1.1. Descripcion del problema

Versiones actuales de PostgreSQL (incluyendo la 8.0) tienen algunas debilidades en la optimización de consultas respecto a tablas TOAST. Las tablas TOAST son una especie de "cámara de extensiones" utilizadas para almacenar valores grandes (en sentido de tamaño de datos) que no se pueden mostrar en paginas de datos (como textos largos, imágenes o geometrías complejas con muchos vértices). Para mas información visita the PostgreSQL Documentation for TOAST

El problema aparece si ocurre que tienes una tabla con geometrías bastante grandes, pero no demasiadas filas de ellas (como una tabla que contiene los límites de todos los países europeos en alta resolución). A continuación, la tabla en sí es pequeña, pero utiliza una gran cantidad de espacio TOAST. En nuestro caso de ejemplo, la tabla en sí tenía alrededor de 80 filas y se utiliza sólo 3 páginas de datos, pero la tabla TOAST utiliza 8225 páginas.

Ahora al emitir una consulta en la que utilizas el operador geométrico && para buscar un límite que coincide sólo unas pocas de esas filas, el optimizador de consultas ve que la tabla sólo tiene 3 páginas y 80 filas. Se estima que un escaseo secuencial en una tabla pequeña de este tipo es mucho más rápida que usando un índice. Y por lo que decide ignorar el índice de GIST. Por lo general, esta estimación es correcta. Pero en nuestro caso, el operador && tiene que buscar en cada geometría del disco la comparación de los limites, y leer todas las páginas TOAST también.

Para comprobar si padeces de este error, utiliza el comando "EXPLAIN ANALYZE" postgresql. Para obtener más información y los detalles técnicos, puedes leer el hilo en la lista de correo de rendimiento postgres: http://archives.postgresql.org/pgsql-performance/2005-02/msg00030.php

and newer thread on PostGIS https://lists.osgeo.org/pipermail/postgis-devel/2017-June/026209.html

6.1.2. Soluciones provisionales

La gente de PostgreSQL esta intentando resolver este problema haciendo la estimación de la consulta compatible con TOAST. Por el momento, aquí van dos soluciones provisionales:

La primera consiste en forzar la consulta a utilizar indices. Envia "SET enable_seqscan TO off;" al servidor antes de ejecutar la consulta. Esto, básicamente fuerza al planificador de consultas a evitar exploraciones secuenciales siempre que sea posible. Por lo tanto, utiliza el índice GIST como de costumbre. Pero este comando debe ser establecido en cada conexión, y hace que el planeador de consultas cometa errores de estimación en otros casos, por lo que debes enviar al servidor "SET enable_seqscan TO on;" después de la consulta.

La segunda solución es hacer el escaseo secuencia tan rápido como el planificador de consultas cree. Esto, se puede lograr creando una consulta que "cachee" los limites o bbox, y hacer coincidir en contra de esta. En nuestro ejemplo, los comandos son:

SELECT AddGeometryColumn('myschema','mytable','bbox','4326','GEOMETRY','2');
UPDATE mytable SET bbox = ST_Envelope(ST_Force2D(geom));

Ahora cambia tu consulta para utilizar el operador espacial && con bbox en vez de geom_column, así:

SELECT geom_column
FROM mytable
WHERE bbox && ST_SetSRID('BOX3D(0 0,1 1)'::box3d,4326);

Por supuesto, si añades o cambias filas de "mutable", tienes que mantener el campo bbox sincronizado. La forma mas transparente de hacerlo son los triggers o funciones disparadoras, pero también puedes modificar tu aplicación para mantener la columna bbox o ejecutar la consulta UPDATE siguiente después de cada modificación.

6.2. CLUSTERing o indices geométricos

Para las tablas que en su mayoría son de sólo lectura, y donde se utiliza un índice único para la mayoría de las consultas, PostgreSQL ofrece el comando CLUSTER. Este comando reordena físicamente todas las filas de datos en el mismo orden que los criterios de índice, dando dos ventajas de rendimiento: En primer lugar, para los recorridos de intervalo del índice, el número de búsquedas en la tabla de datos se reduce drásticamente. En segundo lugar, si el conjunto de trabajo se concentra en algunos intervalos pequeños en los índices, tienes un caché más eficiente porque las filas de datos se distribuyen a lo largo de un menor número de páginas de datos. (Te invitamos a leer la documentación de comandos CLUSTER del manual de PostgreSQL sobre este tema.)

De todas formas, PostgreSQL no permite el "clustering" en indices GiST de PostGIS por que los indices GiST simplemente ignoran los valores NULL, tendrás el siguiente mensaje de error:

lwgeom=# CLUSTER my_geom_index ON my_table;
ERROR: cannot cluster when index access method does not handle null values
HINT: You may be able to work around this by marking column "geom" NOT NULL.

Como sugiere el mensaje de ayuda, podemos evitar esta deficiencia añadiendo una restricción "not null" a la tabla:

lwgeom=# ALTER TABLE my_table ALTER COLUMN geom SET not null;
ALTER TABLE

Por supuesto, esto no funcionará si necesitas valores NULL en tu columna de geometrías. Adicionalmente, debes utilizar el método anterior para añadir la restricción, utilizando restricciones CHEK como "ALTER TABLE blubb ADD CHECK (geómetra is not nulo);" no funcionara.

6.3. Evitar la conversión de dimensión

A veces, sucede que tienes datos en 3D o 4D en tus tablas pero siempre, al acceder a ella utilizando funciones conformes con OpenGIS como ST_AsText () o ST_AsBinary (), sólo devuelven geometrías 2D de salida. Esto ocurre por que lo hacen llamando internamente a la función ST_Force_2d (), que introduce una sobrecarga significativa para geometrías grandes . Para evitar esta sobrecarga, puede ser factible comprobar la validez de suprimir esas dimensiones adicionales de una vez por todas:

UPDATE mytable SET geom = ST_Force2D(geom);
VACUUM FULL ANALYZE mytable;

Ten en cuenta que si las has añadido a tu columna de geometría utilizando addGeometryColumn () habrá una restricción en la dimensión de la geometría. Para pasar la restricción por alto tendrás que quitarla. Recuerda actualizar la entrada en la tabla geometry_columns y volver a crear la restricción después.

En el caso de tablas de gran tamaño, puede ser conveniente dividir este UPDATE en porciones más pequeñas, restringiendo la actualización de una parte de la tabla a través de una cláusula WHERE y su clave primaria o de otros criterios, y la ejecución de un simple "VACUUM"; entre los UPDATE. Esto reduce drásticamente la necesidad de espacio de disco temporal. Además, si has mezclado dimensiones de geometrías, que restringen el UPDATE con "WHERE dimension(the_geom)>2" salta la reescritura de geometrías que ya están en 2D.