Está en la página 1de 156

Optimización del rendimiento con MySQL ( deCharlas Mayo 2012)

Vicente Víctor Jiménez Cerrada <vjimenez@warp.es>


@capitangolo

Bienvenidos a "Optimización del rendimiento con MySQL".


Soy Víctor Jiménez y seré vuestro ponente para hoy.
Trabajo en Warp Networks S.L. http://www.warp.es
Donde nos dedicamos a varias cosas, entre ellas, formación y consultoría
MySQL
Trabajo en Warp Networks S.L. http://www.warp.es
Donde nos dedicamos a varias cosas, entre ellas, formación y consultoría
MySQL
Agenda

5" 1- Introducción. ¿Por qué optimizar?

20"
2- Arquitectura de MySQL

3- Optimización de consultas
30"

4- Ejercicios
40"

5- Ruegos y Preguntas

1 - Introducción ¿Por Qué
optimizar?

2- Arquitectura de MySQL
3- Optimización de consultas
4- Ejercicios
5- Ruegos y Preguntas
1 - Introducción ¿Por Qué optimizar?

Desarrollamos nuestras apps web sin importarnos mucho el rendimiento.


Cuando nuestra web tiene éxito y se convierte en la gallina de los huevos
de oro, tenemos más solicitudes, y si no tenemos cuidado…
1 - Introducción ¿Por Qué optimizar?

… nuestro servidor se cuelga.


A esto se le llama morir de éxito.
1 - Introducción ¿Por Qué optimizar?

Pasos para no morir de éxito


Optimizar la aplicación
Optimizar la base de datos
Escalar
1 - Introducción ¿Por Qué optimizar?

Optimización
1 - Introducción ¿Por Qué optimizar?

Optimización
Hacer más con lo mismo
1 - Introducción ¿Por Qué optimizar?

Optimización
Hacer más con lo mismo
Objetivo: Aumentar Consultas / segundo
1 - Introducción ¿Por Qué optimizar?

Optimización
Hacer más con lo mismo
Objetivo: Aumentar Consultas / segundo
¿Cómo?: Menor tiempo de ejecución
1 - Introducción ¿Por Qué optimizar?

0s 1s 2s 3s 4s 5s 6s 7s 8s 9s

Tenemos una consulta que tarda 2,5 segundos en ejecutarse si se ejecuta


sola.
Pero lo normal es que esa consulta se ejecute en varios procesos en
paralelo.
1 - Introducción ¿Por Qué optimizar?

0s 1s 2s 3s 4s 5s 6s 7s 8s 9s

Tenemos una consulta que tarda 2,5 segundos en ejecutarse si se ejecuta


sola.
Pero lo normal es que esa consulta se ejecute en varios procesos en
paralelo.
1 - Introducción ¿Por Qué optimizar?

0s 1s 2s 3s 4s 5s 6s 7s 8s 9s

Dado que la máquina tiene más recursos ocupados, es posible que tarde
más en ejecutarse.
En este caso, se ha ejecutado la consulta cinco veces en paralelo, cada
una un segundo más tarde que la anterior.
1 - Introducción ¿Por Qué optimizar?

0s 1s 2s 3s 4s 5s 6s 7s 8s 9s

Dado que la máquina tiene más recursos ocupados, es posible que tarde
más en ejecutarse.
En este caso, se ha ejecutado la consulta cinco veces en paralelo, cada
una un segundo más tarde que la anterior.
1 - Introducción ¿Por Qué optimizar?

C=5

0s 1s 2s 3s 4s 5s 6s 7s 8s 9s

En un momento se están ejecutando 5 consultas a la vez.


El servidor necesita poder soportar estos picos.
1 - Introducción ¿Por Qué optimizar?

C max = 1

0s 1s 2s 3s 4s 5s 6s 7s 8s 9s

Si optimizamos esas consultas para que tarden medio segundo...


1 - Introducción ¿Por Qué optimizar?

C max = 1

0s 1s 2s 3s 4s 5s 6s 7s 8s 9s

... sólo se ejecuta una consulta cada vez.


Reduciendo el nivel de concurrencia que tiene que soportar el servidor.
1 - Introducción ¿Por Qué optimizar?

0s 1s 2s 3s 4s 5s 6s 7s 8s 9s

Dejando muchos más recursos libres.


1 - Introducción ¿Por Qué optimizar?

Más información

http://www.slideshare.net/capitangolo/no-mueras-de-exito

Dejando muchos más recursos libres.


1- Introducción. ¿Por qué optimizar?

2 - Arquitectura MySQL

3- Optimización de consultas
4- Ejercicios
5- Ruegos y Preguntas
2 - Arquitectura MySQL

mysqld mysql
mysqld-nt Workbench
PHP My Admin

MySQL tiene una arquitectura cliente servidor.


Aunque hay algunos programas 'ninjas' que acceden directamente a los
datos.
2 - Arquitectura MySQL

mysqld mysql
mysqld-nt Workbench
PHP My Admin

myisamchk
myisampack

MySQL tiene una arquitectura cliente servidor.


Aunque hay algunos programas 'ninjas' que acceden directamente a los
datos.
2 - Arquitectura MySQL

/usr/local/mysql/data

test

table.frm

world

City.frm

Country.frm

CountryLanguage.frm

Hostname.pid

Hostname.err

En disco guarda tablas, logs y archivos de estado.


2 - Arquitectura MySQL

Uso de Memoria

Thread Cache
Buffers y Cachés
Tablas en memoria
Tablas temporales
Buffers de cliente
2 - Arquitectura MySQL

Uso de Memoria

Por Instancia
Reservado en el arranque del servidor
Compartido para todos los usuarios
Query Cache
Key Cache
InnoDB Buffer Pool
Por Sesión
Reservado por cada conexión
Principalmente para gestionar los resultados
sort_buffer
join_buffer
read_buffer

Hay que tener cuidado al configurar las variables de sesión.


20MB de sort_buffer x 100 conexiones = 2GB de memoria
2 - Arquitectura MySQL

API C Subsistemas

Intérprete Funciones base


Query
Hilos
Optimizador
Cache
Buffers y cachés
Executador
Red
Motores Logs
MyISAM InnoDB Acceso y Permisos
Memory CSV
2 - Arquitectura MySQL

Motores de Almacenamiento

Gestionan la persistencia y recuperación de los datos


Configuración a nivel de Tabla
Oficiales:
MyISAM Motor por defecto en MySQL 5.0
InnoDB Motor por defecto en MySQL 5.5
Memory
Archive
Blackhole
CSV

