Está en la página 1de 17

MySql: ndices y optimizacin de consultas

Hacer que una consulta trabaje es una cosa, pero obtener una consulta que trabaje lo
ms rpidamente es otra muy diferente. Podemos acelerar nuestras consultas de dos
maneras bsicamente, una de ellas es afinando nuestro servidor para que responda lo
mejor posible, y la otra, que es la que trataremos en este artculo, es haciendo uso de
los ndices de una manera inteligente. Los ndices son usados para encontrar
rpidamente los registros que tengan un determinado valor en alguna de sus
columnas. Sin un ndice, MySQL tiene que iniciar con el primer registro y leer a travs
de toda la tabla para encontrar los registros relevantes. An en tablas pequeas, de
unos 1000 registros, es por lo menos 100 veces ms rpido leer los datos usando un
ndice, que haciendo una lectura secuencial. Cuando MySQL trata de responder una
consulta, examina una variedad de estadsticas acerca de nuestros datos y decide
como buscar los datos que deseamos de la manera ms rpida. Sin embargo, como se
acaba de mencionar, cuando en una tabla no existen ndices en los cuales pueda
auxiliarse MySQL para resolver una consulta se tendrn que leer todos los registros de
la tabla de manera secuencial. Esto es comnmente llamado un "escaneo completo de
una tabla", y es muchas veces algo que se debe evitar. En particular, debemos evitar
las escaneos completos de tablas por las siguientes razones:

Sobrecarga de CPU. El proceso de checar cada uno de los registros en una tabla
es insignificante cuando se tienen pocos datos, pero puede convertirse en un
problema a medida que va aumentando la cantidad de registros en nuestra
tabla. Existe una relacin proporcional entre el nmero de registros que tiene
una tabla y la cantidad de tiempo que le toma a MySQL revisarla
completamente.
Concurrencia. Mientras MySQL est leyendo los datos de una tabla, ste la
bloquea, de tal manera que nadie ms puede escribir en ella, aunque si pueden
leerla. Cuando MySQL est actualizando o eliminando filas de una tabla, ste la
bloquea, y por lo tanto nadie puede al menos leerla.
Sobrecarga de disco. En una tabla muy grande, un escaneo completo consume
una gran cantidad de entrada/salida en el disco. Esto puede alentar
siginificativamente nuestro servidor de bases de datos, especialmente si
tenemos un disco IDE algo antiguo.

En resumen, lo mejor es tratar de que los escaneos completos de tablas sean mnimos
-- especialmente si nuestra aplicacin necesita escalabilidad en tamao, nmero de
usuarios, o ambos. Las versiones actuales de MySQL hacen distintas mejoras en cuanto
a concurrencia, pero ese tema est ms all de nuestra discusin. Es en estos casos
donde la indexacin puede ayudarnos. De manera simple, un ndice le permite a
MySQL determinar si un valor dado coincide con cualquier fila en una tabla. Cuando
indexamos una columna en particular, MySQL crea otra estructura de datos (un ndice)
que usa para almacenar informacin extra acerca de los valores en la columna
indexada. Los valores indexados son llamados frecuentemente claves. Aunque esta es
la manera simple de explicar los ndices, realmente es un poco ms complejo, ya que
1

MySQL almacena todas las claves del ndice en una estructura de datos de rbol. Esta
estructura de datos de rbol le permite a MySQL encontrar claves muy rpidamente.
Cuando MySQL encuentre que hay un ndice en una columna, lo usar en vez de hacer
un escaneo completo de la tabla. Esto reduce de manera imporante los tiempos de
CPU y las operaciones de entrada/salida en disco, a su vez que se mejora la
concurrencia porque MySQL bloquear la tabla nicamente para obtener las filas que
necesite (en base a lo que encontr en el ndice). Cuando tenemos grandes cantidades
de datos en nuestras tablas, la mejora en la obtencin de los datos puede ser muy
significativa.

Creacin de ndices
Existen cuatro tipos de ndices que podemos utilizar en MySQL; de clave primaria,
nicos, de texto completo, y ordinarios. Cada uno de ellos ser explicado a
continuacin.
ndices de clave primaria
Una clave primaria es un ndice sobre uno o ms campos donde cada valor es nico y
ninguno de los valores son NULL.
Para crear un ndice de clave primaria tenemos bsicamente dos opciones:
1. Crear el ndice de clave primaria al momento de crear la tabla. En este caso se usa la
opcin PRIMARY KEY al final de la definicin de los campos, con una lista de los campos
que sern parte del ndice.
CREATE TABLE nombreTabla(campo1 tipoDato,
[campo2...,] PRIMARY KEY (campo1 [,campo2...]) );
Hacemos nfasis en que la palabra clave NOT NULL es obligatoria para un campo
cuando ste vaya a formar parte de una clave primaria; como mencionamos
anteriormente, las claves primarias no pueden contener valores nulos. Si intentamos
crear una clave primaria sobre un campo nulo, MySQL nos marcar un error.
2. Crear una clave primaria en una tabla existente con el uso del comando ALTER
TABLE:

