Sunteți pe pagina 1din 156

Optimizacin del rendimiento con MySQL ( deCharlas Mayo 2012)

Vicente Vctor Jimnez Cerrada <vjimenez@warp.es> @capitangolo

Bienvenidos a "Optimizacin del rendimiento con MySQL". Soy Vctor Jimnez y ser vuestro ponente para hoy.

Trabajo en Warp Networks S.L. http://www.warp.es Donde nos dedicamos a varias cosas, entre ellas, formacin y consultora MySQL

Trabajo en Warp Networks S.L. http://www.warp.es Donde nos dedicamos a varias cosas, entre ellas, formacin y consultora MySQL

Agenda
5"

1- Introduccin. Por qu optimizar? 2- Arquitectura de MySQL 3- Optimizacin de consultas

20"

30"

4- Ejercicios
40"

5- Ruegos y Preguntas

1 - Introduccin Por Qu optimizar?


2- Arquitectura de MySQL 3- Optimizacin de consultas 4- Ejercicios 5- Ruegos y Preguntas

1 - Introduccin 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 ms solicitudes, y si no tenemos cuidado

1 - Introduccin Por Qu optimizar?

nuestro servidor se cuelga. A esto se le llama morir de xito.

1 - Introduccin Por Qu optimizar?

Pasos para no morir de xito


Optimizar la aplicacin Optimizar la base de datos Escalar

1 - Introduccin Por Qu optimizar?

Optimizacin

1 - Introduccin Por Qu optimizar?

Optimizacin
Hacer ms con lo mismo

1 - Introduccin Por Qu optimizar?

Optimizacin
Hacer ms con lo mismo Objetivo: Aumentar Consultas / segundo

1 - Introduccin Por Qu optimizar?

Optimizacin
Hacer ms con lo mismo Objetivo: Aumentar Consultas / segundo Cmo?: Menor tiempo de ejecucin

1 - Introduccin 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 - Introduccin 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 - Introduccin Por Qu optimizar?

0s

1s

2s

3s

4s

5s

6s

7s

8s

9s

Dado que la mquina tiene ms recursos ocupados, es posible que tarde ms en ejecutarse. En este caso, se ha ejecutado la consulta cinco veces en paralelo, cada una un segundo ms tarde que la anterior.

1 - Introduccin Por Qu optimizar?

0s

1s

2s

3s

4s

5s

6s

7s

8s

9s

Dado que la mquina tiene ms recursos ocupados, es posible que tarde ms en ejecutarse. En este caso, se ha ejecutado la consulta cinco veces en paralelo, cada una un segundo ms tarde que la anterior.

1 - Introduccin Por Qu optimizar?

C=5

0s

1s

2s

3s

4s

5s

6s

7s

8s

9s

En un momento se estn ejecutando 5 consultas a la vez. El servidor necesita poder soportar estos picos.

1 - Introduccin 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 - Introduccin Por Qu optimizar?

C max = 1

0s

1s

2s

3s

4s

5s

6s

7s

8s

9s

... slo se ejecuta una consulta cada vez. Reduciendo el nivel de concurrencia que tiene que soportar el servidor.

1 - Introduccin Por Qu optimizar?

0s

1s

2s

3s

4s

5s

6s

7s

8s

9s

Dejando muchos ms recursos libres.

1 - Introduccin Por Qu optimizar?

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

Dejando muchos ms recursos libres.

1- Introduccin. Por qu optimizar?

2 - Arquitectura MySQL
3- Optimizacin de consultas 4- Ejercicios 5- Ruegos y Preguntas

2 - Arquitectura MySQL

mysqld mysqld-nt

mysql 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 mysqld-nt

mysql 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 Cachs 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 Sesin
Reservado por cada conexin Principalmente para gestionar los resultados sort_buffer join_buffer read_buffer

Hay que tener cuidado al congurar las variables de sesin. 20MB de sort_buffer x 100 conexiones = 2GB de memoria

2 - Arquitectura MySQL

API C Intrprete Query Optimizador Cache Executador Motores MyISAM Memory InnoDB CSV

