Rendimiento en MySQL I

03 jun. 2008
En Todoexpertos trabajamos con MySQL.

Antes de entrar en Avanzis ya había trabajado con esta base de datos, aunque no de forma tan intensa como con Todoexpertos. Siempre he usado SQLServer 2000, SQL Server 2005 y SQL Server 2005 Express, sobretodo porque su integración con ASP.NET es total. Incluso he hecho mis pinitos con Oracle... pero eso es harina de otro costal.

A pesar de mis reticencias iniciales, cada vez me gusta más MySQL. Al ser un proyecto OpenSource, la comunidad que lo rodea provee de un montón de herramientas extremadamente útiles.

Como os podréis imaginar, la carga que Todoexpertos le da a la base de datos es brutal, con lo que pequeños matices implican grandes efectos en el rendimiento.

Ha llegado a haber picos de peticiones SQL que han provocado el temible error "Sort Aborted", que viene a significar algo como "MySQL no puede aguantar tanto, y durantes los próximos minutos va a estar algo muerto".

Tras todo este tiempo lidiando con MySQL, quisiera dar algunos consejos para quien los quiera tomar (algunos de los cuales no sólo sirven para MySQL). Empecemos por el principio, y en próximos artículos hablaremos de otros.

La sentencia EXPLAIN
MySQL nos provee de la sentencia Explain, que nos ayudará enormemente a la hora de analizar cualquier sentencia SQL. Os recomiendo encarecidamente que la estudiéis. Éste es un buen comienzo.

Índices de la base de datos
Parece absurdo decirlo por ser demasiado obvio, pero lo tengo que decir: hay que configurar bien los índices. Y bien significa BIEN.

Imaginemos un tabla con 3 campos numéricos (a,b,c) y 100.000 registros.
El campo 'a' es la clave única, el 'b' es un índice y el 'c' no es nada.

Un análisis obvio
SQL buena: SELECT b FROM tabla WHERE a=100
SQL pésima: SELECT * FROM tabla WHERE c=100

En el primer caso ni tan siquiera entramos en la tabla, porque todos los datos que requerimos los tenemos en el índice.

En el segundo caso estamos recorriendo TODA la tabla en busca del campo 'c' que contenga el valor de 100: de principio a fin.

Algo menos obvio
SQL mala: SELECT * FROM tabla WHERE a>100 ORDER BY b DESC

¿Seguro que es mala? Tanto 'a' como 'b' son índices, pero MySQL no los aprovecha cuando uno está en el WHERE y el otro en el ORDER BY. Se nos hace necesario, en este caso, hacer un índice conjunto de 'a' y 'b'. La diferencia de rendimiento es espectacular.

SQL_CALC_FOUND_ROWS vs. COUNT(*)
Es muy común querer mostrar los resultados paginados. Para ello suele usarse la cláusula LIMIT y el OFFSET. Por ejemplo...

SELECT * FROM tabla WHERE a>100 LIMIT 10

... cogerá los 10 primeros elementos con a>100

Esto se correspondería con los 10 primeros elementos de la página 0. Así pues, también es muy común querer conocer la cantidad total de elementos en la tabla, de modo que podamos conocer el número total de páginas que podemos mostrarle al usuario.

El modo más inmediato de conocer el número total de items sería el siguiente:

SELECT COUNT(*) FROM tabla WHERE a>100

Aunque yo aconsejo éste:

SELECT COUNT(a) FROM tabla WHERE a>100

Sin embargo, usando este método estamos ejecutando dos sentencias SQL. Para evitar esto nació el SQL_CALC_FOUND.

Colocado al principio de la sentencia SELECT podemos recoger el número de filas afectadas por la petición:

SELECT SQL_CALC_FOUND * FROM tabla WHERE a>100 LIMIT 10

De modo que sin cerrar la conexión, accedemos al número de registrados usando SELECT FOUND_ROWS().

Bien, ¿no? nos ahorramos la ejecución de una sentencia SQL... PUES NO. De bien nada. Después de pelearme amargamente con el SQL_CALC_FOUND, y tras ver los comentarios de muchos desarrolladores, concluyo que usar COUNT(*) es mucho más rápido, siempre y cuando se usen los índices adecuados y siempre en la misma conexión que el SQL anterior (abrir y cerrar la conexión dos veces es innecesario y costoso).


Pues bueno, ahí queda dicho. Próximamente más.
comments powered by Disqus
subir