Una buena característica de MySQL es la compresión de los datos de tabla. Viene incluida con el motor InnoDB y siempre que tengas “innodb_file_per_table”. La situación ideal sería decidir qué y cómo comprimir en la fase de diseño de tu proyecto, pero rara vez la realidad es ideal.

En CAPSiDE nos llegó una alerta de uno de los miles de sistemas que gestionamos para nuestros clientes, y fue sobre espacio disponible en un sistema de archivos que alojaba datos MySQL.

Normalmente solemos gestionar infraestructura cloud, donde la expansión del disco tiende a ser sencilla. Amazon RDS, por ejemplo, hace que sea muy fácil expandir espacio en disco.

Esta vez nos encontrábamos en una infraestructura más tradicional donde no podíamos utilizar nuestros conjuros cloud. En este caso una expansión de disco significaba algo de caída del servicio y el cliente estaba en pleno pico de temporada, por lo que una caída del sistema no podía contemplarse.

Afortunadamente, una de las tablas más grandes consistía básicamente en registros con un gran campo de texto. Teníamos un sistema de archivos de 240GB con 30GB libres, que estaba utilizando 50GB de texto, prácticamente.

Un gzip de un mysqldump de la tabla ocupaba simplemente 2.25GB o el 4,5% del tamaño original, por lo que parecía que no habría problemas haciendo un simple

 

alter table big_text_table ROW_FORMAT=COMPRESSED

Aquella fue la primera idea, ir de aquí:

comprimir una tabla MySQL

a aquí:

comprimiendo una tabla MySQL

¿Qué podría salir mal?

Pues resulta que la respuesta es “un montón de cosas”.

¿Cómo encontramos los escollos posibles? ¿Conocía alguie realmente inteligente y expermientado de antemano la respuesta a nuestra pregunta?

Sería genial decir que sí, pero la verdad es bastante más prosaica. Tenemos un montón de gente inteligente y experimentada, pero sabemos de manera más importante que en un sistema de producción es mejor probar los cambios antes de aplicarlos. En este caso, sin nuestra magia del cloud, nuestro entorno de prueba era el de producción. No era la situación ideal, pero era mejor que ir a por la primera idea sin testear.

Entonces, creamos la “not_so_big_test_table” con el 5% del contenido de la grande y la misma estructura. Teníamos una tabla MySQL de 2.5GB para probar y todavía 27.5GB libres de espacio en disco.

Ejecutamos la prueba y esperábamos terminar la “alter table” con cerca de 30GB de espacio libre otra vez:

Pero, para nuestra sorpresa, se nos concedió menos de 25GB. Incluso peor, mientras monitorizábamos el espacio libre en disco, vimos que había menos de 22.5GB libres (y disparamos una alerta CRÍTICA en el sistema de producción para acabar de coronarnos)

En otras palabras; si lo hubiésemos hecho con la primera idea que tuvimos sobre los 50GB al completo, nos habrían hecho falta alrededor de 150GB de espacio libre en disco cuando teníamos escasamente 30GB.

Comprimiendo una tabla MySQL – las dificultades

Veamos las distintas razones por las cuales nuestros requerimientos de espacio se triplicaron durante el proceso en vez de reducirse.

1) Alter Table normalmente hace una copia de la misma

Esto nos lo esperábamos, y no sonaba tan mal. Teníamos 27.5GB de espacio libre. El gzip del mysqldump de toda la tabla utilizaba el 4.5% del espacio. Ese ratio nos daba solo 115MB de tamaño nada más. ¿Verdad? Bueno… No exactamente…

2) ROW_FORMAT=COMPRESSED no es de ninguna forma tan eficiente como hacer un gzip de la tabla entera

Aunque es cierto que ambas hacen uso del mismo algoritmo de compresión (zlib), InnoDB no aplica el algoritmo en la tabla MySQL al completo.

Aplica descompresión fila por fila y campo por campo, individualmente. Por eso tus ganancias de espacio son tan pequeñas. Todo se reduce a cuántas filas tienes y cuán largos son tus campos.

Además, la forma que InnoDB almacena las columnas largas o campos depende de lo grande que sea cada fila y del parámetro KEY_BLOCK_SIZE. Si éste es demasiado grande puede ser insuficiente.

En nuestro caso, el campo “big_text” era ligeramente superior a 4KB. Con el KEY_BLOCK_SIZE por defecto de 16KB, nuestra tabla todavía utilizaba el 93% del espacio original. ¡Ups!

La recomendación es hacer pruebas con datos reales y ver lo que obtienes. Hazlo, no te arrepentirás.

3) Binary-Logs

Si de verdad te preocupan tus datos, cuentas con mecanismos de seguridad para evitar perderlos, ¿verdad?

Un mecanismo sencillo y bueno de MySQL es la replicación Master/Slave, donde un sistema separado ejecuta un esclavo MySQL que recibe todas las actualizaciones hechas en el Master. Si tienes esto funcionando, entonces cuentas con Binary-Logs. Estos son los archivos donde todos los cambios en el máster se escriben antes de que se envíen al esclavo.

Incluso aunque no tengas Binary-Logs para replicación Master/Slave, puede que todavía los tengas que autorizar para un punto de recuperación en el tiempo.

Lo que tenemos que tener en cuenta es que debemos tener cuidado con ellos y cómo los mantenemos. Esto es lo que más importa, dado que no están comprimidos.

Ahí lo tienes. Tres formas distintas mediante las cuales el proceso consumía espacio en disco que no fuimos capaces de prever ni esperábamos que tuviesen tal impacto.

Arreglos para estos escollos

1) Espacio del “alter table”

La creación de una tabla nueva mediante “alter table” rara vez puede evitarse, y en este caso no fue posible. Estábamos en MySQL 5.5 por lo que la opción “ALGORITHM=INPLACE” no estaba disponible.

2) Espacio de compresión

Para lograr una compresión mejor, probamos diferentes valores de KEY_BLOCK_SIZE con nuestra muestra del 5%. Estos fueron los resultados.

Así que establecimos 2KB para KEY_BLOCK_SIZE, que ofreció el espacio en disco óptimo, nuestro objetivo.

Nota: elegir 8KB también podría habernos ayudado a ahorrar la misma cantidad de disco y puede que hubiera resultado en mejor rendimiento.

3) Espacio de Binary-Logs

Para evitar problemas con Binary-Logs, los seguimos eliminando manualmente. Para ello, utilizamos:

show slave status \G

sobre el esclavo. Muestra qué archivo del máster está siendo procesado en el valor Relay_Master_Log_File.

Una vez que el esclavo ha saltado a un nuevo archivo Binary-Log, introducimos un

purge binary logs to ‘mysql-bin.xxxxxx’;

sobre el master. Nos aseguramos que manteníamos los Binary-Logs en el esclavo, solo en caso de que algún desastre nos golpease en el peor momento.

Con todo esto, procedimos a comprimir la tabla MySQL. Fuimos capaces de hacer todo el proceso con 8GB de espacio libre disponible en el punto más crítico, y como puedes observar:


con alrededor de 60GB de espacio libre en disco, ¡ya estaba todo hecho!

TAGS: Binary logs, Bynary logs, compressing a MySQL table, comprimiendo una tabla MySQL, mySQL table

speech-bubble-13-icon Created with Sketch.
Comentarios

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

*
*