Subsistemas Funciones base Hilos Buffers y cachs Red Logs Acceso y Permisos

2 - Arquitectura MySQL

Motores de Almacenamiento
Gestionan la persistencia y recuperacin de los datos Configuracin 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 InfoBrigth Nitro PBXT

2 - Arquitectura MySQL 2.1 - MyISAM

2 - Arquitectura MySQL MyISAM

Caractersticas de MyISAM (I)

/usr/local/mysql world City.frm City.MYD City.MYI

MyISAM guarda la informacin en dos archivos .MYD(ata) y .MYI(ndex)

2 - Arquitectura MySQL MyISAM

Caractersticas 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

Caractersticas de MyISAM (III)


Compresin 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 Problemtico 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 dems selects posteriores esperan a que el insert libere el bloqueo. Hasta que el primer select termina, no se terminan de ejecutar las dems 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 dems selects posteriores esperan a que el insert libere el bloqueo. Hasta que el primer select termina, no se terminan de ejecutar las dems 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 dems selects posteriores esperan a que el insert libere el bloqueo. Hasta que el primer select termina, no se terminan de ejecutar las dems 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 dems selects posteriores esperan a que el insert libere el bloqueo. Hasta que el primer select termina, no se terminan de ejecutar las dems 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

Caractersticas de InnoDB (I): Almacenamiento

/usr/local/mysql test table.frm ibdata1 ib_logfile0 ib_logfile1

InnoDB guarda toda la informacin de todas las tablas en el tablespace. Archivo ibdata

2 - Arquitectura MySQL InnoDB

Caractersticas de InnoDB (II): Files per table


innodb_file_per_table /usr/local/mysql test table.frm table.ibd ibdata1 ib_logfile0 ib_logfile1

Si conguramos innodb_le_per_table tenemos un sub-espacio de tabla por cada tabla Archivo .ibd El espacio de tabla sigue conteniendo informacin de cada tabla, es necesario.

2 - Arquitectura MySQL InnoDB

Caractersticas de InnoDB (III)


Transacciones full ACID Bloqueo a nivel de fila
El bloqueo se realiza en la PK Buscando rangos, se bloquean tambin los huecos

Bloqueo a nivel de tabla Cachea tanto ndices como datos

2 - Arquitectura MySQL InnoDB

InnoDB Buffers (I)


Buffer Pool Cach de datos e ndices Log Buffer Log de transacciones logfiles Log de transacciones Redo log ibdata Diccionario de datos Undo log
LOG BUFFER

MYSQL SERVER

BUFFER POOL

commit & checkpoints

checkpoints

ib_logfiles

ibdata

2 - Arquitectura MySQL InnoDB

InnoDB Buffers (II): Configuracin


innodb_flush_log_at_trx_commit
Controla cmo el commit dispara el flush del log a disco

innodb_buffer_pool_size
80% de la memoria Cuanto ms grande, ms datos se cachean

innodb_log_buffer_size
Un mayor tamao permite que las transacciones grandes no tengan que escribir a disco.

innodb_log_file_size
Un mayor tamao de logfile:
reduce el tiempo entre checkpoints aumenta el tiempo de recuperacin

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 tambin 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 tambin 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 tambin incluyen la clave primaria.

1- Introduccin. Por qu optimizar? 2- Arquitectura de MySQL

3 - Optimizacin de consultas
4- Ejercicios 5- Ruegos y Preguntas

3 - Optimizacin de consultas 3.1 - Query Cache

3 - Optimizacin 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 - Optimizacin 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 - Optimizacin de consultas Query Cache

SELECT result

MySQL server
Query Cache Parse Optimization Execution MyISAM InnoDB Memory SELECT result

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 - Optimizacin de consultas Query Cache

MySQL server
Query Cache Parse Optimization Execution MyISAM InnoDB Memory SELECT result

Tablas

La prxima vez que se ejecute la misma consulta, se obtendrn los datos dirctamente de la cache. Lo que es un proceso casi instantneo.

3 - Optimizacin de consultas Query Cache

SELECT result

MySQL server
Query Cache Parse Optimization Execution MyISAM InnoDB Memory SELECT result

Tablas