ALTER TABLE nombreTabla ADD PRIMARY KEY(campo1 [,campo2...]);


Por ejemplo, suponiendo que ya tenemos en nuestro sistema una tabla que fue creada
de la siguiente manera (sin clave primaria, y con el campo id aceptando valores NUL):
CREATE TABLE usuarios(id int, nombre varchar(50), apellidos varchar(70));
Podemos crear una clave primaria sobre el campo id con esta sentencia:
2

ALTER TABLE usuarios MODIFY id INT NOT NULL, ADD PRIMARY KEY(id);
Para observar los cambios que hemos hecho, podemos examinar las columnas de la
tabla usuarios con una sentencia DESCRIBE:
mysql> DESCRIBE usuarios;
+-----------+-------------+------+-----+---------+-------+
| Field | Type
| Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+-------+
| id
| int(11) | | PRI | 0
|
|
| nombre | varchar(50) | YES | | NULL |
|
| apellidos | varchar(70) | YES | | NULL |
|
+-----------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

El campo id no tiene un valor YES en la columna Null, lo que indica que este campo ya
no podr almacenar valores nulos. Se puede observar tambin que se tiene un valor
PRI en la columna Key, lo que indica que este campo es una clave primaria.
Las claves primarias pueden constar de ms de un campo. Hay algunas veces en las
que un solo campo no puede identificar de manera nica a un registro.
ndices ordinarios
Un ndice que no es primario permite valores duplicados (a menos que los campos
hayan sido especificados como UNIQUE).
Para crear un ndice ordinario tenemos bsicamente dos opciones:
1. Podemos crear un ndice ordinario al mismo tiempo que creamos la tabla con el uso
de la opcin INDEX.

CREATE TABLE nombreTabla(campo1 tipoDato, campo2 tipoDato,..


INDEX [nombreIndice] (campo1 [,campo2...]));
2. De igual manera, podemos crear el ndice con el uso de la sentencia ALTER TABLE si
es que la tabla ya existe.
ALTER TABLE nombreTabla ADD INDEX [nombreIndice] (campo1 [,campo2...]);
Tambin es posible usar la sentencia CREATE INDEX para crear un ndice en una tabla
existente.
CREATE INDEX nombreIndice ON nombreTabla(campo1 [,campo2...]);

Ambas sentencias piden el nombre del ndice, sin embargo con la sentencia CREATE
INDEX el nombre es obligatorio.
Por ejemplo, para la siguiente definicin de tabla:

CREATE TABLE usuarios(id int, nombre varchar(50), apellidos varchar(70));


Se puede crear un ndice en la columna apellidos con una sentencia ALTER TABLE:
ALTER TABLE usuarios ADD INDEX idx_apellidos (apellidos);
O bien, con una sentencia CREATE INDEX:
CREATE INDEX idx_apellidos ON usuarios(apellidos);

ndices de texto completo


Los ndices de texto completo son del tipo FULLTEXT, se usan en tablas del tipo
MyISAM, y pueden contener uno o ms campos del tipo CHAR, VARCHAR y TEXT. Un
ndice de texto completo est diseado para facilitar y optimizar la bsqueda de
palabras clave en tablas que tienen grandes cantidades de informacin en campos de
texto.
Para crear un ndice de texto completo tenemos bsicamente dos opciones:
1. Crear el ndice al momento de crear la tabla.
CREATE TABLE nombreTabla( campo1 TIPO, campo2 TIPO,
FULLTEXT [nombreIndice] (campo1 [campo2,...]) );

2. Crear el ndice una vez que ha sido creada la tabla.


ALTER TABLE nombreTabla ADD FULTEXT [nombreIndice] (campo1 [,campo2,...]);

La siguiente sentencia tambin se puede usar para crear un ndice cuando la tabla ya
existe.
CREATE FULLTEXT INDEX nombreIndice ON nombreTabla(campo1 [,campo2,...]);

