Posts tagged with “percona”


Thu 1 Dec

Nuevos cambios, nuevas oportunidades

Hace bastante tiempo que no escribo aquí y creo que es necesario dar una explicación a las personas que me siguen, aunque no sean muchas ;)

Mi vida laboral ha vuelto a dar un cambio importante, el segundo en lo que voy de año. Desde Octubre he entrado a trabajar en Percona como Support Engineer lo cual, bajo mi punto de vista, es un salto profesional grandísimo y le estoy muy agradecido a Ewen Fortune por esta oportunidad. Ahora mismo tengo de todo en mi vida, pero no tiempo libre ;) Los que me conocen bien saben que en cuanto tengo un reto por delante no puedo dejar de trabajar y esforzarme dando lo mejor de mi, por lo que estos meses están siendo muy intensos. Mucha lectura, estudio y práctica para poder alcanzar el nivel de profesionalidad y conocimiento que tanto hacen destacar a Percona.

Seguiré publicando posts sobre MySQL en mi blog, pero en un principio con menos frecuencia que antes, al menos hasta que me asiente completamente en mi nuevo trabajo. De vez en cuando podéis encontrar algún post mio en MySQL Performance Blog y si saco tiempo iré publicando aquí las versiones en Español.

Para empezar, aquí tenéis mi primera contribución:

Avoiding auto-increment holes on InnoDB with INSERT IGNORE

Muchas gracias a todos y un saludo especial a mis antiguos compañeros de Arsys.


Mon 17 Oct

Percona Live London

La próxima semana comienza la Percona Live y esta vez podremos disfrutarla sin salir de Europa. Durará dos días, 24 y 25 de Octubre.

Durante el primer dia se impartirán tutoriales de diversos tema, como por ejemplo NDB o Sphinx.

http://www.percona.com/live/london-2011/schedule-tutorial/

Y el segundo día se reserva para las conferencias:

http://www.percona.com/live/london-2011/schedule-conference/

Las conferencias serán impartidas no solo por compañeros de Percona, si no también por trabajadores de empresas como Paypal, Facebook, Nokia, Couchbase o Monty Program.

Como se puede comprobar, posibilidades de aprender hay miles. Aún estás a tiempo de apuntarte :) Yo estaré por allí toda la semana, así que si algún lector de este blog va a asistir a la Percona Live... ¡allí nos veremos!


Fri 7 Oct

Recopilar información del SO en el momento preciso

Muchas veces los problemas que tenemos con nuestra base de datos no son continuos, si no puntuales, y ocurren generalmente cuando no estamos mirando la pantalla. Las herramientas que hoy voy a mostraron os permitirán cazar el momento exacto de un problema y recabar todos los datos posibles en ese preciso instante de tiempo.

Las dos herramientas pertenecen al Percona Toolkit. Estas toolkit incluyen lo que antes era Aspersa y Maatkit. Por lo tanto, comenzamos descargando:

Percona Toolkit Download

Dentro de el las utilidades que hoy usaremos serán:

pt-collector: es la herramienta que se lanzará cuando una condición específica se de. Entre otras cosas se encargará de recargar información sobre IO, procesos, memoria, processlist, estado de las variables en MySQL y salidas de comandos como iostat, mpstat, df, lsof. También permite capturar tráfico tcpdump, el proceso con strace y lanzar un profile con Oprofile.

pt-stalk: será el encargado de llamar a pt-collector cuando una determinado estado, definido por nosotros, se de. Por ejemplo, podemos configurarlo de forma que lance pt-collector cuando el número de queries simultáneas alcancen los 100, cuando existan más de 25 tabla temporales en creación, cuando un proceso/usuario en particular se conecte, etc.

Los dos son script en bash (no es Perl! Yuhuu!). No hay fichero de configuración, se deben editar las variables dentro de los propios scripts.

Empezamos con pt-stack:

