Optimización de MySQL

17
 1 MySql: Índices y optimización d e consultas Hacer que una consulta trabaje es una cosa, pero obtener una consulta que trabaje lo más rápidamente es otra muy diferente. Podemos acelerar nuestras consultas de dos maneras básicamente, una de ellas es afinando nuestro servidor para que responda lo mejor posible, y la otra, que es la que trataremos en este artículo, es haciendo uso de los índices de una manera inteligente. Los índices son usados para encontrar rápidamente 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 través de toda la tabla para encontrar los registros relevantes. Aún en tablas pequeñas, de unos 1000 registros, es por lo menos 100 veces más rápido leer los datos usando un índice, que haciendo una lectura secuencial. Cuando MySQL trata de responder una consulta, examina una variedad de e stadísticas acerca de nuestros datos y decide como buscar los datos que deseamos de la manera más rápida. 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 tendrán que leer todos los registros de la tabla de manera secuencial. Esto es comúnmente 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 relación proporcional entre el número 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 más 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 e l 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 mínimos -- especialmente si nuestra aplicación necesita escalabilidad en tamaño, número de usuarios, o ambos. Las versiones actuales de MySQL hacen distintas mejoras en cuanto a concurrencia, pero ese tema está más allá de nuestra discusión. Es en estos casos donde la indexación 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 información 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 más complejo, ya que

description

Conceptos de Optimización en MySQL

Transcript of Optimización de MySQL

  • 1

    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

  • 2

    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:

  • 3

    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...]);

  • 4

    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:

  • 5

    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.

  • 6

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

  • 7

    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:

  • 8

    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;

  • 9

    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)

  • 10

    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.

  • 11

    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.

  • 12

    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 temporary

    Cuando veamos esto, la consulta necesita ser 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,

  • 13

    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;

  • 14

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

  • 15

    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.

  • 16

    o La conexin 2, 3 y 4 hacen una insercin. o 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

  • 17

    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.