Está en la página 1de 89

Optimización de Consultas

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

Legajo Nombre Materia salario

Cómo sería un índice cuya clave


admite duplicados ?
Podemos tener varios índices

HAY QUE ELEGIR UNO. Cómo lo elegimos ?


Conceptos Generales a todos los RDBMS
• El optimizador (planner)
• Input: Query, Estadísticas
• Output: Plan de Acceso (“Supuestamente” el Optimo)
• El query se ejecuta de acuerdo al Plan de Acceso, su tiempo de
ejecución varía dependiendo de haber elegido un buen plan

• El optimizador funciona como una


caja negra, No podemos
configurarlo. Sin embargo:
• Algunos motores soportan Directivas
(Informix SI, Postgres NO)
• Existen algunos parámetros que
afectan su funcionamiento
Tiempo de Optimización
(eleccion del plan de Tiempo de Ejecución
acceso optimo)

Tiempo Total del Query


Optimizador basado en costos (1/3)
SELECT T1.A, T1.B, T2.C, T2.D Indices Disponibles:
FROM T1, T2 Ninguno
WHERE T1.A = T2.A and T1.E > 100 AND T2.F = ’06-04-2014’

Plan 1 Plan 2 Plan


C. Total: 6800 C. Total: 3100 Optimo
Filtro de T1.E > 100 and Junta por Hash join
T2.F=‘06-04-2014’ T1.A =T2.A
Costo: 300 Costo: 1000

Junta por Hash join Filtro de T1.E


> 100 Filtro T2.F=‘06-04-2014’
T1.A =T2.A
Costo: 400 Costo: 200
Costo: 5000

Acceso Acceso Acceso Acceso


Seq T1 Seq T2 Seq T1 Seq T2
Costo:1000 Costo:500 Costo:1000 Costo:500

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 UN INDICE Plan 3 Plan 4 Plan