# ########################################################################
# Configuration settings.
# ########################################################################
# This is the max number of  we want to tolerate.
THRESHOLD=${THRESHOLD:-50}
# This is the thing to check for.
VARIABLE=${VARIABLE:-Threads_connected}
# How many times must the condition be met before the script will fire?
CYCLES=${CYCLES:-1}
# Collect GDB stacktraces?
GDB=${GDB:-no}
# Collect oprofile data?
OPROFILE=${OPROFILE:-yes}
# Collect strace data?
STRACE=${STRACE:-no}
# Collect tcpdump data?
TCPDUMP=${TCPDUMP:-yes}
# Send mail to this list of addresses when the script triggers.
# EMAIL=
# Any options to pass to mysql/mysqladmin, such as -u, -p, etc
# MYSQLOPTIONS=""
# This is the interval between checks.
INTERVAL=${INTERVAL:-30}
[...]
# This is the location of the 'collect' script.
if [ -z "${COLLECT}" ]; then
   COLLECT="/usr/bin/pt-collect";
fi
# This is where to store the collected data.
if [ -z "${DEST}" ]; then
   DEST="/tmp/collected/"
fi

Como vemos, la configuración es sencilla. Estamos diciendo que cuando la variable "Threads_connected" tenga un valor superior a 50 lanzará el pt-collect junto con información de oprofile y tcpdump. Hay más opciones, pero esto es un post de introducción, no un manual :)

Entonces, el primer paso es lanzar el pt-stack:

root@debian-slave:~# pt-stalk 
2011_10_07_14_34_36 check results: Threads_connected = 2, matched = no, cycles_true = 0
[...]
2011_10_07_14_41_28 check results: Threads_connected = 71, matched = yes, cycles_true = 1
2011_10_07_14_41_28 sleeping 300 seconds to avoid DOS attack

Si ahora miramos la carpeta donde se guardan los resultados de collect, ahí tendemos toda la información necesaria recogida.

root@debian-slave:~# ls -l /tmp/collected/
total 1248
-rw-r--r-- 1 root root   9120 oct  7 14:42 2011_10_07_14_41_29-df
-rw-r--r-- 1 root root  19800 oct  7 14:42 2011_10_07_14_41_29-diskstats
-rw-r--r-- 1 root root     13 oct  7 14:42 2011_10_07_14_41_29-hostname
-rw-r--r-- 1 root root   2645 oct  7 14:41 2011_10_07_14_41_29-innodbstatus1
-rw-r--r-- 1 root root   2645 oct  7 14:42 2011_10_07_14_41_29-innodbstatus2
-rw-r--r-- 1 root root  40800 oct  7 14:42 2011_10_07_14_41_29-interrupts
-rw-r--r-- 1 root root     66 oct  7 14:41 2011_10_07_14_41_29-iostat
-rw-r--r-- 1 root root     66 oct  7 14:41 2011_10_07_14_41_29-iostat-overall
-rw-r--r-- 1 root root     91 oct  7 14:41 2011_10_07_14_41_29-log_error
-rw-r--r-- 1 root root  16327 oct  7 14:41 2011_10_07_14_41_29-lsof
-rw-r--r-- 1 root root  35580 oct  7 14:42 2011_10_07_14_41_29-meminfo
-rw-r--r-- 1 root root     66 oct  7 14:41 2011_10_07_14_41_29-mpstat
-rw-r--r-- 1 root root     66 oct  7 14:41 2011_10_07_14_41_29-mpstat-overall
-rw-r--r-- 1 root root  66573 oct  7 14:41 2011_10_07_14_41_29-mutex-status1
-rw-r--r-- 1 root root  66573 oct  7 14:42 2011_10_07_14_41_29-mutex-status2
-rw-r--r-- 1 root root 442530 oct  7 14:42 2011_10_07_14_41_29-mysqladmin
-rw-r--r-- 1 root root  32040 oct  7 14:42 2011_10_07_14_41_29-netstat
-rw-r--r-- 1 root root  41520 oct  7 14:42 2011_10_07_14_41_29-netstat_s
-rw-r--r-- 1 root root     53 oct  7 14:41 2011_10_07_14_41_29-opentables1
-rw-r--r-- 1 root root     52 oct  7 14:42 2011_10_07_14_41_29-opentables2
-rw-r--r-- 1 root root    423 oct  7 14:42 2011_10_07_14_41_29-output
-rw-r--r-- 1 root root  26742 oct  7 14:41 2011_10_07_14_41_29-pmap
-rw-r--r-- 1 root root  15714 oct  7 14:41 2011_10_07_14_41_29-processlist1
-rw-r--r-- 1 root root    194 oct  7 14:42 2011_10_07_14_41_29-processlist2
-rw-r--r-- 1 root root  35224 oct  7 14:42 2011_10_07_14_41_29-procstat
-rw-r--r-- 1 root root  44906 oct  7 14:42 2011_10_07_14_41_29-procvmstat
-rw-r--r-- 1 root root   6451 oct  7 14:41 2011_10_07_14_41_29-ps
-rw-r--r-- 1 root root 229140 oct  7 14:42 2011_10_07_14_41_29-slabinfo
-rw-r--r-- 1 root root     25 oct  7 14:41 2011_10_07_14_41_29-stacktrace
-rw-r--r-- 1 root root  22924 oct  7 14:41 2011_10_07_14_41_29-sysctl
-rw-r--r-- 1 root root   7006 oct  7 14:41 2011_10_07_14_41_29-top
-rw-r--r-- 1 root root     90 oct  7 14:41 2011_10_07_14_41_29-trigger
-rw-r--r-- 1 root root   6596 oct  7 14:41 2011_10_07_14_41_29-variables
-rw-r--r-- 1 root root   2670 oct  7 14:42 2011_10_07_14_41_29-vmstat
-rw-r--r-- 1 root root    313 oct  7 14:42 2011_10_07_14_41_29-vmstat-overall