De terceros:
solidDB Nitro
InfoBrigth PBXT
2 - Arquitectura MySQL
2.1 - MyISAM
2 - Arquitectura MySQL » MyISAM

Características de MyISAM (I)

/usr/local/mysql

world

City.frm

City.MYD

City.MYI

MyISAM guarda la información en dos archivos


.MYD(ata) y .MYI(ndex)
2 - Arquitectura MySQL » MyISAM

Características de MyISAM (II)

No soporta transacciones
Bloqueos a nivel de tabla
Para un backup binario, copiar:
.frm
.MYD
.MYI
Un backup binario es portable
2 - Arquitectura MySQL » MyISAM

Características de MyISAM (III)

Compresión de índices
prefijos en índices de tipo texto
Fulltext
ALTER TABLE table ADD FULLTEXT(column1, column2)
SELECT […] WHERE MATCH (column1, column2) AGAINST ('TEXT');
Concurrent inserts
concurrent_insert = 0 | 1 | 2
R-Tree index
Datos Geoposicionados (GIS)
2 - Arquitectura MySQL » MyISAM

Bloqueos MyISAM (I)

Bloqueo a nivel de tabla


Problemático cuando hay concurrencia
2 - Arquitectura MySQL » MyISAM

Bloqueos MyISAM (II) - Consultas lentas

0s 1s 2s 3s 4s 5s 6s 7s 8s 9s

Los select obtienen bloqueo de lectura compartido.


El insert solicita un bloqueo exclusivo de escritura.
Todos los demás selects posteriores esperan a que el insert libere el
bloqueo.
Hasta que el primer select termina, no se terminan de ejecutar las demás
consultas.
¡¡¡LLegamos a tener concurrencia 7!!!
2 - Arquitectura MySQL » MyISAM

Bloqueos MyISAM (II) - Consultas lentas

SELECT x FROM tabla …

SELECT x FROM tabla

INSERT INTO tabla

0s 1s 2s 3s 4s 5s 6s 7s 8s 9s

Los select obtienen bloqueo de lectura compartido.


El insert solicita un bloqueo exclusivo de escritura.
Todos los demás selects posteriores esperan a que el insert libere el
bloqueo.
Hasta que el primer select termina, no se terminan de ejecutar las demás
consultas.
¡¡¡LLegamos a tener concurrencia 7!!!
2 - Arquitectura MySQL » MyISAM

Bloqueos MyISAM (II) - Consultas lentas

SELECT x FROM tabla …

SELECT x FROM tabla

INSERT INTO tabla

SELECT x FROM tabla …

SELECT x FROM tabla …

SELECT x FROM tabla …

SELECT x FROM …

SELECT …

0s 1s 2s 3s 4s 5s 6s 7s 8s 9s

Los select obtienen bloqueo de lectura compartido.


El insert solicita un bloqueo exclusivo de escritura.
Todos los demás selects posteriores esperan a que el insert libere el
bloqueo.
Hasta que el primer select termina, no se terminan de ejecutar las demás
consultas.
¡¡¡LLegamos a tener concurrencia 7!!!
2 - Arquitectura MySQL » MyISAM

Bloqueos MyISAM (II) - Consultas lentas

SELECT x FROM tabla …

SELECT x FROM tabla

INSERT INTO tabla

SELECT x FROM tabla …

SELECT x FROM tabla …

SELECT x FROM tabla …

SELECT x FROM …

SELECT …

0s 1s 2s 3s 4s 5s 6s 7s 8s 9s

Los select obtienen bloqueo de lectura compartido.


El insert solicita un bloqueo exclusivo de escritura.
Todos los demás selects posteriores esperan a que el insert libere el
bloqueo.
Hasta que el primer select termina, no se terminan de ejecutar las demás
consultas.
¡¡¡LLegamos a tener concurrencia 7!!!
2 - Arquitectura MySQL » MyISAM

Bloqueos MyISAM (II) - Consultas lentas

SELECT …

SELECT …

INSERT…

SELECT …

SELECT …

SELECT …

SELECT …

SELECT …

0s 1s 2s 3s 4s 5s 6s 7s 8s 9s

Si optimizamos el primer select, los bloqueos bajan exponencialmente.


Concurrencia 4
2 - Arquitectura MySQL » MyISAM

Key Cache

Caché para índices MyISAM

key_buffer_size > 0

En MyISAM existe una caché de claves


2 - Arquitectura MySQL
2.2 - InnoDB
2 - Arquitectura MySQL » InnoDB

Características de InnoDB (I): Almacenamiento

/usr/local/mysql

test

table.frm

ibdata1

ib_logfile0

ib_logfile1

InnoDB guarda toda la información de todas las tablas en el tablespace.


Archivo ibdata
2 - Arquitectura MySQL » InnoDB

Características de InnoDB (II): Files per table

innodb_file_per_table
/usr/local/mysql

test

table.frm

table.ibd

ibdata1

ib_logfile0

ib_logfile1

Si configuramos innodb_file_per_table tenemos un sub-espacio de tabla


por cada tabla
Archivo .ibd
El espacio de tabla sigue conteniendo información de cada tabla, es
necesario.
2 - Arquitectura MySQL » InnoDB

Características de InnoDB (III)

Transacciones full ACID


Bloqueo a nivel de fila
El bloqueo se realiza en la PK
Buscando rangos, se bloquean también los huecos
Bloqueo a nivel de tabla
Cachea tanto índices como datos
2 - Arquitectura MySQL » InnoDB

InnoDB Buffers (I)

Buffer Pool
MYSQL SERVER
Caché de datos e índices
Log Buffer
Log de transacciones
logfiles
BUFFER
Log de transacciones LOG BUFFER
POOL
Redo log
ibdata commit checkpoints
& checkpoints
Diccionario de datos
Undo log

ib_logfiles ibdata
2 - Arquitectura MySQL » InnoDB

InnoDB Buffers (II): Configuración

innodb_flush_log_at_trx_commit
Controla cómo el commit dispara el flush del log a disco
innodb_buffer_pool_size
80% de la memoria
Cuanto más grande, más datos se cachean
innodb_log_buffer_size
Un mayor tamaño permite que las transacciones grandes no tengan que escribir
a disco.
innodb_log_file_size
Un mayor tamaño de logfile:
reduce el tiempo entre checkpoints
aumenta el tiempo de recuperación
2 - Arquitectura MySQL » InnoDB

Índices InnoDB
PK Index