La prxima vez que se ejecute la misma consulta, se obtendrn los datos dirctamente de la cache. Lo que es un proceso casi instantneo.

3 - Optimizacin de consultas Query Cache

MySQL server
Query Cache Parse Optimization Execution MyISAM InnoDB Memory SELECT result

Tablas

Estos datos estn siempre actualizados? S, porque si se modican los datos subyacentes, la cach se limpia.

3 - Optimizacin de consultas Query Cache

UPDATE

MySQL server
Query Cache Parse Optimization Execution MyISAM InnoDB Memory

Tablas

Estos datos estn siempre actualizados? S, porque si se modican los datos subyacentes, la cach se limpia.

3 - Optimizacin de consultas Query Cache

MySQL server
Query Cache Parse Optimization Execution MyISAM InnoDB Memory

Tablas

Estos datos estn siempre actualizados? S, porque si se modican los datos subyacentes, la cach se limpia.

3 - Optimizacin 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 - Optimizacin de consultas Query Cache

Monitorizar la Query Cache (I)


SET GLOBAL query_cache_size = 4 * 1024 * 1024; SHOW GLOBAL STATUS LIKE 'qcache%'; +-------------------------+---------+ | Variable_name | Qcache_free_blocks | Qcache_free_memory | Qcache_hits | Qcache_inserts | Qcache_lowmem_prunes | Qcache_not_cached | Qcache_total_blocks | Value | 1 | 460 | 173 | 0 | 37 | 366 | | | | | | | | +-------------------------+---------+ | 3555808 |

| Qcache_queries_in_cache | 173

+-------------------------+---------+

3 - Optimizacin de consultas Query Cache

Monitorizar la Query Cache (II)


SET GLOBAL query_cache_size = 4 * 1024 * 1024; SHOW GLOBAL STATUS LIKE 'qcache%'; +-------------------------+---------+ | Variable_name | Qcache_free_blocks | Qcache_free_memory | Qcache_hits | Qcache_inserts | Qcache_lowmem_prunes | Qcache_not_cached | Qcache_total_blocks | Value | 1 | 460 | 173 | 0 | 37 | 366 | | | | | | | | +-------------------------+---------+ | 3555808 |

| Qcache_queries_in_cache | 173

+-------------------------+---------+

3 - Optimizacin 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 - Optimizacin de consultas 3.2 - mysqlslap

3 - Optimizacin 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 bsico de MySQL

3 - Optimizacin 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 - Optimizacin 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 - Optimizacin de consultas mysqlslap

Preparar una suite de test


Datos
similares a los de produccin

Consultas
similares a las de produccin suficiente cantidad claves desordenadas

Servidor MySQL
Exclusivo para el test

Resultados
Leer con cuidado

3 - Optimizacin 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 - Optimizacin 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 - Optimizacin 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 - Optimizacin de consultas mysqlslap

Preparar una suite de test: (III) Servidor MySQL


Exclusivo para el test Configuracin similar a produccin Limpiar cachs Desactivar la Query Cache

3 - Optimizacin de consultas mysqlslap

Preparar una suite de test: (IV) Resultados


Una diferencia de un 10% no es representativa
Test A : 5,45 s Test B : 5,95 s

4,5

1,5

3 - Optimizacin de consultas mysqlslap

Preparar una suite de test: (V) Resultados


En produccin 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 A+B+C D 0 40 80 120 160

El test D tarda 3 veces ms!! No hay que jarse en el valor absoluto, en produccin no tardar eso. Hay que jarse en el porcentaje de mejora entre una opcin y otra.

3 - Optimizacin de consultas 3.3 - EXPLAIN

3 - Optimizacin 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 cmo va a ejecutar MySQL una consulta.

3 - Optimizacin de consultas EXPLAIN

ALTER TABLE City ADD INDEX (Population); ALTER TABLE City ADD INDEX `pop_code`(Population, CountryCode); EXPLAIN SELECT FROM Name 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 - Optimizacin de consultas EXPLAIN

ALTER TABLE City ADD INDEX (Population); ALTER TABLE City ADD INDEX `pop_code`(Population, CountryCode); EXPLAIN SELECT FROM Name 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 - Optimizacin de consultas EXPLAIN