UNICAMENTE para fines ilustrativos, consideremos la siguiente definicin de tabla:

CREATE TABLE usuarios(id int, nombre varchar(50), apellidos varchar(70));


Podramos crear un ndice FULLTEXT en la columna nombre, en la columna apellidos, o
bien, un ndice que ocupe ambos campos. A continuacin se muestran los tres casos.
CREATE FULLTEXT INDEX idx_nombre ON usuarios(nombre);
CREATE FULLTEXT INDEX idx_apellidos ON usuarios(apellidos);
CREATE FULLTEXT INDEX idx_nombre_apellidos ON usuarios(nombre,apellidos);

- Nota: Cuando se tienen grandes cantidades de datos, es mucho ms rpido cargar los
datos en una tabla que no tiene ndices de texto completo y despus crear los ndices
necesarios, ya que la carga de datos en una tabla que ya tiene ndices de este tipo es
un proceso lento.
ndices nicos
Los ndices nicos son bsicamente como los ndices ordinarios, excepto que los
valores duplicados no son permitidos.
Para crear un ndice UNIQUE se tienen bsicamente dos opciones:
1. Crear un ndice nico cuando la tabla es creada con el uso de la opcin UNIQUE.
CREATE TABLE nombreTabla(campo1 tipoDato, campo2 tipoDato,..
UNIQUE [nombreIndice] (campo1 [,campo2...]));
2. O bien, si la tabla ya existe, se usa la sentencia ALTER TABLE.
ALTER TABLE nombreTabla ADD UNIQUE [nombreIndice] (campo1, campo2) ...
De igual manera, tambin es posible usar la sentencia CREATE INDEX para crear un
ndice nico en una tabla existente.
CREATE UNIQUE INDEX nombreIndice ON nombreTabla(campo1 [,campo2...]);

UNICAMENTE para fines ilustrativos, consideremos de nuevo la siguiente definicin de


tabla:
CREATE TABLE usuarios(id int, nombre varchar(50), apellidos varchar(70));
Podramos crear un ndice UNIQUE en la columna nombre, y un ndice UNIQUE en la
columna apellidos.
ALTER TABLE usuarios ADD UNIQUE idx_nombre (nombre);
CREATE UNIQUE INDEX idx_apellidos ON usuarios(apellidos);
En el primer caso hacemos uso del comando ALTER TABLE, y en el segundo caso
creamos el ndice con la sentencia CREATE INDEX.
5

ndices compuestos
Los ndices compuestos son simplemente aquellos que estn basados en mltiples
columnas. MySQL nicamente usa un ndice por tabla cuando est procesando una
consulta. Esto significa que si tenemos varias columnas que frecuentemente aparecen
juntas en una clusula WHERE, tenemos la oportunidad de acelerar estas consultas al
crear un ndice compuesto.
Si una tabla tiene un ndice formado por mltiples columnas, cualquier prefijo ms a la
izquierda puede ser usado por el optimizador de consultas de MySQL para encontrar
las filas. Por ejemplo, si tenemos un ndice compuesto por tres columnas (col1, col2,
col3), tendramos capacidades de bsqueda en (col1), (col1, col2) y (col1, col2, col3).
MySQL no puede usar un ndice parcial si las columnas no forman un prefijo ms a la
izquierda del ndice. Supongamos que tenemos unas sentencias SELECT como estas:
mysql> SELECT * FROM algunaTabla WHERE col1=valor1;
mysql> SELECT * FROM algunaTabla WHERE col2=valor2;
mysql> SELECT * FROM algunaTabla WHERE col2=valor2 AND col3=valor3;
Si el ndice existe en (col1, col2, col3), slo la primera de estas consultas usar el
ndice. La segunda y la tercera involucran a las columnas en el ndice, pero (col2) y
(col2, col3) no son los prefijos ms a la izquierda de (col1, col2, col3).
Este es otro ejemplo. Consideremos la siguiente definicin de una tabla:
CREATE TABLE usuarios(id int, nombre varchar(50), apellidos varchar(70));
Si frecuentemente hacemos consultas en la tabla usuarios basadas en el nombre como
en los apellidos, podemos beneficiarnos de un ndice compuesto en las columnas
nombre y apellidos.
ALTER TABLE usuarios ADD INDEX idx_nombre(nombre, apellidos);