PK PK PK Index Index Index

PK Index

En InnoDB, la clave primaria direcciona directamente a la tabla.


El resto de claves, direccionan a la clave primaria.
El resto de claves también incluyen la clave primaria.
2 - Arquitectura MySQL » InnoDB

Índices InnoDB
PK Index

PK PK PK Index Index Index

PK Index

En InnoDB, la clave primaria direcciona directamente a la tabla.


El resto de claves, direccionan a la clave primaria.
El resto de claves también incluyen la clave primaria.
2 - Arquitectura MySQL » InnoDB

Índices InnoDB
PK Index

PK PK PK Index Index Index

PK Index

En InnoDB, la clave primaria direcciona directamente a la tabla.


El resto de claves, direccionan a la clave primaria.
El resto de claves también incluyen la clave primaria.
1- Introducción. ¿Por qué optimizar?
2- Arquitectura de MySQL

3 - Optimización de consultas

4- Ejercicios
5- Ruegos y Preguntas
3 - Optimización de consultas
3.1 - Query Cache
3 - Optimización de consultas » Query Cache

MySQL server

Query Cache

Parse

Optimization

Execution

MyISAM InnoDB Memory

Tablas

Si activamos la Query Cache,


la primera vez que ejecutemos la consulta seguirá los mismos pasos.
Con un paso adicional, a la vez que se devuelven los datos al usuario, se
guarda una copia en la caché de consultas.
3 - Optimización de consultas » Query Cache

SELECT

result

MySQL server

Query Cache

Parse

Optimization

Execution

MyISAM InnoDB Memory

Tablas

Si activamos la Query Cache,


la primera vez que ejecutemos la consulta seguirá los mismos pasos.
Con un paso adicional, a la vez que se devuelven los datos al usuario, se
guarda una copia en la caché de consultas.
3 - Optimización de consultas » Query Cache

SELECT

result

MySQL server
SELECT result
Query Cache

Parse

Optimization

Execution

MyISAM InnoDB Memory

Tablas

Si activamos la Query Cache,


la primera vez que ejecutemos la consulta seguirá los mismos pasos.
Con un paso adicional, a la vez que se devuelven los datos al usuario, se
guarda una copia en la caché de consultas.
3 - Optimización de consultas » Query Cache

MySQL server
SELECT result
Query Cache

Parse

Optimization

Execution

MyISAM InnoDB Memory

Tablas

La próxima vez que se ejecute la misma consulta,


se obtendrán los datos diréctamente de la cache.
Lo que es un proceso casi instantáneo.
3 - Optimización de consultas » Query Cache

SELECT

result

MySQL server
SELECT result
Query Cache

Parse

Optimization

Execution

MyISAM InnoDB Memory

Tablas

La próxima vez que se ejecute la misma consulta,


se obtendrán los datos diréctamente de la cache.
Lo que es un proceso casi instantáneo.
3 - Optimización de consultas » Query Cache

MySQL server
SELECT result
Query Cache

Parse

Optimization

Execution

MyISAM InnoDB Memory

Tablas

¿Estos datos están siempre actualizados?


Sí, porque si se modifican los datos subyacentes, la caché se limpia.
3 - Optimización de consultas » Query Cache

UPDATE

MySQL server

Query Cache

Parse

Optimization

Execution

MyISAM InnoDB Memory

Tablas

¿Estos datos están siempre actualizados?


Sí, porque si se modifican los datos subyacentes, la caché se limpia.
3 - Optimización de consultas » Query Cache

MySQL server

Query Cache

Parse

Optimization

Execution

MyISAM InnoDB Memory

Tablas

¿Estos datos están siempre actualizados?


Sí, porque si se modifican los datos subyacentes, la caché se limpia.
3 - Optimización de consultas » Query Cache

Configurar la Query Cache

query_cache_type
0 (OFF)
1 (ON) - SELECT SLQ_NO_CACHE
2 (DEMAND) - SELECT SQL_CACHE
query_cache_size
query_cache_limit
query_cache_min_res_unit
3 - Optimización de consultas » Query Cache

Monitorizar la Query Cache (I)

SET GLOBAL query_cache_size = 4 * 1024 * 1024;


SHOW GLOBAL STATUS LIKE 'qcache%';
+-------------------------+---------+
| Variable_name | Value |
+-------------------------+---------+
| Qcache_free_blocks | 1 |
| Qcache_free_memory | 3555808 |
| Qcache_hits | 460 |
| Qcache_inserts | 173 |
| Qcache_lowmem_prunes | 0 |
| Qcache_not_cached | 37 |
| Qcache_queries_in_cache | 173 |
| Qcache_total_blocks | 366 |
+-------------------------+---------+
3 - Optimización de consultas » Query Cache

Monitorizar la Query Cache (II)

SET GLOBAL query_cache_size = 4 * 1024 * 1024;


SHOW GLOBAL STATUS LIKE 'qcache%';
+-------------------------+---------+
| Variable_name | Value |
+-------------------------+---------+
| Qcache_free_blocks | 1 |
| Qcache_free_memory | 3555808 |
| Qcache_hits | 460 |
| Qcache_inserts | 173 |
| Qcache_lowmem_prunes | 0 |
| Qcache_not_cached | 37 |
| Qcache_queries_in_cache | 173 |
| Qcache_total_blocks | 366 |
+-------------------------+---------+
3 - Optimización de consultas » Query Cache

Monitorizar la Query Cache (III)

Uso = Qcache_hits / (Qcache_hits + COM_select)

SHOW GLOBAL STATUS LIKE 'Qcache_hits';


+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Qcache_hits | 460 |
+---------------+-------+
SHOW GLOBAL STATUS LIKE 'COM_select';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_select | 334 |
+---------------+-------+
3 - Optimización de consultas
3.2 - mysqlslap
3 - Optimización de consultas » mysqlslap

mysqlslap

$ mysqlslap [opciones]

opciones:
-i
-c
--create-schema
-q

otras opciones:
--user --password --host --port --socket

ejemplo:
$ mysqlslap -i 10 -c 2 --create-schema=test -q test.sql

mysqlslap permite realizar benchmarking básico de MySQL


3 - Optimización de consultas » mysqlslap

salida de mysqlslap

$ mysqlslap -i 10 -c 2 --create-schema=test -q test.sql


Benchmark
Average number of seconds to run all queries: 51.776 seconds
Minimum number of seconds to run all queries: 51.776 seconds
Maximum number of seconds to run all queries: 51.776 seconds
Number of clients running queries: 2
Average number of queries per client: 239