ALTER TABLE City ADD INDEX (Population); ALTER TABLE City ADD INDEX `pop_code`(Population, CountryCode); EXPLAIN SELECT FROM Name 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 - Optimizacin de consultas EXPLAIN

ALTER TABLE City ADD INDEX (Population); ALTER TABLE City ADD INDEX `pop_code`(Population, CountryCode); EXPLAIN SELECT FROM Name 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 - Optimizacin de consultas EXPLAIN

> EXPLAIN SELECT FROM * City ***********

> EXPLAIN SELECT FROM id City ***********

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

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

3 - Optimizacin de consultas EXPLAIN

> EXPLAIN SELECT FROM * City ***********

> EXPLAIN SELECT FROM id City ***********

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

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

Seleccionar slo las columnas necesarias permite que: A veces los datos puedan leerse slo de los ndices (memoria). Se lean menos datos de disco en el resto de casos.

3 - Optimizacin de consultas EXPLAIN

> EXPLAIN SELECT FROM COUNT(id) City

> EXPLAIN SELECT FROM COUNT(*) City

WHERE CountryCode = 'ESP'\G


150,0

WHERE CountryCode = 'ESP'\G

c
112,5

i 5 5 5 5

COUNT(id) 29,974 31,862 63,253 125,721

COUNT(*) 3,837 6,009 10,087 19,412

1 2
75,0

4 8

37,5

2 COUNT(id)

4 COUNT(*)

En este caso, lo que ocurre es que COUNT(id) cuenta el nmero de las cuyo id es != NULL. Para ello tiene que comprobar valor a valor.

3 - Optimizacin de consultas EXPLAIN EXPLAIN SELECT FROM City.Name, Country.Name City, Country

WHERE Country.Code = City.CountryCode AND Country.Name="Spain"\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: ********** *********** 2. row id: 1 select_type: SIMPLE table: Country type: eq_ref possible_keys: PRIMARY key: PRIMARY key_len: 3 ref: world.City.CountryCode rows: 1 Extra: Using where **********

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

3 - Optimizacin de consultas EXPLAIN EXPLAIN SELECT FROM City.Name, Country.Name City, Country

WHERE Country.Code = City.CountryCode AND Country.Name="Spain"\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: ********** *********** 2. row id: 1 select_type: SIMPLE table: Country type: eq_ref possible_keys: PRIMARY key: PRIMARY key_len: 3 ref: world.City.CountryCode rows: 1 Extra: Using where **********

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

3 - Optimizacin de consultas EXPLAIN EXPLAIN SELECT FROM City.Name, Country.Name City, Country

WHERE Country.Code = City.CountryCode AND Country.Code="ESP"\G *********** 1. row id: 1 select_type: SIMPLE table: Country type: const possible_keys: PRIMARY key: PRIMARY key_len: 3 ref: const rows: 1 Extra: ********** *********** 2. 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 **********

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

3 - Optimizacin de consultas EXPLAIN EXPLAIN SELECT FROM City.Name, Country.Name City, Country

WHERE Country.Code = City.CountryCode AND Country.Code="ESP"\G *********** 1. row id: 1 select_type: SIMPLE table: Country type: const possible_keys: PRIMARY key: PRIMARY key_len: 3 ref: const rows: 1 Extra: ********** *********** 2. 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 **********

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

3 - Optimizacin de consultas EXPLAIN

Forzar ndices
USE INDEX (ndices) IGNORE INDEX (ndices) FORCE INDEX (ndices)

3 - Optimizacin de consultas EXPLAIN

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

3 - Optimizacin de consultas EXPLAIN

ANALYZE TABLE
Actualiza las estadsticas de una tabla Ejecutado peridicamente ayuda al optimizador

3 - Optimizacin de consultas 3.4 - Metodologa para elegir el ndice ms ptimo

3 - Optimizacin de consultas Metodologa

Metodologa para elegir el ndice ms ptimo

3 - Optimizacin de consultas Metodologa

Metodologa para elegir el ndice ms ptimo