Por ejemplo:

root@debian-slave:~# cat /tmp/collected/2011_10_07_14_41_29-processlist1 |more
*************************** 1. row ***************************
     Id: 304
   User: root
   Host: localhost
     db: sbtest
Command: Query
   Time: 0
  State: NULL
   Info: UNLOCK TABLES
*************************** 2. row ***************************
     Id: 305
   User: root
   Host: localhost
     db: sbtest
Command: Execute
   Time: 0
  State: Writing to net
   Info: SELECT c from sbtest where id between 504280 and 504379 order by c
*************************** 3. row ***************************
     Id: 306
   User: root
   Host: localhost
     db: sbtest
Command: Query
   Time: 0
  State: NULL
   Info: UNLOCK TABLES
[...]

Ya no es necesario que te quedes a las 3 de la mañana esperando a que todo deje de funcionar para empezar a recopilar datos :)


Thu 28 Jul

Backups y restauraciones de tablas en InnoDB

Mucha gente cree erroneamente que gracias a la opción innodb-file-per-table te permite, como MyISAM, portar una tabla en binario de un servidor a otro de forma transparente o recuperar el backup de una tabla. El problema viene cuando realmente necesitan hacer uso de ese backup y no funciona como ellos esperaban.

Al contrario que con MyISAM, donde los ficheros de tablas MYD e MYI son independientes del resto y portables, todas las tablas de InnoDB dependen de un tablespace común donde se almacenan las definiciones de las tablas y además depende de los IDs de transacciones entre otras cosas. Por lo que, si restauras un .idb, no recuperarás los datos.

Todo esto se aplica a la versión original de MySQL, la desarrollada por Oracle. Pero Xtrabackup y Percona Server nos permite esquivar esta limitación y trabajar con los ficheros binarios como si se tratasen de tablas MyISAM, moviéndolas y restaurándolas de un servidor a otro.

Lo primero que sorprende es que el servidor de origen no tiene porque ser Percona Server, puede ser el MySQL del repositorio de tu distribución. Los únicos requisitos son:

  • El servidor origen y destino deben tener --innodb-file-per-table
  • Hacer el backup con xtrabackup
  • Recuperar el tablespace en un Percona Server

El proceso de realización del backup no cambia, pero si la posterior reparación (--prepare). El comando de reparación será el mismo, solo que añadimos la opción --export.

xtrabackup --prepare --export --innodb-file-per-table --target-dir=/mnt/backups/mysql-data

Esta opción generará un fichero extra .exp

/mnt/backups/mysql-data/db/table.exp
/mnt/backups/mysql-data/db/table.ibd

Ahora, ¿cómo importamos esa tabla en otro MySQL?

Tal y como hemos comentado antes, el destino debe ser un Percona Server. Únicamente tendremos que ejecutar los siguientes comandos:

ALTER TABLE db.table DISCARD TABLESPACE;

Copiamos las tablas exportadas al subdirectorio db/ del servidor destino.