A LOS PLANES C. Total: 1900 C. Total: 1650 Optimo
ANTERIORES SE Filtro T2.F=‘06-04-2014’
AGREGAN ALGUNOS Filtro de T1.E > 100 and Costo: 50
MAS T2.F=‘06-04-2014’
Costo: 300 Junta por Nested Loop
join T1.A =T2.A
Plan 1 Costo: 200
Junta por Nested Loop
C. Total: 6800 Join T1.A =T2.A Filtro de T1.E
Costo: 600 > 100
Plan 2 Costo: 400
C. Total: 3100
Acceso
Seq T1
Acceso
Costo:1000
Seq T1
Costo:1000
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 (3/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’ Ix2: clave T1.E

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

• Hay algún índice para crear que pueda mejorar ? NO


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 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

• Hay algún índice para crear que pueda mejorar ? NO


Indices Redundantes
Daad la Tabla T1 (A, B, C, D, E, F)

INDICE compuesto con clave (A, B, C)


INDICE con clave (A)
INDICE con clave (B)
INDICE con clave (C )
Monitoreo de Queries
• Pg_stat_activity
• Foto de los queries en ejecución en un instante dado
• Pg_stat_statement
• Podemos tener un poco de historia de queries
ejecutados y agruparlos por query, junto con algunas
estadísticas
• Log de Postgres
• Podemos pedir a postgres que logee queries que
demoren mas de x miliseg.
Pg_stat_activity (1/1)
La tabla pg_stat_activity nos muestra los queries
actualmente en ejecución.
Se puede calcular el tiempo que lleva corriendo !

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;

Las estadísticas recolectadas por este módulo estarán en


la vista pg_stat_statements. Esta vista contiene una fila
por cada query distinto, database ID, y user ID
Pg_stat_statements (2/5)
SELECT left(query, 50),
round(sum(total_time/1000)::numeric, 6) as sum,
round((sum(total_time/1000)/sum(calls))::numeric, 6) as average,
sum(calls) as cuantas,
round(100.0*(sum(total_time/tot))::numeric, 2) as percent
FROM (select sum(total_time) as "tot "
from pg_stat_statements) asx, pg_stat_statements
group by query
order by sum(total_time) desc limit 10;
Pg_stat_statements (3/5)
TABLA pg_stat_statements
Pg_stat_statements (4/5)
Pg_stat_statements (5/5)

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

SELECT * FROM mdp_personas WHERE apellido = ‘Servat’


EXPLAIN ANALIZE de Queries
Si usamos EXPLAIN ANALYZE vemos el plan de acceso (solo
pronósticos ) + algunas métricas de la ejecución (ejecuta el query)
SELECT * FROM mdp_personas WHERE email_valido = 0
SELECT * FROM mdp_personas WHERE apellido = ‘Servat’
Análisis de Queries
Ejemplo de Acceso Indexado + Filtro por otra Columna
EXPLAIN select * from CENSO2012
where DNI_TITULAR = 14345550
AND CODPOSTAL = 9993;

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

Aquí el optimizador decidió usar un plan de 2 pasos: El plan interno, utiliza el


índice para encontrar la ubicación de las filas que matchean con la condición
(unique1 < 100), como son varias filas, arma un plan externo que se encarga de
hacer el fetch de las filas.
La razón para hacerlo en 2 pasos es que en el plan externo las filas a acceder se re-
ordenan de acuerdo a su posición física en disco antes de leerse, para asi
minimizar el costo de hacer fetchs separados. The "bitmap" mentioned in the
node names is the mechanism that does the sorting.
Múltiples Indices
EXPLAIN select * from CENSO2012
where APELLIDO = ‘PEREZ’
OR CODPOSTAL = 9993;
• Si Tenemos un índice (apellido, codpostal) No nos sirve ya que tendremos
que leer secuencialmente toda la tabla para verificar CODPOSTAL=9993
• Si tenemos 2 indices: ix1(apellido) y ix2(codpostal) PostgreSQL tiene la
capacidad de hacer múltiples index scans a una misma tabla, guardar sus
resultados y luego combinarlos (mediante AND o OR )

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’

 Si tuvieramos este índice no lo usaría:


CREATE INDEX nosirve ON LOCALIDADES (nombre);

 En cambio este índice si va a ser usado


CREATE INDEX sirve ON LOCALIDADES (upper(nombre));
Indices Funcionales
– Cualquier operación que le hagamos a una columna indexada
impide que se use el índice
– En general si tenemos una expresión en el WHERE que aparece
con frecuencia deberíamos considerar un índice funcional
explain analyze select numero_cargo from rrhh_cargos where cod_rrhh_importacion = 6656;
explain analyze select numero_cargo from rrhh_cargos where cod_rrhh_importacion - 2 = 6654 ;

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

 Si tuvieramos este índice no lo usaría:


CREATE INDEX idx_documento_cargo ON rrhh_cargos USING btree
(cod_rrhh_importacion, cuil_cuit);

 En cambio este índice si va a ser usado


CREATE INDEX idx_documento_cargo ON rrhh_cargos USING btree
(cod_rrhh_importacion, "substring"((cuil_cuit)::text, 3, 8));
Optimizacion con Subqueries
• Evitar Queries correlacionados (si es posible)
• Si no es posible evitarlo, revisar si le falta algun índice (vamos a ver un
ejemplo)
• Subqueries en el from no tienen índices
• A veces es más conveniente agregar un subquery en el FROM.
• Otras veces puede ser necesario crear una temporal previamente , inclusive se
puede crear índices a las temporales
• En lo posible Aplanar subqueries (usando joins en lugar de subqueries,
aunque no siempre es posible)
Optimizacion con Subqueries
Un subquery “NO Correlacionado” no referencia ninguna columna del query
externo y por lo tanto tendrá un resultado constante. El SubQuery se
SELECT FROM sga_alumnos Ejecuta 1 vez

WHERE persona IN (SELECT persona FROM mdp_personas


WHERE apellido = ‘Perez’);

Un subquery “Correlacionado” hace referencia a alguna columna del query


externo, y por lo tanto se debe ejecutar una vez por cada fila candidata del
query externo.
El SubQuery se Ejecuta
N veces. 1 vez por cada
row en Materia
SELECT nombre FROM materias mat candidata al resultado

WHERE 0 = (selec count(*) FROM cursa


WHERE cursa.materia_id = mat.materia_id)
Subquery Correlacionado (Caso real 1/5) El SubQuery se
Ejecuta 1 vez por
cada fila del query
externo

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

EXPLAIN ANALYZE select * from CENSO2012 C, Localidades L


where L.codpostal = C.codpostal
and L.nombre = ‘localidad2341’;
Juntas
 Hash Join
 (ejecutamos el mismo query pero sin índices)
EXPLAIN ANALYZE select * from CENSO2012 C, Localidades L
where L.codpostal = C.codpostal
and L.nombre = ‘localidad2341’;
JOINS - INNER Vs JOIN Tradicional
select *
FROM PERSONAS P, LOCALIDAD L
WHERE L.codpostal = P.codpostal
and L.nombre = ‘localidad2341’;

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’;

• Estos Joins son todos equivalentes.


• El ON es para especificar la condición de JOIN
• El USING es para indicar las Columnas de condición de JOIN, si tienen el mismo nombre en ambas tablas
• El NATURAL indica que la junta es por todas las columnas de igual nombre en ambas tablas
• EL ON / USING / NATURAL se pueden usar en INNER o en OUTER joins
Outer Vs Inner Join
• INNER = Equivalente al Join standard . Todas las tuplas del
resultado se forman mediante 2 partes que se juntan.
• OUTER = Una de las tablas puede no tener una tupla “contraparte”

• 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

• SELECT * FROM A, B, C WHERE A.id = B.id AND B.ref = C.id


• Tiene que evaluar todos los posibles ordenes: A,B,C o A,C,B o B,C,A o …. (6 ordenes posibles)

En este tutorial : http://www.postgresql.org/docs/9.3/static/tutorial-join.html


no hay ninguna mencion a que el INNER JOIN es mas performante q el JOIN tradicional usando el WHERE
Demo 1 Subquery Correlacionado El SubQuery se
Ejecuta 1 vez por
cada fila del query
externo

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 from test where nacim = '02-07-2015';


QUERY PLAN
----------------------------------------------------------------------------
Index Scan using testix1 on test (cost=0.29..4.30 rows=1 width=10)
Index Cond: (nacim = '2015-07-02 00:00:00'::timestamp without time zone)
(2 filas)

guarani_3_10=# select nombre from test where nacim = '02-07-2015' ;


nombre
--------
(0 filas)

guarani_3_10=# select nombre,nacim from test where nacim::date = '02-07-2015' limit 3 ;


nombre | nacim
---------+----------------------------
nombre1 | 2015-07-02 10:46:41.692558
nombre2 | 2015-07-02 10:46:41.692558
nombre3 | 2015-07-02 10:46:41.692558
(3 filas)

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)