Saber qu queremos hacer

3 - Optimizacin de consultas Metodologa

Metodologa para elegir el ndice ms ptimo


Saber qu queremos hacer Saber cul es la manera ms ptima

3 - Optimizacin de consultas Metodologa

Metodologa para elegir el ndice ms ptimo


Saber qu queremos hacer Saber cul es la manera ms ptima Saber qu hace mysql y por qu

3 - Optimizacin de consultas Metodologa

Metodologa para elegir el ndice ms ptimo


Saber qu queremos hacer Saber cul es la manera ms ptima Saber qu hace mysql y por qu

3 - Optimizacin de consultas Metodologa

Metodologa para elegir el ndice ms ptimo


Saber qu queremos hacer Saber cul es la manera ms ptima Saber qu hace mysql y por qu MySQL slo utilizar un ndice por tabla

3 - Optimizacin de consultas Metodologa

Metodologa para elegir el ndice ms ptimo


Saber qu queremos hacer Saber cul es la manera ms ptima Saber qu hace mysql y por qu MySQL slo utilizar un ndice por tabla MySQL procesar un JOIN realizando el menor nmero de accesos

3 - Optimizacin de consultas Metodologa

Metodologa para elegir el ndice ms ptimo


Saber qu queremos hacer Saber cul es la manera ms ptima Saber qu hace mysql y por qu MySQL slo utilizar un ndice por tabla MySQL procesar un JOIN realizando el menor nmero de accesos
empezando por la tabla con un ndice ms optimo

3 - Optimizacin de consultas Metodologa

Metodologa para elegir el ndice ms ptimo


Saber qu queremos hacer Saber cul es la manera ms ptima Saber qu hace mysql y por qu MySQL slo utilizar un ndice por tabla MySQL procesar un JOIN realizando el menor nmero de accesos
empezando por la tabla con un ndice ms optimo dictado por las condiciones WHERE

3 - Optimizacin de consultas Metodologa

Metodologa para elegir el ndice ms ptimo


Saber qu queremos hacer Saber cul es la manera ms ptima Saber qu hace mysql y por qu MySQL slo utilizar un ndice por tabla MySQL procesar un JOIN realizando el menor nmero de accesos
empezando por la tabla con un ndice ms optimo dictado por las condiciones WHERE y utilizando claves para seleccionar los registros de otras tablas

3 - Optimizacin de consultas Metodologa

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

3 - Optimizacin de consultas Metodologa

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 - Optimizacin de consultas Metodologa

II Dibujar Tablas y Relaciones


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

3 - Optimizacin de consultas Metodologa

II Dibujar Tablas y Relaciones


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

City

Country

3 - Optimizacin de consultas Metodologa

II Dibujar Tablas y Relaciones


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

City

Country

3 - Optimizacin de consultas Metodologa

II Dibujar Tablas y Relaciones


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

City
CountryCode Code

Country

3 - Optimizacin de consultas Metodologa

III Aadir columnas de SELECT y WHERE


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

City
CountryCode Code

Country

3 - Optimizacin de consultas Metodologa

III Aadir columnas de SELECT y WHERE


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

City
CountryCode Code

Country
Code = "ESP"

3 - Optimizacin de consultas Metodologa

III Aadir columnas de SELECT y WHERE


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

City
CountryCode Code Name

Country
Code = "ESP" Name

3 - Optimizacin de consultas Metodologa

IV Aadir ndices
SELECT City.Name, Country.Name FROM City, Country WHERE Country.Code = City.CountryCode AND Country.Code = "ESP";

City
CountryCode
PK PK

Country
Code = "ESP" Name Code

Name

3 - Optimizacin de consultas Metodologa

V Elegir el mejor plan de actuacin

SELECT

City.Name, Country.Name

City
CountryCode
PK PK

Country
Code = "ESP" Name Code

FROM

City, Country

WHERE Country.Code = City.CountryCode AND Country.Code = "ESP";

Name

3 - Optimizacin de consultas Metodologa

V Elegir el mejor plan de actuacin


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

SELECT

City.Name, Country.Name

City
CountryCode
PK PK