$ mysqlslap -i 10 -c 2 --create-schema=test -q test.sql --csv=test.csv


$ cat test.csv
,mixed,51.776,51.776,51.776,2,239
3 - Optimización de consultas » mysqlslap

salida de mysqlslap

$ mysqlslap -i 10 -c 2 --create-schema=test -q test.sql


Benchmark
Average number of seconds to run all queries: 51.776 seconds
Minimum number of seconds to run all queries: 51.776 seconds
Maximum number of seconds to run all queries: 51.776 seconds
Number of clients running queries: 2
Average number of queries per client: 239

$ mysqlslap -i 10 -c 2 --create-schema=test -q test.sql --csv=test.csv


$ cat test.csv
,mixed,51.776,51.776,51.776,2,239
3 - Optimización de consultas » mysqlslap

Preparar una suite de test

Datos
similares a los de producción

Consultas
similares a las de producción
suficiente cantidad
claves desordenadas

Servidor MySQL
Exclusivo para el test

Resultados
Leer con cuidado
3 - Optimización de consultas » mysqlslap

Preparar una suite de test: (I) Datos

> CREATE TABLE CityHuge LIKE City;


> INSERT INTO CityHuge
SELECT NULL, Name, CountryCode, District, Population
FROM City;
> INSERT INTO CityHuge
SELECT NULL, Name, CountryCode, District, Population
FROM CityHuge;
> SELECT COUNT(*) FROM CityHuge;
+----------+
| COUNT(*) |
+----------+
| 1044224 |
+----------+

Podemos crear tablas grandes insertando los datos de esa misma tabla
en sí misma.
3 - Optimización de consultas » mysqlslap

Preparar una suite de test: (II) Consultas

Logs
general
consultas lentas

Podemos hacer suites de test a partir de consultas SQL


3 - Optimización de consultas » mysqlslap

Preparar una suite de test: (II) Consultas

Logs
general
consultas lentas

SQL
$ mysql world -N --batch -e \
"SELECT CONCAT(
'SELECT ID FROM CityHuge WHERE CountryCode=\"', code, '\";')
FROM Country" > test.sql

Podemos hacer suites de test a partir de consultas SQL


3 - Optimización de consultas » mysqlslap

Preparar una suite de test: (III) Servidor MySQL

Exclusivo para el test


Configuración similar a producción
Limpiar cachés
Desactivar la Query Cache
3 - Optimización de consultas » mysqlslap

Preparar una suite de test: (IV) Resultados

Una diferencia de un 10% no es representativa

Test A : 5,45 s
6
Test B : 5,95 s

4,5

1,5

0
A B
3 - Optimización de consultas » mysqlslap

Preparar una suite de test: (V) Resultados

En producción se ejecutan consultas de varios tipos mezcladas:


Test A (5 selects tabla 1) : 0,11 s
Test B (5 selects tabla 2) : 45 s
Test C (400 inserts en tablas 1 y 2) : 15 s
Test D ( A + B + C intercalados) : 153s

A+B+C

D
0 40 80 120 160

El test D tarda ¡¡3 veces más!!


No hay que fijarse en el valor absoluto, en producción no tardará eso.
Hay que fijarse en el porcentaje de mejora entre una opción y otra.
3 - Optimización de consultas
3.3 - EXPLAIN
3 - Optimización de consultas » EXPLAIN

EXPLAIN

EXPLAIN SELECT id FROM City WHERE Population > 1000000\G


*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: City
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 4079
Extra: Using where

Explain informa de cómo va a ejecutar MySQL una consulta.


3 - Optimización de consultas » EXPLAIN

ALTER TABLE City ADD INDEX (Population);


ALTER TABLE City ADD INDEX `pop_code`(Population, CountryCode);
EXPLAIN SELECT Name
FROM City
WHERE CountryCode = 'ESP' AND Population > 1000000\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: City
type: range
possible_keys: Population,pop_code
key: Population
key_len: 4
ref: NULL
rows: 238
Extra: Using where
3 - Optimización de consultas » EXPLAIN

ALTER TABLE City ADD INDEX (Population);


ALTER TABLE City ADD INDEX `pop_code`(Population, CountryCode);
EXPLAIN SELECT Name
FROM City
WHERE CountryCode = 'ESP' AND Population > 1000000\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: City
type: range
possible_keys: Population,pop_code
key: Population
key_len: 4
ref: NULL
rows: 238
Extra: Using where
3 - Optimización de consultas » EXPLAIN

ALTER TABLE City ADD INDEX (Population);


ALTER TABLE City ADD INDEX `pop_code`(Population, CountryCode);
EXPLAIN SELECT Name
FROM City
WHERE CountryCode = 'ESP' AND Population > 1000000\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: City
type: range
possible_keys: Population,pop_code
key: Population
key_len: 4
ref: NULL
rows: 238
Extra: Using where
3 - Optimización de consultas » EXPLAIN

ALTER TABLE City ADD INDEX (Population);


ALTER TABLE City ADD INDEX `pop_code`(Population, CountryCode);
EXPLAIN SELECT Name
FROM City
WHERE CountryCode = 'ESP' AND Population > 1000000\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: City
type: range
possible_keys: Population,pop_code
key: Population
key_len: 4
ref: NULL
rows: 238
Extra: Using where
3 - Optimización de consultas » EXPLAIN

> EXPLAIN > EXPLAIN


SELECT * SELECT id
FROM City FROM City
WHERE ID = '345'\G WHERE ID = '345'\G
******** 1. row *********** ******** 1. row ***********
id: 1 id: 1
select_type: SIMPLE select_type: SIMPLE
table: City table: City
type: const type: const
possible_keys: PRIMARY possible_keys: PRIMARY
key: PRIMARY key: PRIMARY
key_len: 4 key_len: 4
ref: const ref: const
rows: 1 rows: 1
Extra: Extra: Using index
3 - Optimización de consultas » EXPLAIN

> EXPLAIN > EXPLAIN


SELECT * SELECT id
FROM City FROM City
WHERE ID = '345'\G WHERE ID = '345'\G
******** 1. row *********** ******** 1. row ***********
id: 1 id: 1
select_type: SIMPLE select_type: SIMPLE
table: City table: City
type: const type: const
possible_keys: PRIMARY possible_keys: PRIMARY
key: PRIMARY key: PRIMARY
key_len: 4 key_len: 4
ref: const ref: const
rows: 1 rows: 1
Extra: Extra: Using index

