Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Optimización de Consultas
• Trataremos de Orientarlo a Desarrolladores
• Es un tema que tiene muchos “Sub-temas”
• Algunos subtemas están fuera del scope de tareas
de un desarrollador.
• Configuración de Posgresql,
• Analyze (actualizar estadísticas)
• Vamos a ver:
• Algunas formas de monitorear queries
• Explain y Explain/Analyze
• Algunos ejemplos de queries y como los optimizamos
Optimización de Consultas
• Conceptos Generales
• Antes de ir a Postgres, veamos algo general de índices y del optimizador
• Trade off Teoría / Práctica
• Explain y Explain Analyze
• Explain = Plan de Acceso + Pronóstico
• Explain Analyze = Plan de Acceso + Pronóstico + Realidad
• Tipos de Accesos de Postgres
• Sequencial, Indexado, Múltiple Indice, Nested Loop Join, Hash Join,
Subqueries correlacionados
• Monitoreo de queries
• Psql (poco pgadminIII)
• Formas de JOIN
• JOIN Tradicional, INNER JOIN, OUTER JOIN
• Que diferencias hay ?
• Es alguno más performante ?
Optimización de Consultas - Indices
• Indices Btree+ Soportan eficientemente búsquedas por igualdad y rangos
• Where legajo = 123
• Where salario > 8000
• Son los más usados (en Postgres y otras bases)
• Existen otros tipos de índices (R-Tree,hash,Gin)
Indice Btree
Tiempo de Optimización
(eleccion del plan de Tiempo de Ejecución
acceso optimo)
Los costos son valores arbitrarios
para que cierre el ejemplo (puede
Tiempo Total del Query haber inconsistencias)
Optimizador basado en costos (2/3)
SELECT T1.A, T1.B, T2.C, T2.D Indices Disponibles:
FROM T1, T2 Ix1: clave T2.A
WHERE T1.A = T2.A and T1.E > 100 AND T2.F = ’06-04-2014’
TENEMOS DOS
INDICES A LOS
Plan
Plan 5 Optimo
PLANES ANTERIOES
C. Total: 350
SE AGREGAN
ALGUNOS MAS Filtro T2.F=‘06-04-2014’
Costo: 100
Plan 1 Plan 4
C. Total: 6800 C. Total: 1650
Junta por Nested Loop
Join T1.A =T2.A
Plan 2 Costo: 200
C. Total: 3100
Plan 3 Filtro T1.E > 100 via
C. Total: 1900 Indice T1.E
Costo:50
Tiempo de Optimización
(eleccion del plan de Tiempo de Ejecución
acceso optimo)
Los costos son valores arbitrarios
para que cierre el ejemplo (puede
Tiempo Total del Query haber inconsistencias)
Index Matching Ejemplos
Tabla negocio.sga_alumnos
Alumno legajo Propuesta Plan_version ubicacion Modalidad Division Anio_cursada readmisiones Regular calidad
Dado el query:
SELECT * FROM sga_alumnos Claves de Indices
WHERE legajo = 1111 en sga_alumnos
Legajo
AND modalidad = ‘P’
Persona
Calidad
• De los índices que existen cuales puedo usar el planner ? Modalidad
Puedo usar idx Es bueno ? Comentarios Plan version
legajo Si !! Trae 1 fila, y luego verifico el filtro Propuesta
de modalidad = ‘P’ Ubicacion
modalidad Maso Trae muchas filas y luego en cada Persona, propuesta
una verifico el filtro legajo = 1111
Dado el query:
Claves de Indices
SELECT * FROM sga_alumnos en sga_alumnos
Legajo
WHERE modalidad = ‘P’
Persona
AND anio_cursada = 2014 Calidad
Modalidad
• De los índices que existen cuales puedo usar ? Plan version
Puedo usar idx Es bueno ? Comentarios Propuesta
modalidad Maso Trae muchas filas y luego en cada una Ubicacion
verifico el filtro anio_cursada = 2014
Persona, propuesta
• Hay algún índice para crear que pueda mejorar ?
Puedo usar idx Es bueno ? Comentarios
Anio_cursada Si (no es Trae algunas filas y luego en cada
super) una verifico el filtro modalidad = P
Index Matching Ejemplos
Tabla negocio.sga_alumnos
Alumno legajo Propuesta Plan_version ubicacion Modalidad Division Anio_cursada readmisiones Regular calidad
Dado el query:
Claves de Indices
SELECT * FROM sga_alumnos en sga_alumnos
Legajo
WHERE legajo > 0
Persona
AND apellido = ‘Perez’ Calidad
Modalidad
• De los índices que existen cuales puedo usar ? Plan version
Puedo usar idx Es bueno ? Comentarios Propuesta
legajo Malo Trae todas las filas y luego en cada Ubicacion
(con >0) una verifico el filtro apellido = ‘Perez’
Persona, propuesta
Apellido mejor Trae solo los Perez
apellido
Queda para otra oportunidad ver como se activa. Naturalmente en producción debería
desactivarse.
Postgres Query Log (1/1)
Para activarlo es necesario configurar el SQL query
logging, en el postgresql.conf con el parámetro :
0 Indica loggear todos los queries.
-1 deshabilita el log.
log_min_duration_statement = 0 otro valor (ej.300) solo logea
aquellos queries cuya duración
exceda 300 milisegundos.
Pg_stat_statements (1/5)
Extension para agrupar los queries que recibe la base
Permite conocer de cada query:
• Cuántas veces se lo ejecuta,
• Cuánto tiempo acumulan todas sus ejecuciones
Se instala asi:
1- Modificar parámetro en el postgresql.conf
shared_preload_libraries = 'pg_stat_statements'
2- Bajar y subir postgres
3- En la base de datos que deseo monitorear ejecutar:
CREATE extension pg_stat_statements;
en el postgresql.conf
Monitoreo de Queries
Conclusiones
Ventajas Desventajas
Pg_stat_activity Facil de Usar Es una foto de los queries en
ejecución.No tiene los
queries que ya terminaron
LOG de postgres Podemos ver la historia, los Requiere un poco de
queries que ya terminaron, y configuración y buscar en el
su duración. log generado por postgres.
Queries rápidos que se
ejecutan muchas veces, no
los veo
Pg_stat_statement Muestra historia y queries Requiere configurar y agregar
actuales. Incluye la cantidad una extensión en la base de
de veces que se ejecutó un datos. Y ejecutar un query
query y duración total. para obtener los resultados
Puede detectar queries
relativamente rápidos que se
ejecutan muchas veces
Análisis de Queries
• Arranquemos con casos de una sola tabla para
graficar algunas ideas:
SELECT * FROM mdp_personas WHERE email_valido = 1
SELECT * FROM mdp_personas WHERE apellido = ‘Perez’
• Como ya vimos Postgres tiene 2 formas de
acceder a mdp_personas:
• Secuencial (no es bueno, pero a veces no queda otra)
• Usando un Indice (si existe alguno que le sirva)
• Existen varias formas de usar índices para acceder a tablas
• Cuando el query tiene varias tablas se combinan !!
EXPLAIN de Queries
Si anteponemos EXPLAIN al query vemos el plan de acceso
generado por el planner (solo pronósticos, no ejecuta el query)
SELECT * FROM mdp_personas WHERE email_valido = 0
Que hubiera pasado si existieran 2 índices: uno por dni y otro por codpostal ?
- 2 Posibles Planes de acceso. Cual es el menos costoso en términos de lectura ?
- Habrá alguna otra forma de acceder por índice cuando hay 2 índices?.
Ejemplo Where nombre = ‘juan’ and codpostal = ‘1661’
Plan-reading is an art that deserves an extensive tutorial, which this is not; but
here is some basic information.
Análisis de Queries
– Acceso por indice, filtrando por otra condicion
(Similar al anterior) y luego con un sort
– También lo tenemos en formato grafico en el
PGAdminIII
Bitmap Index Scan
To combine multiple indexes, the system scans each needed index and prepares a bitmap in memory giving the locations of table rows that are reported as matching that
index's conditions. The bitmaps are then ANDed and ORed together as needed by the query. Finally, the actual table rows are visited and returned. Por ejemplo: Un qury con
WHERE x = 42 OR x = 47 OR x = 53 OR x = 99 could be broken down into four separate scans of an index on x, each scan using one of the query clauses. The results of these
scans are then ORed together to produce the result.
Si tenemos 2 indexes uno con clave x y el otro con clave y . Un query con WHERE x = 5 AND y = 6 puede hacer 2 index scans en cada indice y luego combinar los resultados
mediante un AND.
Indices Funcionales
SELECT codpostal FROM LOCALIDADES
WHERE upper(nombre) = ‘MAR DEL PLATA’
QUERY PLAN
---------------------------------------------------------------------------------
Bitmap Heap Scan on rrhh_cargos (cost=851.82..64504.40 rows=48943 width=6)
(actual time=24.000..184.007 rows=46974 loops=1)
Recheck Cond: (cod_rrhh_importacion = 6656)
-> Bitmap Index Scan on rrhh_cargos_imp (cost=0.00..839.58 rows=48943
width=0) (actual time=20.000..20.000 rows=46974 loops=1)
Index Cond: (cod_rrhh_importacion = 6656)
Total runtime: 312.010 ms
QUERY PLAN
---------------------------------------------------------------------------------
Seq Scan on rrhh_cargos (cost=0.00..105526.25 rows=13646 width=6) (actual
time=360.011..2176.070 rows=46974 loops=1)
Filter: ((cod_rrhh_importacion - 2) = 6654)
Total runtime: 2204.071 ms
Indices Funcionales – Un EjemploReal
Dado este query
http://comunidad.siu.edu.ar/foro/index.php?topic=8823
Subquery Correlacionado (Caso real) 2/5
Subquery Correlacionado (Caso real) 3/5
Subquery Correlacionado (Caso real 4/5)
Subquery Correlacionado (Caso real 5/5)
COMPARANDO LOS EXPLAINS DE (USANDO www.explain.depesz.com)
SIN INDICE
CON INDICE
REVISAR SI VA !!Aplanar
subqueries(FROM-list subquery example)
El optimizador puede cambiar el query internamente
SELECT *
FROM t1, (SELECT * FROM t2 WHERE t2.x = 10) t2
WHERE t1.id = t2.id;
-- converted by the optimizer into
SELECT *
FROM t1, t2
WHERE t1.id = t2.id and t2.x = 10
Juntas
• Si tenemos una junta entre 2 tablas con una
condicion de junta T1.A = T2.B
• Es conveniente tener 2 índices: T1(A) y T2(B)
• El optimizador usará uno solo, pero tendrá más
opciones o planes para analizar
• Postgres tiene 3 técnicas o mecanismos para
resolver las JUNTAS:
• NESTED LOOP JOIN
• HASH JOIN
• SORT MERGE JOIN
• SI no hay índices, Postgres deberá usar HASH o
SORT MERGE.
Juntas
SELECT * FROM T1, T2
WHERE T1.PK = T2.FK
Tipos de Joins
Nestloop: Es el más utilizado. Primero recorre una tabla (T1) y por cada fila ,
busca en la segunda tabla (T2) (usando un Índice) las filas que cumplen
T2.FK = T1.PK
Merge: Ambas tablas son ordenadas por las columnas del JOIN (T1.PK y
T2.FK) y luego se scanean en paralelo y se detectan las filas que hacen
JOIN (No es performante, revela falta de índices)
Hash: Una de las tablas se scanea y se crea una tabla de HASH, usando las
columnas del JOIN como claves de HASH. Luego la otra tabla se escanea
y se aplica la funcion de HASH a las columnas del JOIN, con ese
resultado se busca en la tabla de HASH las filas que hacen JOIN (No es
performante, revela falta de índices)
SET enable_nestloop = off; --permite ver q se haría en otro caso
SET enable_hashjoin = off;
SET enable_mergejoin= off;
Juntas
select *
FROM PERSONAS P INNER JOIN LOCALIDAD L ON L.codpostal = P.codpostal
WHERE L.nombre = ‘localidad2341’;
select *
FROM PERSONAS P INNER JOIN LOCALIDAD L USING (codpostal)
WHERE L.nombre = ‘localidad2341’;
select *
FROM PERSONAS P NATURAL JOIN LOCALIDAD L
WHERE L.nombre = ‘localidad2341’;
• El uso del OUTER puede imponer un orden en la forma que se accede a tablas. Por ejemplo si tenemos
• SELECT * FROM A LEFT OUTER JOIN (B join C on (b.ref = c.id) ) ON (A.id = B.id)
• Tiene que resolver el primero el JOIN entre B y C y luego el JOIN entre A y B+C
http://comunidad.siu.edu.ar/foro/index.php?topic=8823
Demo 1 Subquery Correlacionado
• cd /home/demo/test/mapuche
• Ejecutamos ./q1.sh y vemos la salida
• Ejecutamos time ./q1.sh y vemos cuanto demora
• Ejecutamos ./qex1.sh y vemos que el Subplan se
resuelve con una busqueda sequencial.
• More creaidx.sh para ver el indice que vamos a crear
• Ejecutamos ./creaidx.sh y creamos el indice
• Ejecutamos ./q1.sh y vemos que trae las mismas filas
pero más rápido
• Ejecutamos time ./q1.sh y comparamos los tiempos
Demo2 Queries en el FROM (qry_dnp_mal.sh) 1 Vez por Row
Demo2 Queries en el FROM (qry_dnp_ok.sh)
1 Vez por query. (No
es correlacionado)
Demo 2 Uso de Queries en el FROM
• cd /home/demo/test/pilaga
• Ejecutamos ./qry_dnp_mal.sh y vemos la salida
• Ejecutamos time ./qry_dnp_mal.sh y vemos cuanto demora
• Ejecutamos time ./qry_dnp_ok.sh y vemos cuanto demora.
• Ejecutamos time ./qry_dnp_ok.sh > /dev/null y vemos cuanto
demora. La diferencia es el trafico de las filas
• Ejecutamos time ./qry_exp_dnp_mal.sh y vemos cuanto demora
• Ejecutamos time ./qry_exp_ana_dnp_mal.sh y vemos cuanto
demora
guarani_3_10=# \d test;
Tabla «public.test»
Columna | Tipo | Modificadores
---------+-----------------------------+---------------
nombre | character varying(30) |
nacim | timestamp without time zone |
Índices:
"testix1" btree (nacim)
guarani_3_10=# explain select nombre,nacim from test where nacim::date = '02-07-2015' limit 3 ;
QUERY PLAN
--------------------------------------------------------------
Limit (cost=0.00..12.54 rows=3 width=18)
-> Seq Scan on test (cost=0.00..209.00 rows=50 width=18)
Filter: ((nacim)::date = '2015-07-02'::date)
(3 filas)
cost -> costo en traer una pagina (constante 1.0 en seq scan)
rows -> en filas
width -> en bytes
Análisis de Consultas
Ejemplo de barrido Secuencial
(Los barridos secuenciales suelen ser ineficientes, recorren toda la tabla)
cost -> costo en traer una pagina (constante 1.0 en seq scan)
rows -> en filas
width -> en bytes
Query1.sql
Índices
En las hojas del árbol el optimizador debe decidir
cual es la mejor forma de acceder a cada tabla
Barrido Secuencial ?
Acceso Indexado ?
Qué índices me permite usar Postgres ?
CREATE [ UNIQUE ] INDEX [CONCURRENTLY] name ON table
[USING method] ({ column | ( expression )}
[WITH (storage_parameter = value [, ... ] )]
[TABLESPACE tablespace ]
[ WHERE predicate ]
Índices (7)
387 294 A
> 293
292 292
T
97
O
95
292 S
89
89
59
57
56
Query1.sql
Análisis de Consultas
Acceso Index Bitmap
Puede acceder a una tabla usando 2 índices, luego obtener la
intersección de los punteros
EXPLAIN ANALYZE select * from CENSO2012 where nacimiento=‘14-01-1995’
and codpostal = 9993;
Query3.sql
Análisis de Consultas
– Acceso Index Bitmap
• Cuando se accede a una tabla por un índice, y se espera que
muchas filas sean recuperadas, es conveniente partir el
acceso a la tabla en 2 etapas:
– Acceder primero por el índice recuperando todos los punteros a las filas.
– Ordenar dichos punteros de acuerdo a la posición física de la fila a recuperar
EXPLAIN ANALYZE select * from CENSO2012 where nacimiento=‘14-01-1995’;
Query3.sql
EXPLAIN Vs EXPLAIN ANALYZE
El mismo query ejecutado con EXPLAIN y con EXPLAIN ANALYZE
Costo en unidades teóricas Tiempo de ejecución en mseg
Análisis de Consultas
SELECT * FROM T1, T2
WHERE T1.PK = T2.FK
Tipos de Joins
Nestloop: Es el más utilizado. Primero recorre una tabla (T1) y por cada fila ,
busca en la segunda tabla (T2) (usando un Índice) las filas que cumplen
T2.FK = T1.PK
Merge: Ambas tablas son ordenadas por las columnas del JOIN (T1.PK y
T2.FK) y luego se scanean en paralelo y se detectan las filas que hacen
JOIN (No es performante, revela falta de índices)
Hash: Una de las tablas se scanea y se crea una tabla de HASH, usando las
columnas del JOIN como claves de HASH. Luego la otra tabla se escanea
y se aplica la funcion de HASH a las columnas del JOIN, con ese
resultado se busca en la tabla de HASH las filas que hacen JOIN (No es
performante, revela falta de índices)
SET enable_nestloop = off; --permite ver q se haría en otro caso
SET enable_hashjoin = off;
SET enable_mergejoin= off;
Análisis de Consultas
Nested Loop Join
Notar que si por algún motivo tenemos que buscar por dni_propietario en vehículos,
no tenemos un índice
Control de la Integridad Referencial
Los INSERT en vehículo tienen que verificar si
Existe el DNI en propietario, para lo cual tienen un
índice (la PK)
Los DELETES en propietario tienen que verificar si
Existe alguna referencia a la fila a borrar en
vehículo, para lo cual NO hay un índice
Los DELETES en propietario se pueden volver
muuuy lentos, si tenemos muchas filas en
vehículos.
Control de la Integridad Referencial
EJEMPLO: Un DELETE que borra 5000 filas tarda 8 minutos,
porque tiene que validar la FK y no tiene índice.
Control de la Integridad Referencial
Alternativas para acelerar que los DELETES en
propietario
Le ponemos un índice a vehículos
Desactivamos las FKs durante el DELETE
Varias FORMAS de desactivar las constraints:
PONER LA EN MODO DEFERED (difiere el control al momento del
commit)
DROPEAR y RECREAR CONSTRAINT (controla la FK al recrearla)
DESHABILITAR LOS TRIGGERS (no se controla la FK mientras se
deshabilitan)
alter table propietario disable trigger all
antes de 8.1 hacer un:
update pg_class set reltriggers=0 where relname = ‘propietario';
Control de la Integridad Referencial
Si desactivamos los trigggers antes, el DELETE se ejecuta en
0.5 segs. Pero no se controla la FK durante el DELETE
Control de la Integridad Referencial
Borrando y re-creando la
FK. EL Delete tarda 1.5
seg. Al recrear la FK
controla que los datos
existentes la cumplan. La
tabla con la FK tiene
500.000 filas
Window Functions
Son funciones similares a las funciones de agregación. Pero tienen la
ventaja de que NO obligan a agrupar filas (mediante group by) en una
sola fila. Así cada fila es independiente de las demas, aunque la window
function claramente accederá a otras filas aparte de la row corriente para
hacer sus cálculos
En el ejemplo vemos que tenemos el promedio de salario por depto, junto
con los datos de cada empleado. Con un group by no podriamos
obtenerlo ya que deberiamos agrupar por “deptname” y no podriamos
traer empno y salary
Window Functions
La diferencia con las funciones de agrupación es que siempre la “window
funtion” va acompañada de un OVER, el cual indica o define cual será la
ventana (como se agrupan las filas del query).
La cláusula PARTITION BY EXPRESION agrupa las filas que tienen el
mismo valor en expresión. Para cada fila del SELECT se calcula la
window function usando todas las filas que están en el mismo grupo que
la fila corriente
Window Functions
• Window functions are permitted only in the SELECT list and the
ORDER BY clause of the query. They are forbidden elsewhere, such
as in GROUP BY, HAVING and WHERE clauses. This is because
they logically execute after the processing of those clauses.
Análisis de Consultas (3)
Índices (10)
Hash
Implementación de las dispersiones (Hashing) lineales de
Litwin
Es el indicado cuando la clave del índice no se repite y la
búsqueda es por un valor único
En diferentes foros indican que los índices Hash son
claramente deficientes frente a B-tree (en PostgreSQL)
=
GiST y GIN
Son usadas en sistemas de información geográfica.
Es una forma mas generalizada de los R-Tree
Índices en Order By (8)
R-tree
Índices (11)
Índices (2)
- Si falla…
Índices incompletos e inutilizables.
Puede pasar de que quede la constraint UNIQUE
Multiples Índices
• given an index on (a, b) a query condition like WHERE a = 5 AND b = 6 could use the index, but a query like WHERE a = 5 OR b = 6 could
not directly use the index.
• Fortunately, PostgreSQL has the ability to combine multiple indexes The system can form AND and OR conditions across several index
scans.
• For example, a query like WHERE x = 42 OR x = 47 OR x = 53 OR x = 99 could be broken down into four separate scans of an index on x,
each scan using one of the query clauses. The results of these scans are then ORed together to produce the result.
• Another example is that if we have separate indexes on x and y, one possible implementation of a query like WHERE x = 5 AND y = 6 is
to use each index with the appropriate query clause and then AND together the index results to identify the result rows.
• To combine multiple indexes, the system scans each needed index and prepares a bitmap in memory giving the locations of table rows
that are reported as matching that index's conditions. The bitmaps are then ANDed and ORed together as needed by the query. Finally,
the actual table rows are visited and returned.
• The table rows are visited in physical order, because that is how the bitmap is laid out; this means that any ordering of the original
indexes is lost, and so a separate sort step will be needed if the query has an ORDER BY clause. For this reason, and because each
additional index scan adds extra time, the planner will sometimes choose to use a simple index scan even though additional indexes are
available that could have been used as well.
• In all but the simplest applications, there are various combinations of indexes that might be useful, and the database developer must
make trade-offs to decide which indexes to provide. Sometimes multicolumn indexes are best, but sometimes it's better to create
separate indexes and rely on the index-combination feature. For example, if your workload includes a mix of queries that sometimes
involve only column x, sometimes only column y, and sometimes both columns, you might choose to create two separate indexes on x
and y, relying on index combination to process the queries that use both columns. You could also create a multicolumn index on (x, y).
This index would typically be more efficient than index combination for queries involving both columns, but as discussed in Section
11.3, it would be almost useless for queries involving only y, so it should not be the only index. A combination of the multicolumn index
and a separate index on y would serve reasonably well. For queries involving only x, the multicolumn index could be used, though it
would be larger and hence slower than an index on x alone. The last alternative is to create all three indexes, but this is probably only
reasonable if the table is searched much more often than it is updated and all three types of query are common. If one of the types of
query is much less common than the others, you'd probably settle for creating just the two indexes that best match the common types.
Esquemas de las tablas del ejemplo