Debido a la forma en que MySQL construye los ndices compuestos, ste puede usar el
ndice idx_nombre para resolver consultas basadas slo en el nombre, o en el nombre
y los apellidos, sin embargo, no usar el ndice en una consulta que haga referencia
nicamente a la columna apellidos.
Por ejemplo, de las siguientes tres consultas, slo las dos primeras haran uso de
nuestro ndice idx_nombre.
SELECT * FROM usuarios WHERE nombre='Eduardo';
SELECT * FROM usuarios WHERE nombre='Eduardo' AND apellidos='Zarate M';
SELECT * FROM usuarios WHERE apellidos='Zarate M';
6

La idea es que los ndices compuestos pueden usarse frecuentemente para acelerar
algunas consultas complejas, pero necesitamos entender sus limitaciones y debemos
ejecutar algn tipo de prueba en vez de asumir que estos ndices siempre nos van a
ayudar.
ndices de parte de campos
En las columnas CHAR y VARCHAR se nos permite crear un ndice que no use el campo
por completo. Retomemos el ejemplo anterior de la tabla usuarios. A pesar de que el
nombre de una persona puede ser de hasta 50 caracteres, es muy comn que los
nombres de las personas sean diferentes en los primeros 10 caracteres. Al usar un
ndice de 10 caracteres en lugar de 50, el ndice ser ms pequeo, y permitir que las
consultas INSERT y UPDATE sean ms rpidas, a la vez que no se afecta la velocidad de
las consultas SELECT.
Para crear un ndice como parte de un campo, slo se tiene que especificar el tamao
entre parntesis despus del nombre de la columna. Por ejemplo, nuestro ndice
idx_nombre pudo haber sido creado tambin de la siguiente manera:
ALTER TABLE usuarios ADD INDEX idx_nombre(nombre(10), apellidos(20));

Eliminar o cambiar un ndice


Algunas veces tendremos la necesidad de cambiar o eliminar un ndice. Cuando
hagamos algn cambio en el ndice, necesitamos eliminar primero el ndice y entonces
reconstruirlo con la nueva definicin.
Para eliminar un ndice de clave primaria podemos usar la siguiente sintaxis:

Procesamiento de consultas
Las reglas que usa MySQL para decidir como obtener los datos de una consulta pueden
llegar a ser difciles de entender si dichas consultas son algo complejas.
Afortunadamente hay unas pocas reglas y un comando que nos permiten tener un
mejor entendimiento de qu es lo que est haciendo MySQL. Primero vamos a
comentar las reglas:

MySQL no usar un ndice si decide que ser mucho ms rpido escanear


completamente una tabla. En general, si un ndice le hace saber a MySQL que
accesar aprximadamente el 30 por ciento de las filas de una tabla, MySQL
abandona el ndice y simplemente ejecuta un escaneo completo de la tabla.
Si mltiples ndices pueden ser usados para satisfacer una consulta, MySQL
usar el que sea ms restrictivo -- esto es, con el que se obtengan el menor
nmero de filas.
Si las columnas que estamos seleccionando forman parte de un ndice, MySQL
puede leer todos los datos que necesitamos desde el ndice y nunca tocar la
tabla en s.
Cuando usamos varias tablas en una consulta, MySQL leer primero los datos
desde la tabla que regrese el menor nmero de filas. El orden en el que se
especifican las tablas puede no ser el mismo que use MySQL. Esto afecta
tambin el orden en el que son regresados finalmente los registros, as que
debemos asegurarnos de usar una clusula ORDER BY si necesitamos que los
registros tengan un orden en particular.

Habiendo dicho esto, es importante tener en cuenta que algunas de las desiciones que
toma MySQL estn basadas en suposiciones, y al igual que nosotros los humanos que
hacemos muchas suposiciones, puede que MySQL ocasionalmente haga alguna que
sea incorrecta.
Si sospechamos que esto ha sucedido, o simplemente queremos entender qu es lo
que est haciendo MySQL para procesar una consulta, podemos usar el comando
EXPLAIN. La seccin a continuacin explica ms a detalle el uso de EXPLAIN.
Analizando como se usan los ndices
EXPLAIN muestra (explica) como son procesadas las sentencias SELECT por MySQL,
como se usan los ndices, y como se unen las tablas. Utilizar EXPLAIN puede ayudarnos
a seleccionar mejores ndices y escribir nuestras consultas ms ptimamente. Lo nico
que tenemos que hacer es agregar la palabra EXPLAIN al inicio de la consulta para que
MySQL nos diga como la est ejecutando. En vez de ejecutar la consulta, MySQL
reportar la lista de ndices que se podran usar en la consulta y lo que conoce acerca
de ellos.
EXPLAIN SELECT nombre, apellidos FROM usuarios WHERE id = 1;
A continuacin explicaremos que significa cada una de estas columnas.
ALTER TABLE nombreTabla DROP PRIMARY KEY;
Para eliminar un ndice ordinario, nico, o de texto completo, necesitamos especificar
el nombre del ndice y usar esta sintaxis:
ALTER TABLE nombreTabla DROP INDEX nombreIndice;
8