Seleccionar sólo las columnas necesarias permite que:


A veces los datos puedan leerse sólo de los índices (memoria).
Se lean menos datos de disco en el resto de casos.
3 - Optimización de consultas » EXPLAIN

> EXPLAIN > EXPLAIN


SELECT COUNT(id) SELECT COUNT(*)
FROM City FROM City
WHERE CountryCode = 'ESP'\G WHERE CountryCode = 'ESP'\G

150,0

c i COUNT(id) COUNT(*)
112,5
1 5 29,974 3,837
2 5 31,862 6,009

75,0 4 5 63,253 10,087


8 5 125,721 19,412

37,5

0
1 2 4 8

COUNT(id) COUNT(*)

En este caso, lo que ocurre es que COUNT(id) cuenta


el número de filas cuyo id es != NULL.
Para ello tiene que comprobar valor a valor.
3 - Optimización de consultas » EXPLAIN

EXPLAIN

SELECT City.Name, Country.Name

FROM City, Country

WHERE Country.Code = City.CountryCode

AND Country.Name="Spain"\G
*********** 1. row ********** *********** 2. row **********

id: 1 id: 1

select_type: SIMPLE select_type: SIMPLE

table: City table: Country

type: ALL type: eq_ref

possible_keys: NULL possible_keys: PRIMARY

key: NULL key: PRIMARY

key_len: NULL key_len: 3

ref: NULL ref: world.City.CountryCode

rows: 4079 rows: 1

Extra: Extra: Using where

La columna sobre la que se aplican las condiciones es importante.


3 - Optimización de consultas » EXPLAIN

EXPLAIN

SELECT City.Name, Country.Name

FROM City, Country

WHERE Country.Code = City.CountryCode

AND Country.Name="Spain"\G
*********** 1. row ********** *********** 2. row **********

id: 1 id: 1

select_type: SIMPLE select_type: SIMPLE

table: City table: Country

type: ALL type: eq_ref

possible_keys: NULL possible_keys: PRIMARY

key: NULL key: PRIMARY

key_len: NULL key_len: 3

ref: NULL ref: world.City.CountryCode

rows: 4079 rows: 1

Extra: Extra: Using where

La columna sobre la que se aplican las condiciones es importante.


3 - Optimización de consultas » EXPLAIN

EXPLAIN

SELECT City.Name, Country.Name

FROM City, Country

WHERE Country.Code = City.CountryCode

AND Country.Code="ESP"\G
*********** 1. row ********** *********** 2. row **********

id: 1 id: 1

select_type: SIMPLE select_type: SIMPLE

table: Country table: City

type: const type: ALL

possible_keys: PRIMARY possible_keys: NULL

key: PRIMARY key: NULL

key_len: 3 key_len: NULL

ref: const ref: NULL

rows: 1 rows: 4079

Extra: Extra: Using where

La columna sobre la que se aplican las condiciones es importante.


3 - Optimización de consultas » EXPLAIN

EXPLAIN

SELECT City.Name, Country.Name

FROM City, Country

WHERE Country.Code = City.CountryCode

AND Country.Code="ESP"\G
*********** 1. row ********** *********** 2. row **********

id: 1 id: 1

select_type: SIMPLE select_type: SIMPLE

table: Country table: City

type: const type: ALL

possible_keys: PRIMARY possible_keys: NULL

key: PRIMARY key: NULL

key_len: 3 key_len: NULL

ref: const ref: NULL

rows: 1 rows: 4079

Extra: Extra: Using where

La columna sobre la que se aplican las condiciones es importante.


3 - Optimización de consultas » EXPLAIN

Forzar Índices

USE INDEX (índices)


IGNORE INDEX (índices)
FORCE INDEX (índices)
3 - Optimización de consultas » EXPLAIN

STRAIGHT_JOIN

fuerza a que un JOIN se construya en el orden en el que se ha declarado


3 - Optimización de consultas » EXPLAIN

ANALYZE TABLE

Actualiza las estadísticas de una tabla


Ejecutado periódicamente ayuda al optimizador
3 - Optimización de consultas
3.4 - Metodología para elegir el
índice más óptimo
3 - Optimización de consultas » Metodología

Metodología para elegir el índice más óptimo


3 - Optimización de consultas » Metodología

Metodología para elegir el índice más óptimo

Saber qué queremos hacer


3 - Optimización de consultas » Metodología

Metodología para elegir el índice más óptimo

Saber qué queremos hacer


Saber cuál es la manera más óptima
3 - Optimización de consultas » Metodología

Metodología para elegir el índice más óptimo

Saber qué queremos hacer


Saber cuál es la manera más óptima
Saber qué hace mysql y por qué
3 - Optimización de consultas » Metodología

Metodología para elegir el índice más óptimo

Saber qué queremos hacer


Saber cuál es la manera más óptima
Saber qué hace mysql y por qué
3 - Optimización de consultas » Metodología

Metodología para elegir el índice más óptimo

Saber qué queremos hacer


Saber cuál es la manera más óptima
Saber qué hace mysql y por qué

MySQL sólo utilizará un índice por tabla


3 - Optimización de consultas » Metodología

Metodología para elegir el índice más óptimo

Saber qué queremos hacer


Saber cuál es la manera más óptima
Saber qué hace mysql y por qué

MySQL sólo utilizará un índice por tabla


MySQL procesará un JOIN realizando el menor número de accesos
3 - Optimización de consultas » Metodología

Metodología para elegir el índice más óptimo

Saber qué queremos hacer


Saber cuál es la manera más óptima
Saber qué hace mysql y por qué

MySQL sólo utilizará un índice por tabla


MySQL procesará un JOIN realizando el menor número de accesos
empezando por la tabla con un índice más optimo
3 - Optimización de consultas » Metodología

Metodología para elegir el índice más óptimo

Saber qué queremos hacer


Saber cuál es la manera más óptima
Saber qué hace mysql y por qué

MySQL sólo utilizará un índice por tabla


MySQL procesará un JOIN realizando el menor número de accesos
empezando por la tabla con un índice más optimo
dictado por las condiciones WHERE
3 - Optimización de consultas » Metodología

Metodología para elegir el índice más óptimo

Saber qué queremos hacer


Saber cuál es la manera más óptima
Saber qué hace mysql y por qué

MySQL sólo utilizará un índice por tabla


MySQL procesará un JOIN realizando el menor número de accesos
empezando por la tabla con un índice más optimo
dictado por las condiciones WHERE
y utilizando claves para seleccionar los registros de otras tablas
3 - Optimización de consultas » Metodología