Country
Code = "ESP" Name Code

FROM

City, Country

WHERE Country.Code = City.CountryCode AND Country.Code = "ESP";

Name

3 - Optimizacin de consultas Metodologa

V Elegir el mejor plan de actuacin


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

SELECT

City.Name, Country.Name

City
CountryCode
PK PK

Country
Code = "ESP" Name Code

FROM

City, Country

WHERE Country.Code = City.CountryCode AND Country.Code = "ESP";

Name

3 - Optimizacin de consultas Metodologa

V Elegir el mejor plan de actuacin


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

SELECT

City.Name, Country.Name

City
CountryCode
PK PK

Country
Code = "ESP" Name Code

FROM

City, Country

WHERE Country.Code = City.CountryCode AND Country.Code = "ESP";

Name

3 - Optimizacin de consultas Metodologa

V Elegir el mejor plan de actuacin


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

SELECT

City.Name, Country.Name

City
CountryCode
PK PK

Country
Code = "ESP" Name Code

FROM

City, Country

WHERE Country.Code = City.CountryCode AND Country.Code = "ESP";

Name

3 - Optimizacin de consultas Metodologa

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: *********** 2. row table: City type: ALL key: NULL rows: 4079 Extra: Using where PK **********

City
CountryCode Code
PK

Country
Code = "ESP" Name

Name

3 - Optimizacin de consultas Metodologa

V Cotejar con EXPLAIN


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

Plan B Country City Seleccionar Code = "ESP" de Country usando PK Por cada registro en Country (1): Recorre secuencialmente City Seleccionar la fila slo si CountryCode = Code De las filas seleccionadas, leer: City.Name (de la tabla) Country.Name (de la tabla)

table: Country type: const key: PRIMARY rows: 1 Extra: *********** 2. row table: City type: ALL key: NULL rows: 4079 Extra: Using where **********

City
CountryCode
PK PK

Country
Code = "ESP" Name Code

Name

3 - Optimizacin de consultas Metodologa

Mi consulta es todo lo ptima que podra? Termin de optimizar S

No Optimizar Explain sigue mi plan de ejecucin? S No Adaptar

3 - Optimizacin de consultas Metodologa

VII Optimizar el uso de ndices


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

Qu columnas aadir a un ndice? Columnas JOIN


En la 2 tabla deben ser la 1 columna de una clave

Condiciones WHERE

Country
PK

Se deben escribir sobre columnas indexadas, o Crear un ndice que contenga al resto de columnas

Code = "ESP" Name

PK Code

Columnas SELECT
Seleccionar slo las necesarias Si todas son parte del ndice mejora el rendimiento

CountryCode

City
Name

Dividir la consulta en trozos ms pequeos


Ayuda a saber qu parte tiene peor rendimiento

3 - Optimizacin de consultas Metodologa

VII Optimizar el uso de ndices


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

Qu columnas aadir a un ndice? Columnas JOIN


En la 2 tabla deben ser la 1 columna de una clave

Condiciones WHERE

Country
PK

Se deben escribir sobre columnas indexadas, o Crear un ndice que contenga al resto de columnas

Code = "ESP" Name

PK Code

Columnas SELECT
Seleccionar slo las necesarias Si todas son parte del ndice mejora el rendimiento

CountryCode

City
Name

Dividir la consulta en trozos ms pequeos


Ayuda a saber qu parte tiene peor rendimiento

3 - Optimizacin de consultas Metodologa

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 - Optimizacin de consultas 3.5 - Optimizacin con Subconsultas

3 - Optimizacin de consultas Optimizacin 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 ************* id: 1 select_type: PRIMARY table: City type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 4079 Extra: Using where ************** 2. row ************** id: 2 select_type: DEPENDENT SUBQUERY table: Country type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 239 Extra: Using where

Las subconsultas no correlativas son aquellas que se pueden ejecutar de manera independiente a la consulta principal.

3 - Optimizacin de consultas Optimizacin con subconsultas