Tambin es vlida esta otra sintaxis:


DROP INDEX nombreIndice ON nombreTabla;
Si no estamos seguros de cul es el nombre del ndice que deseamos eliminar,
podemos hacer uso de la sentencia SHOW KEYS:
SHOW KEYS FROM nombreTabla;
Este es un ejemplo.
CREATE TABLE usuarios
(
id INT NOT,
nombre VARCHAR(50) NOT NULL,
apellidos VARCHAR(70) NOT NULL,
PRIMARY KEY (id),
INDEX (nombre, apellidos)
);
Veamos los ndices que existen en esta tabla:
mysql> SHOW KEYS FROM usuarios;
+----------+------------+----------+--------------+-------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name |
+----------+------------+----------+--------------+-------------+ .
| usuarios |
0 | PRIMARY |
1 | id
| .
| usuarios |
1 | nombre |
1 | nombre | .
| usuarios |
1 | nombre |
2 | apellidos |
+----------+------------+----------+--------------+-------------+
3 rows in set (0.00 sec)
La tercera columna es la que nos proporciona los nombres de los ndices. Podemos
observar que al no especificar un nombre al ndice ordinario en (nombre, apellidos), se
le ha asignado el nombre de la primera columna que forma el ndice.
A continuacin vamos a eliminar los dos ndices que existen en esta tabla:
mysql> ALTER TABLE usuarios DROP PRIMARY KEY;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> ALTER TABLE usuarios DROP INDEX nombre;
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
Por ltimo, podemos verificar que estos ndices ya no existen:
mysql> SHOW KEYS FROM usuarios;
Empty set (0.00 sec)

Usando ndices efectivamente


Dado que los ndices hacen que las consultas se ejecuten ms rpido, podemos estar
incitados a indexar todas las columnas de nuestras tablas. Sin embargo, lo que
tenemos que saber es que el usar ndices tiene un precio. Cada vez que hacemos un
INSERT, UPDATE, REPLACE, o DELETE sobre una tabla, MySQL tiene que actualizar
cualquier ndice en la tabla para reflejar los cambios en los datos.
As que, cmo decidimos usar ndices o no?. La respuesta es "depende". De manera
simple, depende que tipo de consultas ejecutamos y que tan frecuentemente lo
hacemos, aunque realmente depende de muchas otras cosas.
La razn para tener un ndice en una columna es para permitirle a MySQL que ejecute
las bsquedas tan rpido como sea posible (y evitar los escaneos completos de tablas).
Podemos pensar que un ndice contiene una entrada por cada valor nico en la
columna. En el ndice, MySQL debe contar cualquier valor duplicado. Estos valores
duplicados decrementan la eficiencia y la utilidad del ndice.
As que antes de indexar una columna, debemos considerar que porcentaje de
entradas en la tabla son duplicadas. Si el porcentaje es demasiado alto, seguramente
no veremos alguna mejora con el uso de un ndice.
Otra cosa a considerar es qu tan frecuentemente los ndices sern usados. MySQL
puede usar un ndice para una columna en particular nicamente si dicha columna
aparece en la clusula WHERE en una consulta.
Si muy rara vez usamos una columna en una clusula WHERE, seguramente no tiene
mucha sentido indexar dicha columna. De esta manera, probablemente sea ms
eficiente sufrir el escaneo completo de la tabla las raras ocasiones en que se use esta
columna en una consulta, que estar actualizando el ndice cada vez que cambien los
datos de la tabla.
Ante la duda, no tenemos otra alternativa que probar. Siempre podemos ejecutar
algunas pruebas sobre los datos de nuestras tablas con y sin ndices para ver como
obtenemos los resultados ms rpidamente. Lo nico a considerar es que las pruebas
sean lo ms realistas posibles.
Columna

Descripcin

table

La tabla a la que se refieren las dems columnas en esta tabla.

type

El tipo de unin que se est usando. Desde la mejor hasta la


peor, los tipos de uniones son system, const, eq_ref, ref,
range, index, y ALL.
system

La tabla tiene slo una fila.

10

const