Ejemplo

EXPLAIN SELECT City.Name, Country.Name FROM City, Country WHERE


Country.Code = City.CountryCode AND Country.Code = "ESP";
3 - Optimización de consultas » Metodología

I Formatear

EXPLAIN
SELECT City.Name,
Country.Name
FROM City,
Country
WHERE Country.Code = City.CountryCode
AND Country.Code = "ESP";

Existen Formateadores OnLine:


http://www.dpriver.com/pp/sqlformat.htm
3 - Optimización de consultas » Metodología

II Dibujar Tablas y Relaciones


SELECT City.Name,

Country.Name

FROM City,

Country

WHERE Country.Code = City.CountryCode

AND Country.Code = "ESP";


3 - Optimización de consultas » Metodología

II Dibujar Tablas y Relaciones


SELECT City.Name,

Country.Name
City Country
FROM City,

Country

WHERE Country.Code = City.CountryCode

AND Country.Code = "ESP";


3 - Optimización de consultas » Metodología

II Dibujar Tablas y Relaciones


SELECT City.Name,

Country.Name
City Country
FROM City,

Country

WHERE Country.Code = City.CountryCode

AND Country.Code = "ESP";


3 - Optimización de consultas » Metodología

II Dibujar Tablas y Relaciones


SELECT City.Name,

Country.Name
City Country
FROM City,
CountryCode
Country

WHERE Country.Code = City.CountryCode


Code
AND Country.Code = "ESP";
3 - Optimización de consultas » Metodología

III Añadir columnas de SELECT y WHERE


SELECT City.Name,

Country.Name
City Country
FROM City,
CountryCode
Country

WHERE Country.Code = City.CountryCode


Code
AND Country.Code = "ESP";
3 - Optimización de consultas » Metodología

III Añadir columnas de SELECT y WHERE


SELECT City.Name,

Country.Name
City Country
FROM City,
CountryCode
Country
Code = "ESP"
WHERE Country.Code = City.CountryCode
Code
AND Country.Code = "ESP";
3 - Optimización de consultas » Metodología

III Añadir columnas de SELECT y WHERE


SELECT City.Name,

Country.Name
City Country
FROM City,
CountryCode
Country
Code = "ESP"
WHERE Country.Code = City.CountryCode
Code
AND Country.Code = "ESP";
Name Name
3 - Optimización de consultas » Metodología

IV Añadir Índices
SELECT City.Name,

Country.Name
City Country
FROM City,
CountryCode
Country PK
Code = "ESP"
WHERE Country.Code = City.CountryCode PK
Code
AND Country.Code = "ESP";
Name Name
3 - Optimización de consultas » Metodología

V Elegir el mejor plan de actuación

SELECT City.Name,

Country.Name
City Country
FROM City, CountryCode
PK
Code = "ESP"
Country PK
Code
WHERE Country.Code = City.CountryCode
Name Name
AND Country.Code = "ESP";
3 - Optimización de consultas » Metodología

V Elegir el mejor plan de actuación


Plan A
City » Country
Recorrer secuencialmente City
Por cada registro en City (4000):
Buscar una equivalencia en Country
usando PK
Seleccionar la fila sólo si Code = "ESP"
De las filas seleccionadas, leer:
City.Name (de la tabla)
Country.Name (de la tabla)

SELECT City.Name,

Country.Name
City Country
FROM City, CountryCode
PK
Code = "ESP"
Country PK
Code
WHERE Country.Code = City.CountryCode
Name Name
AND Country.Code = "ESP";
3 - Optimización de consultas » Metodología

V Elegir el mejor plan de actuación


Plan A Plan B
City » Country Country » City
Recorrer secuencialmente City Seleccionar Code = "ESP" de Country usando PK
Por cada registro en City (4000): Por cada registro en Country (1):
Buscar una equivalencia en Country Recorre secuencialmente City
usando PK Seleccionar la fila sólo si CountryCode = Code
Seleccionar la fila sólo si Code = "ESP" De las filas seleccionadas, leer:
De las filas seleccionadas, leer: City.Name (de la tabla)
City.Name (de la tabla) Country.Name (de la tabla)
Country.Name (de la tabla)

SELECT City.Name,

Country.Name
City Country
FROM City, CountryCode
PK
Code = "ESP"
Country PK
Code
WHERE Country.Code = City.CountryCode
Name Name
AND Country.Code = "ESP";
3 - Optimización de consultas » Metodología

V Elegir el mejor plan de actuación


Plan A Plan B
City » Country Country » City
Recorrer secuencialmente City Seleccionar Code = "ESP" de Country usando PK
Por cada registro en City (4000): Por cada registro en Country (1):
Buscar una equivalencia en Country Recorre secuencialmente City
usando PK Seleccionar la fila sólo si CountryCode = Code
Seleccionar la fila sólo si Code = "ESP" De las filas seleccionadas, leer:
De las filas seleccionadas, leer: City.Name (de la tabla)
City.Name (de la tabla) Country.Name (de la tabla)
Country.Name (de la tabla)

SELECT City.Name,

Country.Name
City Country
FROM City, CountryCode
PK
Code = "ESP"
Country PK
Code
WHERE Country.Code = City.CountryCode
Name Name
AND Country.Code = "ESP";
3 - Optimización de consultas » Metodología

V Elegir el mejor plan de actuación


Plan A Plan B
City » Country Country » City
Recorrer secuencialmente City Seleccionar Code = "ESP" de Country usando PK
Por cada registro en City (4000): Por cada registro en Country (1):
Buscar una equivalencia en Country Recorre secuencialmente City
usando PK Seleccionar la fila sólo si CountryCode = Code
Seleccionar la fila sólo si Code = "ESP" De las filas seleccionadas, leer:
De las filas seleccionadas, leer: City.Name (de la tabla)
City.Name (de la tabla) Country.Name (de la tabla)
Country.Name (de la tabla)

SELECT City.Name,

Country.Name
City Country
FROM City, CountryCode
PK
Code = "ESP"
Country PK
Code
WHERE Country.Code = City.CountryCode
Name Name
AND Country.Code = "ESP";
3 - Optimización de consultas » Metodología

V Cotejar con EXPLAIN


EXPLAIN

SELECT City.Name,

Country.Name

FROM City,

Country

WHERE Country.Code = City.CountryCode

AND Country.Code = "ESP";

*********** 1. row **********

table: Country