Consultas no correlativas
EXPLAIN SELECT Name FROM City, ( SELECT ) co WHERE City.ID = co.Capital\G ******* 1. row ******* id: 1 select_type: PRIMARY table: <derived2> type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 46 Extra: ******* 2. row ******* id: 1 select_type: PRIMARY table: City type: eq_ref possible_keys: PRIMARY key: PRIMARY key_len: 4 ref: co.Capital rows: 1 Extra: ******* 3. row ******* id: 2 select_type: DERIVED table: Country type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 239 ... Capital FROM Country WHERE Country.Continent = 'Europe'

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

3 - Optimizacin de consultas Optimizacin con subconsultas

Consultas no correlativas
EXPLAIN SELECT FROM Name City, Country

WHERE City.ID = Country.Capital AND Country.Continent = 'Europe'\G ************* 1. row ************* id: 1 select_type: SIMPLE table: Country type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 239 Extra: Using where ************** 2. row ************** id: 1 select_type: SIMPLE table: City type: eq_ref possible_keys: PRIMARY key: PRIMARY key_len: 4 ref: world.Country.Capital rows: 1 Extra:

A veces se pueden reescribir como un JOIN

3 - Optimizacin de consultas Optimizacin con subconsultas

Funciones de agregacin
SELECT FROM Name, Population Country, (SELECT MAX(Population) max_pop FROM Country) co WHERE Country.Population = co.max_pop;

Y otras, como cuando se usan funciones de agregacin, no.

3 - Optimizacin de consultas Optimizacin con subconsultas

Condiciones OR
SELECT FROM ci.Name 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 - Optimizacin de consultas Optimizacin 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 - Optimizacin de consultas 3.6 - Optimizacin usando cachs propias

3 - Optimizacin de consultas Optimizacin usando cachs propias

City Name

Country 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 - Optimizacin de consultas Optimizacin usando cachs propias

City Name

Country Continent Name

City_Country City_Name Continent 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 - Optimizacin de consultas Optimizacin usando cachs propias

City Name

Country Continent Name

City_Country City_Name Continent 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 - Optimizacin de consultas 3.7 - Optimizacin con MySQL 5.5

3 - Optimizacin de consultas MySQL 5.5

MySQL 5.5

Source: http://datacharmer.blogspot.com/2009/04/mysql-54-performance-with-logging.html

3 - Optimizacin de consultas MySQL 5.5

MySQL 5.5

x 1.59

3 - Optimizacin de consultas MySQL 5.5

MySQL 5.5

Actualizar es un cambio "fcil" de llevar a cabo.

3 - Optimizacin de consultas 3.7 - Optimizacin con MySQL 5.5 3.7.1 - Particionado

3 - Optimizacin de consultas Particionado

Particionado
http://www.slideshare.net/datacharmer/mysql-partitions

3 - Optimizacin de consultas 3.7 - Optimizacin con MySQL 5.5 3.7.2 - Triggers

3 - Optimizacin de consultas Triggers

Triggers
Cdigo que se ejecuta: ANTES DESPUS de INSERTAR MODIFICAR BORRAR

3 - Optimizacin 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 - Optimizacin de consultas Triggers

Triggers
Auditora Forzar Integridad Mantener cachs

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

Un trigger apenas afecta al rendimiento en comparacin a un ndice.

1- Introduccin. Por qu optimizar? 2- Arquitectura de MySQL 3- Optimizacin 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 FROM SUM(salary) salaries

WHERE from_date BETWEEN '1999-01-01' AND '2000-01-01';

Ejercicio III (solucin)


SELECT FROM SUM(salary) 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 (solucin)
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- Introduccin. Por qu optimizar? 2- Arquitectura de MySQL 3- Optimizacin de consultas 4- Ejercicios

5.- Ruegos y Preguntas

!
Curso MySQL Performance Tuning
Del 2 al 5 de Junio training@warp.es

!
Gracias!

Optimizacin del rendimiento con MySQL ( deCharlas Mayo 2012)


Vctor Jimnez Cerrada <vjimenez@warp.es> @capitangolo http://slideshare.net/capitangolo

Bienvenidos a "Optimizacin del rendimiento con MySQL". Soy Vctor Jimnez y ser vuestro ponente para hoy.

S-ar putea să vă placă și