La tabla tiene como mximo una fila que


coincide, la cual ser leda en el inicio de la
consulta. Ya que hay slo una fila, los valores de
la columna en esta fila pueden ser considerados
como constantes por el optimizador. Las
tablas const son muy rpidas ya que son ledas
slo una vez. const es usado cuando se
comparan todas las partes de una clave
PRIMARY/UNIQUE con constantes.

eq_ref

Una fila ser leda de esta tabla por cada


combinacin de filas de las tablas previas. Este
es usado cuando todas las partes de un ndice
son usadas por la consulta y el ndice es UNIQUE
o PRIMARY KEY.

ref

Todas las filas con valores en el ndice que


coincidan sern ledos desde esta tabla por cada
combinacin de filas de las tablas previas. ref es
usado si la consulta usa slo un prefijo ms a la
izquierda de la clave, o si la clave no es UNIQUE
o PRIMARY KEY. Si la clave que es usada
coincide slo con pocas filas, esta union es
buena.

range

Slo sern recuperadas las filas que estn en un


rango dado, usando un ndice para seleccionar
las filas. La columna key indica cual ndice es
usado, y el valor key_len contiene la parte ms
grande de la clave que fue usada. La columna
ref ser NULL para este tipo.

index

Este es el mismo que ALL, excepto que slo el


ndice es escaneado. Este es usualmente ms
rpido que ALL, ya que el ndice es usualmente
de menor tamao que la tabla completa.

ALL

Un escaneo completo de tabla ser hecho por


cada combinacin de filas de las tablas previas.
Este es normalmente no bueno si la tabla es la
primera no marcada const, y usualmente muy
malo en todos los otros casos.

possible_keys Los posibles ndices que pueden aplicar a la tabla. Si est vaca
esta celda, no hay posibles ndices a utilizar.
key

El ndice que ha sido seleccionado. Si tiene un valor NULL,


entonces ningn ndice ser utilizado.

key_len

La longitud del ndice usado. Entre ms pequeo sea este


valor, mejor.
11

ref

Las columnas del ndice que se est usando, o una constante


si esta es posible.

rows

Nmero de filas que consider MySQL debe analizar para


regresar los datos requeridos.

extra

Informacin extra acerca de como MySQL resolver la


consulta. Aqu se muestra una explicacin de los posibles
textos que podemos encontrar en esta columna.
Distinct

Una vez que MySQL ha encontrado una fila que


coincida con la combinacin de filas, ste no
buscar ms.

Not exists

MySQL fue capaz de hacer una optimizacin


LEFT JOIN sobre la consulta y no examinar ms
filas en la tabla para la combinacin de filas
previa despus de que encuentre una fila que
coincida con el criterio LEFT JOIN.