type: const

key: PRIMARY

rows: 1

Extra: City Country


*********** 2. row ********** CountryCode
PK
table: City Code = "ESP"
PK
type: ALL Code
key: NULL Name Name
rows: 4079

Extra: Using where


3 - Optimización de consultas » Metodología

V Cotejar con EXPLAIN


EXPLAIN Plan B
SELECT City.Name, Country » City
Country.Name Seleccionar Code = "ESP" de Country usando PK
FROM City, Por cada registro en Country (1):
Country Recorre secuencialmente City
WHERE Country.Code = City.CountryCode Seleccionar la fila sólo si CountryCode = Code
AND Country.Code = "ESP"; De las filas seleccionadas, leer:
*********** 1. row ********** City.Name (de la tabla)
table: Country Country.Name (de la tabla)
type: const

key: PRIMARY

rows: 1

Extra: City Country


*********** 2. row ********** CountryCode
PK
table: City Code = "ESP"
PK
type: ALL Code
key: NULL Name Name
rows: 4079

Extra: Using where


3 - Optimización de consultas » Metodología

¿Mi consulta es
todo lo óptima que podría?


Terminé de optimizar

No

Optimizar

¿Explain sigue mi
plan de ejecución?

No

Adaptar
3 - Optimización de consultas » Metodología

VII Optimizar el uso de índices


SELECT City.Name, ¿Qué columnas añadir a un índice?
Country.Name

FROM City,
Columnas JOIN
Country

WHERE Country.Code = City.CountryCode En la 2ª tabla deben ser la 1ª columna de una clave


AND Country.Code = "ESP";

Condiciones WHERE
Country Se deben escribir sobre columnas indexadas, o
Crear un índice que contenga al resto de columnas
PK Code = "ESP"
Name
PK Code Columnas SELECT
Seleccionar sólo las necesarias
CountryCode
Si todas son parte del índice mejora el rendimiento
City
Name
Dividir la consulta en trozos más pequeños
Ayuda a saber qué parte tiene peor rendimiento
3 - Optimización de consultas » Metodología

VII Optimizar el uso de índices


SELECT City.Name, ¿Qué columnas añadir a un índice?
Country.Name

FROM City,
Columnas JOIN
Country

WHERE Country.Code = City.CountryCode En la 2ª tabla deben ser la 1ª columna de una clave


AND Country.Code = "ESP";

Condiciones WHERE
Country Se deben escribir sobre columnas indexadas, o
Crear un índice que contenga al resto de columnas
PK Code = "ESP"
Name
PK Code Columnas SELECT
Seleccionar sólo las necesarias
CountryCode
Si todas son parte del índice mejora el rendimiento
City
Name
Dividir la consulta en trozos más pequeños
Ayuda a saber qué parte tiene peor rendimiento
3 - Optimización de consultas » Metodología

VIII Adaptar

Para que MySQL siga nuestro plan:


Asegurarnos de que hemos creado los índices
ANALIZE TABLE
FORCE | IGNORE INDEX
Reescribir la consulta
WHERE
Condiciones JOIN
3 - Optimización de consultas
3.5 - Optimización con Subconsultas
3 - Optimización de consultas » Optimización con subconsultas

Consultas no correlativas
EXPLAIN

SELECT Name FROM City

WHERE City.ID IN (

SELECT Capital FROM Country WHERE Country.Continent = 'Europe'

)\G
************* 1. row ************* ************** 2. row **************

id: 1 id: 2

select_type: PRIMARY select_type: DEPENDENT SUBQUERY

table: City table: Country

type: ALL type: ALL

possible_keys: NULL possible_keys: NULL

key: NULL key: NULL

key_len: NULL key_len: NULL

ref: NULL ref: NULL

rows: 4079 rows: 239

Extra: Using where Extra: Using where

Las subconsultas no correlativas son aquellas que se pueden ejecutar de


manera independiente a la consulta principal.
3 - Optimización de consultas » Optimización con subconsultas

Consultas no correlativas
EXPLAIN

SELECT Name FROM City, (

SELECT Capital FROM Country WHERE Country.Continent = 'Europe'

) co

WHERE City.ID = co.Capital\G


******* 1. row ******* ******* 2. row ******* ******* 3. row *******

id: 1 id: 1 id: 2

select_type: PRIMARY select_type: PRIMARY select_type: DERIVED

table: <derived2> table: City table: Country

type: ALL type: eq_ref type: ALL

possible_keys: NULL possible_keys: PRIMARY possible_keys: NULL

key: NULL key: PRIMARY key: NULL

key_len: NULL key_len: 4 key_len: NULL

ref: NULL ref: co.Capital ref: NULL

rows: 46 rows: 1 rows: 239

Extra: Extra: ...

Las subconsultas no correlativas se pueden situar como una tabla con la


que hacer JOIN
3 - Optimización de consultas » Optimización con subconsultas

Consultas no correlativas
EXPLAIN

SELECT Name

FROM City, Country

WHERE City.ID = Country.Capital

AND Country.Continent = 'Europe'\G


************* 1. row ************* ************** 2. row **************

id: 1 id: 1

select_type: SIMPLE select_type: SIMPLE

table: Country table: City

type: ALL type: eq_ref

possible_keys: NULL possible_keys: PRIMARY

key: NULL key: PRIMARY

key_len: NULL key_len: 4

ref: NULL ref: world.Country.Capital

rows: 239 rows: 1

Extra: Using where Extra:

A veces se pueden reescribir como un JOIN


3 - Optimización de consultas » Optimización con subconsultas

Funciones de agregación

SELECT Name, Population


FROM Country,
(SELECT MAX(Population) max_pop FROM Country) co
WHERE Country.Population = co.max_pop;

Y otras, como cuando se usan funciones de agregación, no.


3 - Optimización de consultas » Optimización con subconsultas

Condiciones OR

SELECT ci.Name
FROM City ci, Country co, CountryLanguage cl
WHERE ci.CountryCode = co.Code
AND co.Code = cl.CountryCode
AND (ci.Name LIKE 'Es%' OR cl.Language LIKE 'Es%');

Las condiciones OR sobre campos de varias tablas no permite que se


puedan usar índices.
3 - Optimización de consultas » Optimización con subconsultas

Condiciones OR

ALTER TABLE City ADD INDEX (CountryCode), ADD INDEX (Name);