ALTER TABLE db.table IMPORT TABLESPACE;

Y listo.

Tenéis información más actualizada en el propio manual de xtrabackup.

http://www.percona.com/docs/wiki/percona-xtrabackup:xtrabackup:export_and_import


Tue 26 Jul

Pre-cachear los datos de InnoDB en el buffer pool

Cuando tienes un entorno activo-pasivo o montas un esclavo para las lecturas, el mayor problema que te puedes encontrar al poner los nuevos servidores en producción es que las cachés se encuentren frias (cold cache). Como dichos servidores no han recibido consultas, todas sus cachés, como query cache o innodb_buffer_pool se encuentran vacias y todas las consultas tendrán que ir a disco duro durante los primeros minutos u horas. En esos primeros instantes, el rendimiento de tu backend será pésimo.

Hasta ahora, para evitar en la medida de lo posible ese problema, se lanzaban SELECT contra las tablas que obligasen a leerse todas las filas. Eran consultas muy pesadas que tardaban mucho tiempo en ejecutarse y ralentizaban aún más el rendimiento, pero... no había otra solución. Un ejemplo de está solución se puede leer en el blog de Santi Saez Woop!. Pero esto ya ha cambiado.

En la versión de desarrollo de MySQL 5.6 ya es posible guardar el estado actual del buffer pool en un fichero y recuperarlo en memoria después del reinicio del servicio. No más esperas ni trucos de scripting, ahora hablamos de un procedimiento nativo dentro del propio servicio.

Lanzar un dump del buffer pool:

mysql> SET innodb_buffer_pool_dump_now=ON;

Configurar un dump cuando se realice el apagado del servicio:

mysql> SET innodb_buffer_pool_dump_at_shutdown=ON;

Recuperar un dump en memoria:

mysql> SET innodb_buffer_pool_load_now=ON;

Y en my.cnf podemos poner esta línea, para que se cargue automáticamente durante el arranque del servicio:

innodb_buffer_pool_load_at_startup=ON

Podemos ver el progreso del dump:

mysql> SHOW STATUS LIKE 'innodb_buffer_pool_dump_status';

Y el progreso de la restauración:

mysql> SHOW STATUS LIKE 'innodb_buffer_pool_load_status';

Así como cancelarlo :)

mysql> SET innodb_buffer_pool_load_abort=ON;

Es importante recalcar que si estás utilizando Percona Server, su engine xtradb dispone también de esta funcionalidad, por lo que no tienes que esperar. Simplemente, los comandos cambian:

Lanzar un dump del buffer pool:

mysql> select * from information_schema.XTRADB_ADMIN_COMMAND /*!XTRA_LRU_DUMP*/;

Recuperar un dump en memoria:

mysql> select * from information_schema.XTRADB_ADMIN_COMMAND /*!XTRA_LRU_RESTORE*/;

Programar un dump cada X segundos:

innodb_auto_lru_dump = X

Cargar automáticamente el dump en el arranque:

innodb_buffer_pool_restore_at_startup=1


Fri 8 Jul

Mantener la consistencia de los datos en la replicación

Las replicaciones necesitan de un chequeo constante en la integridad de los datos. Fallos de disco, corrupción de de logs, mezcla de tablas transaccionales y no transaccionales y otros problemas pueden tumbar la consistencia de nuestros datos. Por lo tanto, podemos tener una replicación funcionando, pero los datos, si no hay una comprobación activa, pueden ser diferentes en las dos máquinas. MySQL no tiene comprobaciones activas de consistencia, por lo que es trabajo nuestro. Para ello, instalamos las herramientas maatkit de Percona:

apt-get install maatkit

Las herramientras que usaremos serán mk-table-checksum y mk-table-sync. El funcionamiento de la herramienta se basa en la replicación en base a sentencias de mysql. mk-table-checksum realiza una comprobación mediante un algoritmo de hashing en las tablas, escribiendo los resultados en la base de datos. Estas sentencias se replicarán en el esclavo y se volverán a ejecutar, realizando por lo tanto el mismo hashing en las tablas del esclavo. De esta forma, unicamente debemos comparar los resultados en el maestro y en el esclavo para comprobar si los datos son exactamente iguales o no.

Tenemos dos MySQL en Master-Slave.