guarani_3_10=# explain select nombre,nacim from test where nacim::date = '02-07-2015' ;


QUERY PLAN
--------------------------------------------------------
Seq Scan on test (cost=0.00..209.00 rows=50 width=18)
Filter: ((nacim)::date = '2015-07-02'::date)
(2 filas)
Gracias !!
Análisis de Consultas

EXPLAIN [ ANALYZE ] statement


 Ejemplo

EXPLAIN ANALYZE select * from dh01 where tipo_sexo='M';

Seq Scan on dh01 (cost=0.00..242.00 rows=4285 width=200)


(actual time=0.033..18.091 rows=4336 loops=1)
Filter: (tipo_sexo = 'M'::bpchar)
Total runtime: 29.143 ms

 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)

EXPLAIN select * from CENSO2012 where DNI_TITULAR = 10499752;

 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)

CREATE [ UNIQUE ] INDEX [CONCURRENTLY] name ON table


[USING method] ({ column | ( expression )}
[WITH (storage_parameter = value [, ... ] )]
[TABLESPACE tablespace ]
[ WHERE predicate ]

 Métodos posibles para el armado del índice


 B-tree
 R-tree
 Hash
Indices GiST y GIN orientados para Full Text Search.
 GiST R-tree: two-dimensional spatial data
 GIN Hash: solo para = searchs / No WAL logged
Indices – Btree (árbol B+)
401

• Estructuras de datos 394


393

– Arbol B+ > 387


D

387 294 A
> 293

292 292
T
97
O
95

292 S
89
89
59
57
56

Nivel 0 Nivel 1 Nivel 2


Índices B-tree
 Es el tipo de índice que utiliza PostgreSQL por defecto.
 Es el utilizado por las PRIMARY KEY (claves primarias) en su creación.
 Es el único tipo de índice que soporta la clausula UNIQUE

 Usado en búsquedas por = y por < , <= , > , = >


 Implementación de arboles B de alta concurrencia (Lehman – Yao)
 Maneja solamente búsquedas por rango de una dimensión.
 Es el único (junto con GiST) que soporta indexado por
múltiples columnas.
Análisis de Consultas
 Ejemplo de Acceso Indexado
 Creamos un índice por DNI_TITULAR

EXPLAIN select * from CENSO2012 where DNI_TITULAR = 10499752;

 cost -> costo de i/o + costo de procesamiento


 rows -> en filas
 width -> en bytes

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

EXPLAIN ANALYZE select * from CENSO2012 C, Localidades L


where L.codpostal = C.codpostal
and L.nombre = ‘localidad2341’;
Análisis de Consultas
 Hash Join
 (ejecutamos el mismo query pero sin índices)
EXPLAIN ANALYZE select * from CENSO2012 C, Localidades L
where L.codpostal = C.codpostal
and L.nombre = ‘localidad2341’;
Creación de FKs
 Deben REFERENCIAR a una tabla/columna(s) que
sean PK o UNIQUE
 La restricción del punto anterior nos asegura que
en la tabla REFERENCIADA habrá un índice
CREATE TABLE propietario (
dni integer ,
apellido char(20)
);
alter table propietario add constraint prop_pk primary key(DNI);

CREATE TABLE vehiculos(


patente char(6),
marca smallint,
modelo smallint,
color smallint,
dni_propietario integer
);
alter table vehiculos add constraint vehic_pk primary key(patente);
alter table vehiculos add constraint prop_fk foreign key (dni_propietario) references propietario(dni);
Control de la Integridad Referencial

 Se realiza mediante triggers


 Son creados automáticamente al definir las FKs

CREATE TABLE propietario (


dni integer ,
apellido char(20)
);
alter table propietario add constraint prop_pk primary key(DNI); Tenemos
Indices Aquí
CREATE TABLE vehiculos(
patente char(6),
marca smallint,
modelo smallint,
color smallint,
dni_propietario integer
);
alter table vehiculos add constraint vehic_pk primary key(patente);
alter table vehiculos add constraint prop_fk foreign key (dni_propietario) references propietario(dni);

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)