range
checked
for each
record
(index
map: #)

MySQL no encontr un buen ndice que usar, as


que para cada combinacin de filas en las tablas
precedentes, har un chequeo en cual ndice
usar (si hay alguno), y usar este ndice para
recuperar las filas desde la tabla. Esto no es lo
ms rpido, pero es mejor que hacer un join sin
un ndice.

Using
filesort

Cuando veamos esto, la consulta necesita ser


optimizada. MySQL necesita hacer un paso extra
para encontrar la forma de ordernar las filas que
sern regresadas.

Using
index

La informacin de las columnas es recuperada


desde la tabla usando slo informacin en el
ndice, sin tener que leer la fila actual. Esto
sucede cuando todas las columnas requeridas
son parte del mismo ndice.

Using
Cuando veamos esto, la consulta necesita ser
temporary optimizada. Para resolver la consulta MySQL
necesita crear una tabla temporal para
mantener el resultado. Esto sucede tpicamente
cuando se hace un ORDER BY sobre un conjunto
de columnas diferente al usado en un GROUP
BY.
Where
used

Una clusula WHERE ser usada para restringir


cuales filas sern comparadas en contra de la
siguiente tabla o enviada al cliente. Si no
deseamos regresar todas las filas desde la tabla,

12

y el join es del tipo ALL o index, es muy probable


que hayamos escrito algo mal en la consulta.

Si deseamos obtener consultas que se ejecuten lo ms rpido


posible, debemos ser cuidadosos cuando veamos informacin
extra del tipo Using filesort o Using temporary.

Podemos obtener una buena indicacin de que tan buena es una consulta al
multiplicar todos los valores de la columna rows en la salida de EXPLAIN. Esto nos dice
aproximadamente cuntas filas debe examinar MySQL para ejecutar una consulta. De
lo que se trata es que podamos ir mejorando una consulta progresivamente usando la
informacin proporcionada por EXPLAIN.
Cmo evitar escaneos completos de tablas
La salida de EXPLAIN mostrar ALL en la columna type cuando MySQL hace un escaneo
de tabla para resolver una consulta. Esto sucede usualmente bajo las siguiente
condiciones:

La tabla es demasiado pequea que es ms rpido hacer el escaneo de la tabla


que buscar una ndice. Este es el caso comn para tablas con menos de 10 filas.
No hay restricciones usables en las clusulas ON o WHERE para las columnas
indexadas.
Se estn comparando columnas indexadas con valores constantes y MySQL ha
calculado que las constantes cubren una gran parte de la tabla y que el escaneo
completo ser ms rpido.
Se est usando una clave con baja cardinalidad (muchas filas que coinciden con
el valor clave) en contra de otra columna. En este caso, MySQL asume que el
usar el ndice probablemente se harn una gran cantidad de bsquedas
adicionales de claves y que un escaneo de la tabla ser ms rpido.

Para tablas pequeas, un escaneo de la tabla es frecuentemente apropiado. Para


tablas muy grandes, podemos intentar las siguientes tcnicas para evitar que el
optimizador de consultas de MySQL escoja incorrectamente un escaneo completo.

Usar ANALIZE TABLE para actualizar la distribucin de claves para la tabla


escaneada.
Usar FORCE INDEX en la tabla escaneada para decirle a MySQL que use el ndice
dado. Por ejemplo.

SELECT * FROM tabla1, tabla2 FORCE INDEX (indiceParaColumna)


WHERE tabla1.nombreColumna=tabla2.nombreColumna;
13

Optimizando consultas SELECT


En general, cuando deseamos hacer que una consulta SELECT ... WHERE se ejecute ms
rpido, lo primero que debemos checar es si podemos agregar un ndice. Todas las
referencias entre tablas diferentes deben usualmente ser hechas con ndices. Por
supuesto, debemos usar una sentencia EXPLAIN para determinar cules ndices estn
siendo usados para resolver la consulta.
Optimizando sentencias INSERT
El tiempo que le toma a MySQL insertar un registro est determinado por los
siguientes factores, donde los nmeros indican nicamente valores aproximados:

Establecer la conexin: (3)


Enviar la consulta al servidor: (2)
Analizar la consulta: (2)
Insertar el registro: (1 x tamao del registro)
Insertar ndices: (1 x nmero de ndices)
Cerrar: (1)

Esto no toma en consideracin la sobrecarga inicial de abrir las tablas, lo cual es hecho
una vez por cada consulta en ejecucin de manera concurrente.
El tamao de la tabla hace ms lenta la insercin de los ndices por log N, asumiendo
que los ndices son rboles B.
Por lo tanto, podemos usar los siguientes mtodos para lograr que los INSERTs se
ejecuten ms rpido:

Si estamos insertando muchas filas desde el mismo cliente al mismo tiempo,


usemos una sentencia INSERT con mltiples listas de valores para insertar
varias filas a la vez. Esto es mucho ms rpido que usar varias sentencias
INSERT de manera separada. Por ejemplo, la siguiente consulta:
INSERT INTO nombreTabla VALUES(registro1),(registro2),... (registroN);
es mucho ms rpida que esta alternativa:

INSERT INTO nombreTabla VALUES(registro1);


INSERT INTO nombreTabla VALUES(registro2);
...
INSERT INTO nombreTabla VALUES(registroN);
14

Si estamos insertando una gran cantidad de filas desde diferentes clientes,


podemos obtener una mayor velocidad al usar la sentencia INSERT DELAYED.
Cuando estemos cargando datos en una tabla a partir de un archivo de texto, lo
mejor es usar la sentencia LOAD DATA INFILE. Esto es usualmente 20 veces ms
rpido que usar una gran cantidad de sentencias INSERT.
Es posible hacer que LOAD DATA INFILE se ejecute an ms rpido cuando la
tabla tiene muchos ndices. El procedimiento a seguir es:
1. Deshabilitar los ndices con el uso de la sentencia ALTER TABLE nombreTabla
DISABLE KEYS;
2. Insertar los datos en la tabla con LOAD DATA INFILE. En este momento no se
actualizar ningn ndice y por lo tanto ser muy rpido cargar los datos.

3. Iniciar la creacin de los ndices necesarios con el uso de ALTER TABLE


nombreTabla ENABLE KEYS. Esto crear los ndices en memoria antes de
escribirlos en disco, lo cual es mucho ms rpido ya que se evita una gran
cantidad de lecturas y escrituras en disco. El rbol B del ndice es tambin
perfectamente balanceado.
Es un hecho que no siempre estamos en la posibilidad de insertar los datos a
partir de un archivo de texto, sin embargo, podemos acelerar las operaciones
INSERT que son hechas con mltiples sentencias al bloquear nuestras tablas:
LOCK TABLES nombreTabla WRITE;
INSERT INTO nombreTabla VALUES(registro1),(registro2),(registro3);
INSERT INTO nombreTabla VALUES(registro4),(registro5),(registro6);
...
INSERT INTO nombreTabla VALUES(registroN);
UNLOCK TABLES;

Un beneficio de eficiencia ocurre ya que el bffer del ndice es escrito a disco


slo una vez, despus de que todas las sentencias INSERT han sido
completadas. Normalmente hay tantas escrituras del bffer de un ndice a
disco como diferentes sentencias INSERT son ejecutadas.
El uso explicito de las sentencias de bloqueo (LOCK) no son necesarias si se
pueden insertar todos las filas con una sola sentencia INSERT. Para las tablas
transaccionales, debemos usar BEGIN/COMMIT en vez de LOCK TABLE para
obtener el mismo resultado.
El bloqueo tambin reduce el tiempo total en casos de conexiones mltiples, a
pesar de que el tiempo de espera mximo para conexiones individuales puede
incrementarse ya que se tiene que esperar para al desbloqueo. Por ejemplo:
o

La conexin 1 hace 1000 inserciones.


15

o
o

La conexin 2, 3 y 4 hacen una insercin.


La conexin 5 hace 1000 inserciones.

Si no estamos usando bloqueo, las conexiones 2,3 y 4 terminarn antes que la 1


y la 5. Usando bloqueo, las conexiones 2,3, y 4 probablemente no terminarn
antes de la 1 y la 5, pero el tiempo total debe ser aprximadamente de 40%
ms rpido.
Las sentencias INSERT, UPDATE y DELETE son muy rpidas en MySQL, pero
obtendremos una mejor eficiencia al agregar bloqueo cuando se ejecuten en
promedio ms de 5 inserciones o actualizaciones en una fila.

Optimizando sentencias UPDATE


Las sentencias UPDATE son optimizadas de manera similar a las sentencias SELECT con
la sobrecarga adicional de la escritura. Por ejemplo, para efectos de optimizacin, el
siguiente cdigo:
UPDATE nombreCampo FROM nombreTabla WHERE algunaCondicion
Es el mismo que este:
SELECT nombreCampo FROM nombreTabla WHERE algunaCondicion

Es decir, podemos optimizar una sentencia UPDATE de la misma forma que su


equivalente sentencia SELECT.
La velocidad de escritura depende de la cantidad de datos que estn siendo
actualizados y el nmero de ndices que son actualizados, por lo tanto debemos tener
cuidado de crear ndices que no sean verdaderamente tiles, o bien, hacer que los
campos de la tabla sean ms grandes de lo que realmente necesitamos.
Tambin, otra forma de obtener actualizaciones rpidas es retrasar los UPDATEs y
entonces hacer muchas actualizaciones en una fila posteriormente. Hacer muchas
actualizaciones en una fila es mucho ms rpido que hacer uno a la vez si se bloquea la
tablas.
Debemos notar que para una tabla MyISAM que usa el formato de registro dinmico,
el actualizar el registro a una longitud total ms grande puede dividir el registro. Si esto
llega a ocurrir, es muy importante usar el comando OPTIMIZE TABLE ocasionalmente.
Optimizando sentencias DELETE
16

El tiempo de eliminar registros individuales es exactamente proporcional al nmero de


ndices. Cuando eliminamos, cada registro necesita ser eliminado desde cualquier
ndice asociado, as como tambin del archivo principal de datos.
Si deseamos eliminar todos los registros de una fila, es preferible usar el comando
TRUNCATE TABLE en lugar de ejecutar la tradicional sentencia DELETE, ya que se borra
toda la tabla en una slo operacin, sin la necesidad de eliminar cada ndice y cada
registro de manera individual.
Para obtener mayor velocidad en las tablas MyISAM, tanto para las sentencias INSERT,
como para las sentencias DELETE, podemos hacer ms grande la cach de claves al
incrementar la variable de sistema key_buffer_size.

17