NODO 1:

Puerto TCP 19369

master [localhost] {msandbox} (vida) > select * from producto;
+---+--------+
| i | nombre |
+---+--------+
| 1 | VPS    |
| 2 | Cloud  |
+---+--------+
2 rows in set (0.00 sec)

NODO 2:

Puerto TCP 19370

slave1 [localhost] {msandbox} (vida) > select * from producto;
+---+---------+
| i | nombre  |
+---+---------+
| 1 | VPS     |
| 2 | Cloud   |
| 3 | Storage |
+---+---------+
3 rows in set (0.00 sec)

¿Como comprobamos si los datos son o no consistentes?

Primero, le decimos a mk-table-checksum que haga un checksum de todas las tablas de la base de datos vida y guarde el resultado en test.checksum.

~$ mk-table-checksum u=root,p=msandbox
--socket=/tmp/mysql_sandbox19369.sock --databases=vida
--replicate test.checksum --create-replicate-table
Cannot connect to P=19369,S=/tmp/mysql_sandbox19369.sock,h=SBslave1,p=...,u=root
Cannot connect to P=19369,S=/tmp/mysql_sandbox19369.sock,h=SBslave1,p=...,u=root
DATABASE TABLE    CHUNK HOST        ENGINE      COUNT         CHECKSUM TIME WAIT STAT  LAG
vida     producto     0 soporteit69 InnoDB          2         935b4964    0 NULL NULL NULL

Comprobamos los resultados en Master:

master [localhost] {msandbox} (test) > select * from checksum;
+------+----------+-------+------------+----------+----------+------------+------------+
| db   | tbl      | chunk | boundaries | this_crc | this_cnt | master_crc | master_cnt |
+------+----------+-------+------------+----------+----------+------------+------------+
| vida | producto |     0 | 1=1        | 935b4964 |        2 | 935b4964   |          2 |
+------+----------+-------+------------+----------+----------+------------+------------+
1 row in set (0.00 sec)

Comprobamos los resultados en Slave:

slave1 [localhost] {msandbox} (test) > select * from checksum;
+------+----------+-------+------------+----------+----------+------------+------------+
| db   | tbl      | chunk | boundaries | this_crc | this_cnt | master_crc | master_cnt |
+------+----------+-------+------------+----------+----------+------------+------------+
| vida | producto |     0 | 1=1        | 6120c018 |        3 | 935b4964   |          2 |
+------+----------+-------+------------+----------+----------+------------+------------+
1 row in set (0.00 sec)

Como podemos ver, el crc del slave (this_crc) es diferente al del master (master_crc) por lo tanto, tenemos incosistencias en los datos de la tabla producto de la base de datos vida.

¿Cómo lo arreglamos?

Podriamos hacerlo volviendo a montar la replicación de cero usando un dump de mysqldump. Esto vale si tu BBDD ocupa solo unos cientos de megas, pero si hablamos de gigas, puedes tardar horas y provocar caidas del sistema. Así que haremos uso de mk-table-sync.

~$ mk-table-sync --execute --replicate test.checksum 
--sync-to-master u=root,p=msandbox --socket=/tmp/mysql_sandbox19370.sock

Comprobamos si ahora los datos son consistentes:

master [localhost] {msandbox} (vida) > select * from producto;
+---+--------+
| i | nombre |
+---+--------+
| 1 | VPS    |
| 2 | Cloud  |
+---+--------+
2 rows in set (0.00 sec)
slave1 [localhost] {msandbox} (vida) > select * from producto;
+---+--------+
| i | nombre |
+---+--------+
| 1 | VPS    |
| 2 | Cloud  |
+---+--------+
2 rows in set (0.00 sec)
slave1 [localhost] {msandbox} (test) > select * from checksum;
+------+----------+-------+------------+----------+----------+------------+------------+
| db   | tbl      | chunk | boundaries | this_crc | this_cnt | master_crc | master_cnt |
+------+----------+-------+------------+----------+----------+------------+------------+
| vida | producto |     0 | 1=1        | 935b4964 |        2 | 935b4964   |          2 |
+------+----------+-------+------------+----------+----------+------------+------------+
1 row in set (0.00 sec)

Listo! Replicación consistente y en marcha :)


Wed 14 Oct