EXPLAIN ANALYZE select * from dh01 where tipo_sexo='M';

Vista grafica de la consulta (F7 en PgAdmin III)

EXPLAIN ANALYZE select * from dh01, dh03


where dh01.nro_legaj = dh03.nro_legaj;

- Nested Loop Join


- Merge Sort Join
- Bitmap And
- Hash Join
- Sort
Estadísticas

 PostgreSQL trae un recolector de estadísticas sobre el


uso de la base de datos.

 Las estadísticas que se recolectan se configuran en


postgresql.conf, apartado “RUNTIME STATISTICS”

 Muy útil para tener una idea rápida y sencilla sobre lo


que sucede en cada tabla

 Ver Tablas del catalogo pg_stat_*

select pg_stat_get_tuples_updated(1530410); -- dh21_original


SIU – Capacitación Interna

Í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)

• 11.4. Indexes and ORDER BY


• In addition to simply finding the rows to be returned by a query, an index may be able to deliver them in a specific
sorted order. This allows a query's ORDER BY specification to be honored without a separate sorting step. Of the
index types currently supported by PostgreSQL, only B-tree can produce sorted output — the other index types
return matching rows in an unspecified, implementation-dependent order.
• The planner will consider satisfying an ORDER BY specification either by scanning an available index that matches
the specification, or by scanning the table in physical order and doing an explicit sort. For a query that requires
scanning a large fraction of the table, an explicit sort is likely to be faster than using an index because it requires
less disk I/O due to following a sequential access pattern. Indexes are more useful when only a few rows need be
fetched. An important special case is ORDER BY in combination with LIMIT n: an explicit sort will have to process
all the data to identify the first n rows, but if there is an index matching the ORDER BY, the first n rows can be
retrieved directly, without scanning the remainder at all.
• By default, B-tree indexes store their entries in ascending order with nulls last. This means that a forward scan of
an index on column x produces output satisfying ORDER BY x (or more verbosely, ORDER BY x ASC NULLS LAST).
The index can also be scanned backward, producing output satisfying ORDER BY x DESC (or more verbosely,
ORDER BY x DESC NULLS FIRST, since NULLS FIRST is the default for ORDER BY DESC).
• You can adjust the ordering of a B-tree index by including the options ASC, DESC, NULLS FIRST, and/or NULLS LAST
when creating the index; for example:
• CREATE INDEX test2_info_nulls_low ON test2 (info NULLS FIRST); CREATE INDEX test3_desc_index ON test3 (id
DESC NULLS LAST);
Índices (9)

 R-tree

 Implementación del algoritmo de partición cuadrática


de Guttman
 Los R-tree pueden manejar datos multidimensionales

 Ejemplo: atributo del tipo point (x,y)


Optimización de Consultas
• Indices
• Tipos de Indices
Índices (6)

CREATE [ UNIQUE ] INDEX [CONCURRENTLY] name ON table


[USING method] ({ column | ( expression )}
[WITH (storage_parameter = value [, ... ] )]
[TABLESPACE tablespace ]
[ WHERE predicate ]
 Storage Parameter – Fillfactor
 Similar interpretación al Fillfactor de las tablas
Objet Min Max
Table 10 100
Index Btree 10 90
Index Hash 10 75 Btree, fillfactor por defecto 90%
Index GiST 10 90
Index GIN 10 100
SIU – Capacitación Interna

Índices (11)

CREATE [ UNIQUE ] INDEX [CONCURRENTLY] name ON table


[USING method] ({ column | ( expression )}
[WITH (storage_parameter = value [, ... ] )]
[TABLESPACE tablespace ]
[ WHERE predicate ]

 Permite armar un índice en una ubicación


(tablespace) especifica.
CREATE INDEX peliculas_genero_ix ON peliculas(genero)
TABLESPACE new_tabspace;
SIU – Capacitación Interna

Índices (2)

CREATE [ UNIQUE ] INDEX [CONCURRENTLY] name ON table


[USING method] ({ column | ( expression )}
[WITH (storage_parameter = value [, ... ] )]
[TABLESPACE tablespace ]
[ WHERE predicate ]

 + Permite la actualización de la tabla mientras lo


crea

 - 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

También podría gustarte