ALTER TABLE CountryLanguage ADD INDEX (Language);
SELECT ci.Name
FROM City ci,
( SELECT ID FROM City ci, Country co, CountryLanguage cl
WHERE ci.CountryCode = co.Code
AND co.Code = cl.CountryCode
AND cl.Language LIKE 'Es%'
UNION
SELECT ID FROM City ci
WHERE ci.Name LIKE 'Es%'
) ci_ids
WHERE ci.ID = ci_ids.ID;

Se puede reescribir como subconsultas para ayudar a MySQL.


3 - Optimización de consultas
3.6 - Optimización usando cachés
propias
3 - Optimización de consultas » Optimización usando cachés propias

City Country

Name Continent
Name

Si vamos a consultar muchas veces las ciudades por continente, nos


puede interesar desnormalizar creando una tabla caché que relacione
directamente las ciudades con sus continentes.
3 - Optimización de consultas » Optimización usando cachés propias

City Country City_Country

City_Name
Name Continent Continent
Name Country_Name

Si vamos a consultar muchas veces las ciudades por continente, nos


puede interesar desnormalizar creando una tabla caché que relacione
directamente las ciudades con sus continentes.
3 - Optimización de consultas » Optimización usando cachés propias

City Country City_Country

City_Name
Name Continent Continent
Name Country_Name

Si vamos a consultar muchas veces las ciudades por continente, nos


puede interesar desnormalizar creando una tabla caché que relacione
directamente las ciudades con sus continentes.
3 - Optimización de consultas
3.7 - Optimización con MySQL 5.5
3 - Optimización de consultas » MySQL 5.5

MySQL 5.5

Source:
http://datacharmer.blogspot.com/2009/04/mysql-54-performance-with-logging.html
3 - Optimización de consultas » MySQL 5.5

MySQL 5.5

x 1.59
3 - Optimización de consultas » MySQL 5.5

MySQL 5.5

Actualizar es un cambio "fácil" de llevar a cabo.


3 - Optimización de consultas
3.7 - Optimización con MySQL 5.5
3.7.1 - Particionado
3 - Optimización de consultas » Particionado

Particionado

http://www.slideshare.net/datacharmer/mysql-partitions
3 - Optimización de consultas
3.7 - Optimización con MySQL 5.5
3.7.2 - Triggers
3 - Optimización de consultas » Triggers

Triggers

Código que se ejecuta:

ANTES
DESPUÉS

de

INSERTAR
MODIFICAR
BORRAR
3 - Optimización de consultas » Triggers

Triggers

Hasta 6 triggers por tabla


Sintaxis:

CREATE TABLE account (acct_num INT, amount DECIMAL(10,2));

CREATE TRIGGER ins_sum BEFORE INSERT ON account

FOR EACH ROW SET @sum = @sum + NEW.amount;


3 - Optimización de consultas » Triggers

Triggers

Auditoría
Forzar Integridad
Mantener cachés
3 - Optimización de consultas » Triggers

Coste

A: 15,16 1 Índice
B: 15,20 1 Índice + 1 Trigger
C: 14,87 1 Trigger
15,2

11,4

7,6

3,8

0
A B C

Un trigger apenas afecta al rendimiento en comparación a un índice.


1- Introducción. ¿Por qué optimizar?
2- Arquitectura de MySQL
3- Optimización de consultas

4.- Ejercicios

5- Ruegos y Preguntas
Ejercicio I

> SELECT Name FROM City WHERE Name = 'Madrid';


> SELECT Name FROM City WHERE Name Like 'Madrid%';

$ mysql world -NB -e

"SELECT CONCAT('SELECT Name FROM City WHERE Name = \'', Name ,'\';') FROM
City;" > test_equal.sql

$ mysql world -NB -e

"SELECT CONCAT('SELECT Name FROM City WHERE Name LIKE \'', Name ,'%\';')
FROM City;" > test_like.sql

$ mysqlslap -i 1000 -c 4 --create-schema=world -e test_equal.sql

$ mysqlslap -i 1000 -c 4 --create-schema=world -e test_like.sql


Ejercicio II

> SELECT Name FROM city WHERE Population BETWEEN 20000 AND 30000;

> CREATE TABLE City2 LIKE City;

> INSERT INTO City2 SELECT * FROM City;

> INSERT INTO City2

SELECT Null, CONCAT(Name,'1'), CountryCode, District, Population * 0.95

FROM City2;

> SELECT Name FROM City2 WHERE Population BETWEEN 20000 AND 30000;

> ALTER TABLE City2 ADD INDEX(Population, Name);

> SELECT Name FROM City2 WHERE Population BETWEEN 20000 AND 30000;
Ejercicio III

SELECT SUM(salary)
FROM salaries
WHERE from_date BETWEEN '1999-01-01' AND '2000-01-01';
Ejercicio III (solución)

SELECT SUM(salary)
FROM salaries
WHERE from_date BETWEEN '1999-01-01' AND '2000-01-01';

ALTER TABLE salaries ADD INDEX date_salary(from_date, salary);


Ejercicio IV

SELECT t.title,
AVG(s.salary) salario_medio
FROM titles t,
salaries s
WHERE t.emp_no = s.emp_no
AND t.to_date > NOW()
AND s.to_date > NOW()
GROUP BY t.title
ORDER BY salario_medio DESC;
Ejercicio (solución)

SELECT t.title,
AVG(s.salary) salario_medio
FROM titles t,
salaries s
WHERE t.emp_no = s.emp_no
AND t.to_date > NOW()
AND s.to_date > NOW()
GROUP BY t.title
ORDER BY salario_medio DESC;

ALTER TABLE titles ADD KEY curr_titles(to_date, emp_no, title);


Ejercicio V

SELECT e.first_name,
e.last_name,
s.salary
FROM employees e,
titles t,
salaries s
WHERE e.emp_no = t.emp_no
AND e.emp_no = s.emp_no
AND t.title = 'Manager'
AND t.to_date > NOW()
AND s.to_date > NOW();
1- Introducción. ¿Por qué optimizar?
2- Arquitectura de MySQL
3- Optimización de consultas
4- Ejercicios

5.- Ruegos y Preguntas


!
Curso MySQL Performance Tuning

Del 2 al 5 de Junio
training@warp.es
!
¡Gracias!
Optimización del rendimiento con MySQL ( deCharlas Mayo 2012)

Víctor Jiménez Cerrada <vjimenez@warp.es>


@capitangolo
http://slideshare.net/capitangolo

Bienvenidos a "Optimización del rendimiento con MySQL".


Soy Víctor Jiménez y seré vuestro ponente para hoy.

También podría gustarte